Skip to content
Back to skills

101 Benchmarking Guide 5ad61ae7

ASecurity

Performance testing is essential for writing efficient Apex code. This guide covers reliable benchmarking techniques and real-world performance data. > **Sources**: > - [James Simone - Benchmarking Matters](https://www.jamessimone.net/blog/joys-of-apex/benchmarking-matters/) > - [Dan Appleman - Advanced Apex Programming](https://www.advancedapex.com/) > - [Justus van den Berg - Heap & CPU Optimization](https://medium.com/@justusvandenberg) ---

  • 9 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added October 11, 2026
designgotestingperformance

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 101-benchmarking-guide_5ad61ae7 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 101 Benchmarking Guide 5ad61ae7?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 101 Benchmarking Guide 5ad61ae7
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-101-benchmarking-guide-5ad61ae7/badge)](https://www.skillsdirectory.com/skills/tools-only-101-benchmarking-guide-5ad61ae7)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# Apex Benchmarking Guide

Performance testing is essential for writing efficient Apex code. This guide covers reliable benchmarking techniques and real-world performance data.

> **Sources**:
> - [James Simone - Benchmarking Matters](https://www.jamessimone.net/blog/joys-of-apex/benchmarking-matters/)
> - [Dan Appleman - Advanced Apex Programming](https://www.advancedapex.com/)
> - [Justus van den Berg - Heap & CPU Optimization](https://medium.com/@justusvandenberg)

---

## Why Benchmark?

"Premature optimization is the root of all evil" - but **informed optimization** is essential. Benchmarking answers:

1. **Which approach is faster?** (Loop styles, data structures)
2. **Will this scale?** (200 records vs 10,000)
3. **Where are the bottlenecks?** (CPU, heap, SOQL)

---

## Dan Appleman's Benchmarking Technique

The gold standard for Apex performance testing:

### The Pattern

```apex
// Run in Anonymous Apex for consistent environment
Long startTime = System.currentTimeMillis();

// Your code to benchmark
for (Integer i = 0; i < 10000; i++) {
    // Operation being tested
}

Long endTime = System.currentTimeMillis();
System.debug('Duration: ' + (endTime - startTime) + 'ms');
```

### Key Principles

| Principle | Why It Matters |
|-----------|----------------|
| **Use Anonymous Apex** | Consistent execution environment, no trigger interference |
| **Run Multiple Iterations** | Averages out JIT compilation and garbage collection |
| **Test at Scale** | 200 records ≠ 10,000 records in terms of performance |
| **Isolate the Operation** | Test one thing at a time |
| **Run Multiple Times** | First run often slower due to JIT compilation |

### Example: Complete Benchmark

```apex
// Comprehensive benchmark template
public class BenchmarkRunner {

    public static void compareMethods() {
        Integer iterations = 10000;

        // Warm-up run (JIT compilation)
        warmUp();

        // Method A
        Long startA = System.currentTimeMillis();
        for (Integer i = 0; i < iterations; i++) {
            methodA();
        }
        Long durationA = System.currentTimeMillis() - startA;

        // Method B
        Long startB = System.currentTimeMillis();
        for (Integer i = 0; i < iterations; i++) {
            methodB();
        }
        Long durationB = System.currentTimeMillis() - startB;

        // Results
        System.debug('Method A: ' + durationA + 'ms');
        System.debug('Method B: ' + durationB + 'ms');
        System.debug('Difference: ' + Math.abs(durationA - durationB) + 'ms');
        System.debug('Winner: ' + (durationA < durationB ? 'Method A' : 'Method B'));
    }

    private static void warmUp() {
        for (Integer i = 0; i < 100; i++) {
            methodA();
            methodB();
        }
    }

    private static void methodA() { /* Implementation A */ }
    private static void methodB() { /* Implementation B */ }
}
```

---

## Real-World Benchmark Results

### String Concatenation vs String.join()

From Justus van den Berg's testing:

| Method | Records | Duration | Result |
|--------|---------|----------|--------|
| String `+=` in loop | 1,750 | 11,767ms | CPU LIMIT HIT |
| `String.join()` | 7,500 | 539ms | Still running |
| **Improvement** | - | **22x faster** | - |

```apex
// ❌ SLOW: String concatenation in loop (O(n²) string copies)
String result = '';
for (Account acc : accounts) {
    result += acc.Name + '\n';  // Creates new string each time!
}

// ✅ FAST: String.join() (O(n) single allocation)
List<String> names = new List<String>();
for (Account acc : accounts) {
    names.add(acc.Name);
}
String result = String.join(names, '\n');
```

### Loop Performance Comparison

From Beyond the Cloud benchmarking (10,000 iterations):

| Loop Type | Duration | Notes |
|-----------|----------|-------|
| While loop | ~0.4s | Fastest |
| Cached iterator | ~0.8s | Good alternative |
| For loop (index) | ~1.4s | Acceptable |
| Enhanced for-each | ~2.4s | Convenient but slower |
| Uncached iterator | CPU LIMIT | Avoid |

```apex
// 🏆 FASTEST: While loop
Iterator<Account> iter = accounts.iterator();
while (iter.hasNext()) {
    Account acc = iter.next();
    // process
}

// ✅ GOOD: Traditional for loop
for (Integer i = 0; i < accounts.size(); i++) {
    Account acc = accounts[i];
    // process
}

// ⚠️ CONVENIENT BUT SLOWER: Enhanced for-each
for (Account acc : accounts) {
    // process - OK for small collections
}
```

### Map vs List Lookup

| Operation | Complexity | 10,000 lookups |
|-----------|------------|----------------|
| List.contains() | O(n) | ~500ms |
| Set.contains() | O(1) | ~5ms |
| Map.containsKey() | O(1) | ~5ms |

```apex
// ❌ SLOW: List lookup
List<Id> processedIds = new List<Id>();
for (Account acc : accounts) {
    if (!processedIds.contains(acc.Id)) {  // O(n) each time!
        processedIds.add(acc.Id);
    }
}

// ✅ FAST: Set lookup
Set<Id> processedIds = new Set<Id>();
for (Account acc : accounts) {
    if (!processedIds.contains(acc.Id)) {  // O(1) constant time
        processedIds.add(acc.Id);
    }
}
```

---

## Governor Limit Ceilings

### Official Limits

| Limit | Synchronous | Asynchronous |
|-------|-------------|--------------|
| CPU Time | 10,000 ms | 60,000 ms |
| Heap Size | 6 MB | 12 MB |
| SOQL Queries | 100 | 200 |
| DML Statements | 150 | 150 |

### Practical Thresholds

| Limit | Warning (80%) | Critical (95%) |
|-------|---------------|----------------|
| CPU Time | 8,000 ms | 9,500 ms |
| Heap Size | 4.8 MB | 5.7 MB |
| SOQL Queries | 80 | 95 |

### Runtime Limit Checking

```apex
public void processWithSafety(List<Account> accounts) {
    Integer cpuWarning = 8000;
    Integer heapWarning = 4800000;

    for (Account acc : accounts) {
        // Check before each operation
        if (Limits.getCpuTime() > cpuWarning) {
            System.debug(LoggingLevel.WARN,
                'CPU approaching limit: ' + Limits.getCpuTime() + 'ms');
            // Consider switching to async or chunking
            break;
        }

        if (Limits.getHeapSize() > heapWarning) {
            System.debug(LoggingLevel.WARN,
                'Heap approaching limit: ' + Limits.getHeapSize() + ' bytes');
            break;
        }

        processAccount(acc);
    }
}
```

---

## Benchmarking Anti-Patterns

### ❌ Don't Do These

| Anti-Pattern | Problem | Solution |
|--------------|---------|----------|
| Testing in triggers | Inconsistent environment | Use Anonymous Apex |
| Single iteration | JIT variance affects results | Run 1000+ iterations |
| Testing with 10 records | Doesn't reveal O(n²) issues | Test with 200+ records |
| Ignoring warm-up | First run skewed by JIT | Add warm-up phase |
| Mixing operations | Can't isolate bottleneck | Test one thing at a time |

---

## Benchmarking Checklist

Before optimizing, verify:

- [ ] Ran benchmark multiple times (3-5 runs)
- [ ] Used 1000+ iterations for micro-benchmarks
- [ ] Tested with production-scale data (200+ records)
- [ ] Included warm-up phase
- [ ] Ran in Anonymous Apex (not test context)
- [ ] Compared both approaches fairly
- [ ] Considered readability trade-offs

---

## When NOT to Optimize

Sometimes clarity beats performance:

```apex
// More readable, negligible performance difference for small collections
for (Account acc : accounts) {
    acc.Description = acc.Name + ' - Updated';
}

// vs micro-optimized but harder to read
Iterator<Account> iter = accounts.iterator();
while (iter.hasNext()) {
    Account acc = iter.next();
    acc.Description = acc.Name + ' - Updated';
}
```

**Rule of thumb**: Optimize when:
1. Processing 200+ records regularly
2. Approaching governor limits
3. User experience is affected
4. Benchmarks show measurable improvement

---

## Related Resources

- [templates/benchmarking-template.cls](../templates/benchmarking-template.cls) - Ready-to-use benchmark template
- [templates/cpu-heap-optimization.cls](../templates/cpu-heap-optimization.cls) - Optimization patterns
- [Apex Log Analyzer](./log-analysis-tools.md) - Visual performance analysis

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.