ABAP Performance Tuning in S/4HANA Series : Part 3 : SAT + ST12

Estimated read time 12 min read

SAT + ST12: Profiling ABAP CPU and Memory the Right Way

Series: ABAP Performance Tuning in S/4HANA | Part 3 of 7 | Author: Manish Khanna

Your ST05 trace is clean. No expensive SQL, no N+1 patterns, indexes in place. But the program still takes 45 seconds. The problem is inside ABAP itself — a loop doing MOVE-CORRESPONDING on a 200-field structure 80,000 times, or a factory method creating a new object instance on every iteration. SAT finds the first problem in 10 minutes. ST12 finds the second.

This post covers both tools and when to use which.

SAT — Finding CPU Hotspots

Transaction: SAT (replaces legacy SE30)

SAT measures CPU time, call counts, and execution time per ABAP statement, function module, method, and form routine. Use it when the database is not the bottleneck.

Running an SAT Trace

Step 1 — Configure

Enter SAT. Click Change Measurement Settings (F6)

 

Setting RecommendationVariantAll Statements on the first runAggregate recordsEnable — raw traces can be millions of rowsMaximum trace records500,000 to start; increase if trace cuts offRestrict to userYour user ID

💡Tip: For batch jobs, use the Background measurement option. For dialog programs, use Local — traces only your session.

Step 2 — Start, Execute, Stop

Click Start Measurement (F8). Status bar shows “Measurement active”. Execute your program or transaction. Return to SAT and click Stop Measurement.

Step 3 — Evaluate

Click Evaluate Last Measurement. The hit list shows rows sorted by gross time descending.

Reading SAT Results

The metric that matters: Net time percentage

Gross time includes all sub-calls. Net time is exclusive to that statement. A method with 80% gross time but 2% net time is innocent — the cost is inside a child call. Sort by net time to find the real bottleneck.

💡Tip: If a single statement consumes more than 20% of net time, investigate it first. Everything else is secondary.

Call count anomalies are often more important than per-call time:

A method at 0.01ms per call sounds harmless. At 80,000 calls, that’s 800ms — significant. Look for methods with call counts in the tens of thousands, especially inside loops.

What Each Statement Type Tells You

 

Statement Type High Value MeansSELECTMove to ST05 — the DB layer is the real problemLOOP ATCheck table type, key access, WHERE conditionCALL METHODObject instantiation inside loops, excessive polymorphismMOVE-CORRESPONDINGLarge structures with field catalog mismatchCONCATENATE / string opsString building in a loop — use string tables

Common SAT Findings and Fixes

MOVE-CORRESPONDING in a loop on large structures:

” ❌ Bad: MOVE-CORRESPONDING on 80-field structure, 50,000 times
LOOP AT lt_source INTO ls_source.
  MOVE-CORRESPONDING ls_source TO ls_target.
  APPEND ls_target TO lt_target.
ENDLOOP.

” ✅ Good: map only the fields you actually need
LOOP AT lt_source ASSIGNING FIELD-SYMBOL(<src>).
  DATA(ls_t) = VALUE ty_target(
    vbeln = <src>-vbeln
    posnr = <src>-posnr
    matnr = <src>-matnr ).
  APPEND ls_t TO lt_target.
ENDLOOP.

String concatenation in a loop:

” ❌ Bad: CONCATENATE creates a new string object each iteration
DATA lv_result TYPE string.
LOOP AT lt_lines INTO lv_line.
  CONCATENATE lv_result lv_line INTO lv_result SEPARATED BY ‘,’.
ENDLOOP.

” ✅ Good: build the table first, concatenate once
LOOP AT lt_lines INTO lv_line.
  APPEND lv_line TO lt_parts.
ENDLOOP.
CONCATENATE LINES OF lt_parts INTO lv_result SEPARATED BY ‘,’.

Always use ASSIGNING FIELD-SYMBOL in performance-critical loops:

” ❌ Bad: copies the entire structure per iteration
LOOP AT lt_data INTO ls_data.
  ls_data-status = ‘X’.
  MODIFY lt_data FROM ls_data.
ENDLOOP.

” ✅ Good: direct reference, no copy
LOOP AT lt_data ASSIGNING FIELD-SYMBOL(<fs>).
  <fs>-status = ‘X’.
ENDLOOP.

ST12 — Adding Memory Analysis

Transaction: ST12

ST12 gives you everything SAT gives you, plus memory consumption tracking per object and class. Use ST12 instead of SAT whenever:

The program uses ABAP Objects (classes) extensivelyYou’re seeing STORAGE_PARAMETERS_WRONG_SET dumpsMemory usage grows over time in long-running sessions

 

 

SAT vs. ST12 — When to Use Which

 

 SAT ST12Runtime analysisMemory allocation trackingObject instance countingTrace file sizeSmallerLargerBest forPure CPU profilingOO programs, memory problems

Running an ST12 Trace

Enter ST12. Click New Measurement (F6)

 

Option SettingABAP traceAlways enableMemory traceEnableAggregate identical positionsEnableMaximum size (KB)50,000 for most programs

Start measurement, run your program, stop. Double-click the saved measurement.

The Memory Consumption View

Click Memory Analysis. The columns to watch:

 

Column What It Tells YouAllocated (KB)Heap memory allocated by this objectReleased (KB)Memory freed (GC, FREE, CLEAR)Net allocationAllocated minus Released — net growthInstancesNumber of object instances created

Red flags:

Net allocation > 10 MB for a single transactionInstances count in the hundreds of thousands for a class that should have 1–2 instancesNet allocation grows each time you call the same transaction — accumulating without release

The Most Common Memory Anti-Patterns

Object instantiation inside a loop — the silent memory killer:

” ❌ Bad: 50,000 instances created and never GC’d efficiently
LOOP AT lt_data INTO ls_data.
  lo_processor = NEW zcl_data_processor( ).
  lo_processor->process( ls_data ).
ENDLOOP.

” ✅ Good: one instance, reused
lo_processor = NEW zcl_data_processor( ).
LOOP AT lt_data INTO ls_data.
  lo_processor->process( ls_data ).
ENDLOOP.

Static class attributes that accumulate across calls:

Static attributes persist for the entire ABAP session lifetime. If a method is called in a loop and pushes to a static cache without eviction, memory grows without bound:

” ❌ Bad: static cache grows unbounded
CLASS zcl_cache DEFINITION.
  CLASS-DATA: mt_cache TYPE HASHED TABLE OF ty_entry
                        WITH UNIQUE KEY key.
  CLASS-METHODS: add_entry IMPORTING is_entry TYPE ty_entry.
ENDCLASS.

” ✅ Good: add a maximum size + eviction policy
CLASS-METHODS: add_entry IMPORTING is_entry TYPE ty_entry.
” In implementation:
” IF lines( mt_cache ) > 10000. DELETE mt_cache … ENDIF.

Using CL_ABAP_MEMORY_UTILITIES for programmatic monitoring:

DATA(lv_memory_kb) = cl_abap_memory_utilities=>get_current_memory_use( ) / 1024.
WRITE: / ‘Current memory (KB):’, lv_memory_kb.

Add this before and after the code block you’re profiling to measure net growth.

The Combined Workflow

Run ST05 — clean? Good, move on.Run SAT — find the high-net-time statements.If you see CALL METHOD with very high call counts, or any OO code: switch to ST12.In ST12, check both the runtime view (same as SAT) AND the memory view.Fix one thing. Re-measure. Confirm improvement before the next fix.

Never skip step 5. A change that looks like it should help sometimes makes things worse. The measurement is the proof.

Key Takeaways

SAT is the right tool when the database is not the bottleneckSort by net time, not gross time — gross time includes child callsHigh call counts in loops are often more impactful than high per-call timeUse ST12 instead of SAT for any OO-heavy or memory-intensive programAlways use ASSIGNING FIELD-SYMBOL in performance-critical LOOP ATFix one thing at a time, re-measure after each change

Next in this series: ATC: Make the ABAP Test Cockpit Your Pre-Transport Quality Gate — catch anti-patterns statically before any trace, and integrate ATC into your CI/CD pipeline.

Series Home Page  

 

​ SAT + ST12: Profiling ABAP CPU and Memory the Right WaySeries: ABAP Performance Tuning in S/4HANA | Part 3 of 7 | Author: Manish KhannaYour ST05 trace is clean. No expensive SQL, no N+1 patterns, indexes in place. But the program still takes 45 seconds. The problem is inside ABAP itself — a loop doing MOVE-CORRESPONDING on a 200-field structure 80,000 times, or a factory method creating a new object instance on every iteration. SAT finds the first problem in 10 minutes. ST12 finds the second.This post covers both tools and when to use which.SAT — Finding CPU HotspotsTransaction: SAT (replaces legacy SE30)SAT measures CPU time, call counts, and execution time per ABAP statement, function module, method, and form routine. Use it when the database is not the bottleneck.Running an SAT TraceStep 1 — ConfigureEnter SAT. Click Change Measurement Settings (F6) Setting RecommendationVariantAll Statements on the first runAggregate records✅Enable — raw traces can be millions of rowsMaximum trace records500,000 to start; increase if trace cuts offRestrict to userYour user ID💡Tip: For batch jobs, use the Background measurement option. For dialog programs, use Local — traces only your session.Step 2 — Start, Execute, StopClick Start Measurement (F8). Status bar shows “Measurement active”. Execute your program or transaction. Return to SAT and click Stop Measurement.Step 3 — EvaluateClick Evaluate Last Measurement. The hit list shows rows sorted by gross time descending.Reading SAT ResultsThe metric that matters: Net time percentageGross time includes all sub-calls. Net time is exclusive to that statement. A method with 80% gross time but 2% net time is innocent — the cost is inside a child call. Sort by net time to find the real bottleneck.💡Tip: If a single statement consumes more than 20% of net time, investigate it first. Everything else is secondary.Call count anomalies are often more important than per-call time:A method at 0.01ms per call sounds harmless. At 80,000 calls, that’s 800ms — significant. Look for methods with call counts in the tens of thousands, especially inside loops.What Each Statement Type Tells You Statement Type High Value MeansSELECTMove to ST05 — the DB layer is the real problemLOOP ATCheck table type, key access, WHERE conditionCALL METHODObject instantiation inside loops, excessive polymorphismMOVE-CORRESPONDINGLarge structures with field catalog mismatchCONCATENATE / string opsString building in a loop — use string tablesCommon SAT Findings and FixesMOVE-CORRESPONDING in a loop on large structures:” ❌ Bad: MOVE-CORRESPONDING on 80-field structure, 50,000 times
LOOP AT lt_source INTO ls_source.
  MOVE-CORRESPONDING ls_source TO ls_target.
  APPEND ls_target TO lt_target.
ENDLOOP.

” ✅ Good: map only the fields you actually need
LOOP AT lt_source ASSIGNING FIELD-SYMBOL(<src>).
  DATA(ls_t) = VALUE ty_target(
    vbeln = <src>-vbeln
    posnr = <src>-posnr
    matnr = <src>-matnr ).
  APPEND ls_t TO lt_target.
ENDLOOP.String concatenation in a loop:” ❌ Bad: CONCATENATE creates a new string object each iteration
DATA lv_result TYPE string.
LOOP AT lt_lines INTO lv_line.
  CONCATENATE lv_result lv_line INTO lv_result SEPARATED BY ‘,’.
ENDLOOP.

” ✅ Good: build the table first, concatenate once
LOOP AT lt_lines INTO lv_line.
  APPEND lv_line TO lt_parts.
ENDLOOP.
CONCATENATE LINES OF lt_parts INTO lv_result SEPARATED BY ‘,’.Always use ASSIGNING FIELD-SYMBOL in performance-critical loops:” ❌ Bad: copies the entire structure per iteration
LOOP AT lt_data INTO ls_data.
  ls_data-status = ‘X’.
  MODIFY lt_data FROM ls_data.
ENDLOOP.

” ✅ Good: direct reference, no copy
LOOP AT lt_data ASSIGNING FIELD-SYMBOL(<fs>).
  <fs>-status = ‘X’.
ENDLOOP.ST12 — Adding Memory AnalysisTransaction: ST12ST12 gives you everything SAT gives you, plus memory consumption tracking per object and class. Use ST12 instead of SAT whenever:The program uses ABAP Objects (classes) extensivelyYou’re seeing STORAGE_PARAMETERS_WRONG_SET dumpsMemory usage grows over time in long-running sessions  SAT vs. ST12 — When to Use Which  SAT ✅ST12✅Runtime analysis✅✅Memory allocation tracking❌✅Object instance counting❌✅Trace file sizeSmallerLargerBest forPure CPU profilingOO programs, memory problemsRunning an ST12 TraceEnter ST12. Click New Measurement (F6) Option Setting✅ABAP trace✅Always enableMemory trace✅EnableAggregate identical positions✅EnableMaximum size (KB)50,000 for most programsStart measurement, run your program, stop. Double-click the saved measurement.The Memory Consumption ViewClick Memory Analysis. The columns to watch: Column What It Tells YouAllocated (KB)Heap memory allocated by this objectReleased (KB)Memory freed (GC, FREE, CLEAR)Net allocationAllocated minus Released — net growthInstancesNumber of object instances createdRed flags:Net allocation > 10 MB for a single transactionInstances count in the hundreds of thousands for a class that should have 1–2 instancesNet allocation grows each time you call the same transaction — accumulating without releaseThe Most Common Memory Anti-PatternsObject instantiation inside a loop — the silent memory killer:” ❌ Bad: 50,000 instances created and never GC’d efficiently
LOOP AT lt_data INTO ls_data.
  lo_processor = NEW zcl_data_processor( ).
  lo_processor->process( ls_data ).
ENDLOOP.

” ✅ Good: one instance, reused
lo_processor = NEW zcl_data_processor( ).
LOOP AT lt_data INTO ls_data.
  lo_processor->process( ls_data ).
ENDLOOP.Static class attributes that accumulate across calls:Static attributes persist for the entire ABAP session lifetime. If a method is called in a loop and pushes to a static cache without eviction, memory grows without bound:” ❌ Bad: static cache grows unbounded
CLASS zcl_cache DEFINITION.
  CLASS-DATA: mt_cache TYPE HASHED TABLE OF ty_entry
                        WITH UNIQUE KEY key.
  CLASS-METHODS: add_entry IMPORTING is_entry TYPE ty_entry.
ENDCLASS.

” ✅ Good: add a maximum size + eviction policy
CLASS-METHODS: add_entry IMPORTING is_entry TYPE ty_entry.
” In implementation:
” IF lines( mt_cache ) > 10000. DELETE mt_cache … ENDIF.Using CL_ABAP_MEMORY_UTILITIES for programmatic monitoring:DATA(lv_memory_kb) = cl_abap_memory_utilities=>get_current_memory_use( ) / 1024.
WRITE: / ‘Current memory (KB):’, lv_memory_kb.Add this before and after the code block you’re profiling to measure net growth.The Combined WorkflowRun ST05 — clean? Good, move on.Run SAT — find the high-net-time statements.If you see CALL METHOD with very high call counts, or any OO code: switch to ST12.In ST12, check both the runtime view (same as SAT) AND the memory view.Fix one thing. Re-measure. Confirm improvement before the next fix.Never skip step 5. A change that looks like it should help sometimes makes things worse. The measurement is the proof.Key TakeawaysSAT is the right tool when the database is not the bottleneckSort by net time, not gross time — gross time includes child callsHigh call counts in loops are often more impactful than high per-call timeUse ST12 instead of SAT for any OO-heavy or memory-intensive programAlways use ASSIGNING FIELD-SYMBOL in performance-critical LOOP ATFix one thing at a time, re-measure after each changeNext in this series: ATC: Make the ABAP Test Cockpit Your Pre-Transport Quality Gate — catch anti-patterns statically before any trace, and integrate ATC into your CI/CD pipeline.Series Home Page     Read More Technology Blog Posts by Members articles 

#SAP

#SAPTechnologyblog

You May Also Like

More From Author