ATL Transformations

Performance Optimisation

Optimise ATL transformations for better performance with large models and complex metamodels.

In this tutorial, you’ll learn performance best practices, identify common bottlenecks, and apply optimisation techniques.

You’ll discover how to write efficient helpers, optimise collection operations, and structure transformations for maximum performance.

50 mins Estimated Time

Section 1

Identifying Performance Issues

Performance problems typically arise from inefficient collection operations, redundant computations, and poorly structured helpers.

Following best practices prevents most performance issues before they occur.

Step 1

Identify inefficient patterns.

Common anti-patterns include nested iterations, redundant filtering, and repeated collection traversals.

OptimisationExample.atl
1-- @path Source=/Source/Source.ecore
2-- @path Target=/Target/Target.ecore
3
4module OptimisationExample;
5create OUT: Target from IN: Source;
6
7-- ANTI-PATTERN: Nested iterations (O(n²))
8helper context Source!Container def: inefficientSearch(): Sequence(Source!Element) =
9 self.elements->select(e1 |
10 self.elements->exists(e2 | e2.name = e1.name)
11 );

Step 2

Cache expensive computations.

Store expensive computation results in helpers to avoid recalculation.

OptimisationExample.atl
1-- @path Source=/Source/Source.ecore
2-- @path Target=/Target/Target.ecore
3
4module OptimisationExample;
5create OUT: Target from IN: Source;
6
7-- OPTIMISED: Cache expensive computation
8helper context Source!Container def: elementNames: Set(String) =
9 self.elements->collect(e | e.name)->asSet();
10
11helper context Source!Container def: hasElement(name : String): Boolean =
12 self.elementNames->includes(name);

Step 3

Optimise collection operations.

Combine operations and avoid unnecessary intermediate collections.

OptimisationExample.atl
1-- @path Source=/Source/Source.ecore
2-- @path Target=/Target/Target.ecore
3
4module OptimisationExample;
5create OUT: Target from IN: Source;
6
7-- OPTIMISED: Chain operations instead of intermediate variables
8helper context Source!Container def: activeHighPriorityNames: Sequence(String) =
9 self.elements
10 ->select(e | e.active)
11 ->select(e | e.priority > 5)
12 ->collect(e | e.name);

Step 4

Use appropriate rule types.

Choose matched, lazy, or called rules based on transformation requirements.

OptimisationExample.atl
1-- @path Source=/Source/Source.ecore
2-- @path Target=/Target/Target.ecore
3
4module OptimisationExample;
5create OUT: Target from IN: Source;
6
7-- Use guards to filter early, avoiding unnecessary processing
8rule HighPriorityOnly {
9 from
10 s: Source!Element (s.priority > 5 and s.active)
11 to
12 t: Target!Item (
13 name <- s.name
14 )
15}

Section 2

Advanced Optimisation Techniques

Advanced optimisation focuses on reducing computational overhead, eliminating redundant operations, and measuring actual performance improvements.

These techniques target specific bottlenecks in transformation execution.

Step 1

Minimise helper call overhead.

Reduce the number of helper calls in performance-critical sections.

OptimisationExample.atl
1-- @path Source=/Source/Source.ecore
2-- @path Target=/Target/Target.ecore
3
4module OptimisationExample;
5create OUT: Target from IN: Source;
6
7-- Call expensive helper once, reuse result
8helper context Source!Container def: statistics: String =
9 let count : Integer = self.elements->size() in
10 let active : Integer = self.elements->select(e | e.active)->size() in
11 'Total: ' + count.toString() + ', Active: ' + active.toString();

Step 2

Avoid unnecessary type checking.

Use guard conditions instead of repeated type checks in bindings.

OptimisationExample.atl
1-- @path Source=/Source/Source.ecore
2-- @path Target=/Target/Target.ecore
3
4module OptimisationExample;
5create OUT: Target from IN: Source;
6
7-- Use separate rules with guards instead of type checking in bindings
8rule SpecialElementTransform {
9 from
10 s: Source!SpecialElement -- Type-specific rule
11 to
12 t: Target!SpecialItem (
13 name <- s.name,
14 specialProperty <- s.specialValue -- Direct access, no type check needed
15 )
16}

Step 3

Profile and measure improvements.

Measure transformation performance to identify actual bottlenecks.

OptimisationExample.atl
1-- @path Source=/Source/Source.ecore
2-- @path Target=/Target/Target.ecore
3
4module OptimisationExample;
5create OUT: Target from IN: Source;
6
7-- Add timing helpers to measure performance
8helper def: timestamp(): String =
9 'Processing at time: ' + thisModule.getCurrentTime();
10
11-- Measure transformation sections to identify bottlenecks

Step 4

Build an optimised transformation.

The complete transformation demonstrates all optimisation techniques working together.

OptimisationExample.atl
1-- @path Source=/Source/Source.ecore
2-- @path Target=/Target/Target.ecore
3
4module OptimisationExample;
5create OUT: Target from IN: Source;
6
7-- Optimised helper: cache collection result
8helper context Source!Container def: activeElements: Sequence(Source!Element) =
9 self.elements->select(e | e.active);
10
11-- Optimised helper: combine operations
12helper context Source!Container def: activeElementNames: Sequence(String) =
13 self.activeElements->collect(e | e.name);
14
15-- Efficient rule using guard to filter early
16rule OptimisedTransform {
17 from
18 s: Source!Container (s.elements->size() > 0)
19 to
20 t: Target!Item (
21 name <- s.name,
22 count <- s.activeElements->size(),
23 names <- s.activeElementNames
24 )
25}

Check Your Understanding

Question 1 of 3

What is the most common performance bottleneck in ATL?

Question 2 of 3

When should you cache helper results?

Question 3 of 3

How can you reduce collection operation overhead?

Hello World Template

Learn the basics of MTL (Model-to-Text Language) by creating your first code generation template using the OMG MOFM2T standard.