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:
- Direct call to
Schema.getGlobalDescribe() - Using a cached version of the Global Describe map
- Using
Type.forName()and instantiating the SObject - 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)
| Method | Description | CPU Time |
|---|---|---|
Schema.getGlobalDescribe() | Direct call every iteration | 7,650 ms |
| Cached Global Describe | One describe, reused | 1 ms |
Type.forName() with instantiation | Reflection + SObject creation | 7 ms |
Type.forName() without instantiation | Reflection only | 2 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