Avoid direct Schema.getGlobalDescribe() calls

When working in large Salesforce orgs, seemingly small operations—like retrieving an SObjectType token—can become surprisingly expensive. Recently, I ran a benchmark to quantify the performance cost of different SObjectType-retrieval strategies in Apex. The results were… eye-opening.

Below is a walkthrough of the test, the methodology, and what the numbers tell us about writing efficient Apex in massive orgs.

The Tests

The test measures four common approaches to retrieving SObjectType information in Apex, each repeated 200 times:

  1. Direct call to Schema.getGlobalDescribe()
  2. Using a cached version of the Global Describe map
  3. Using Type.forName() and instantiating the SObject
  4. Using Type.forName() only (no instantiation)

All tests were run in a very large Salesforce org—exactly the kind of environment where the describe API can become a performance bottleneck.

Here’s the Apex code used for the benchmark (run via Execute Anonymous), followed by real CPU time results.

/**
 * Performance benchmark for SObjectType retrieval methods.
 * Run this in Execute Anonymous.
 */
public class SchemaPerformanceTest {

    // Static cache for the second test case
    private Map<String, Schema.SObjectType> globalDescribeCache;

    public void runBenchmark() {
        Integer iterations = 200; // Number of times to run each operation
        String objectName = 'Account'; // Standard object for testing

        System.debug('=== STARTING BENCHMARK (' + iterations + ' iterations) ===');

        // ---------------------------------------------------------
        // TEST 1: Schema.getGlobalDescribe() (Standard)
        // ---------------------------------------------------------
        Long startTime1 = Limits.getCpuTime();
        for (Integer i = 0; i < iterations; i++) {
            Schema.SObjectType t = Schema.getGlobalDescribe().get(objectName);
        }
        Long endTime1 = Limits.getCpuTime();
        Long time1 = endTime1 - startTime1;
        System.debug('1. Schema.getGlobalDescribe() direct call: ' + time1 + ' ms');

        // ---------------------------------------------------------
        // TEST 2: Cached Schema.getGlobalDescribe()
        // ---------------------------------------------------------
        // Initialize cache once (simulating static initialization)
        globalDescribeCache = Schema.getGlobalDescribe();

        Long startTime2 = Limits.getCpuTime();
        for (Integer i = 0; i < iterations; i++) {
            Schema.SObjectType t = globalDescribeCache.get(objectName);
        }
        Long endTime2 = Limits.getCpuTime();
        Long time2 = endTime2 - startTime2;
        System.debug('2. Cached Global Describe Map: ' + time2 + ' ms');

        // ---------------------------------------------------------
        // TEST 3: Type.forName() (Reflection)
        // ---------------------------------------------------------
        
        Long startTime3 = Limits.getCpuTime();
        for (Integer i = 0; i < iterations; i++) {
            Type t = Type.forName(objectName); // Gets the Type
            Schema.SObjectType st = ((SObject)t.newInstance()).getSObjectType(); // Instantiates to get SObjectType
        }
        Long endTime3 = Limits.getCpuTime();
        Long time3 = endTime3 - startTime3;
        System.debug('3. Type.forName() instantiation: ' + time3 + ' ms');

        // ---------------------------------------------------------
        // TEST 4: Type.forName() (Just Type retrieval, not SObjectType)
        // ---------------------------------------------------------
        // Sometimes you only need the Type, not the SObjectType token immediately
        Long startTime4 = Limits.getCpuTime();
        for (Integer i = 0; i < iterations; i++) {
            Type t = Type.forName(objectName);
        }
        Long endTime4 = Limits.getCpuTime();
        Long time4 = endTime4 - startTime4;
        System.debug('4. Type.forName() (Type only): ' + time4 + ' ms');

        System.debug('=== BENCHMARK COMPLETE ===');
    }
}

// Execute the test
new SchemaPerformanceTest().runBenchmark();

Benchmark Results (200 iterations)

MethodDescriptionCPU Time
Schema.getGlobalDescribe()Direct call every iteration7,650 ms
Cached Global DescribeOne describe, reused1 ms
Type.forName() with instantiationReflection + SObject creation7 ms
Type.forName() without instantiationReflection only2 ms

Analysis

Schema.getGlobalDescribe() is expensive—and scales with org size

In small orgs, this may only cost a handful of milliseconds.
In massive orgs, it balloons dramatically.

This is because Salesforce must construct a map of every object in the org—including managed package objects, historical tables, big objects, etc.

If your code calls Schema.getGlobalDescribe() repeatedly, especially inside loops or triggers, you may be burning CPU at an alarming rate.

Caching Global Describe is practically free

    Building the map one time is the key.

    Lookup performance afterward is stunningly fast: 1 ms total for 200 iterations.

    Type.forName() is surprisingly fast

    If your framework, utility class, or trigger handler repeatedly needs SObjectType info, caching is essential.

    The reflection engine in Apex is far more optimized than most developers assume.
    Using:

    Type t = Type.forName('Account');
    
    

    …is only 2 ms across 200 iterations.

    Even with instantiation:

    Type t = Type.forName('Account');
    SObject s = (SObject)t.newInstance();
    Schema.SObjectType st = s.getSObjectType();
    
    

    …it’s only 7 ms.

    This makes Type.forName() an extremely efficient alternative when working with dynamic object names.

    Final Thoughts

    This benchmark confirms what many have suspected but not measured:
    Uncached describes can be shockingly expensive in large Salesforce orgs.

    By contrast:

    👍 Caching describe data
    👍 Using Type.forName()
    …are both extremely lightweight and can drastically reduce CPU usage.

    If you’re building scalable Apex—especially in enterprise orgs—optimizing how you retrieve SObjectType information is an easy win that can prevent future headaches.

    Leave a comment