1. Salesforce Apex Hours
Apex Code Benchmarking
#SalesforceApexHours #FarmingtonHillsSFDCDug
Speaker
Date
Venue/Link
Purushottam Bhaigade , Amit Chaudhary
Saturday, Dec 7, 2019 10:00 AM EST ( 8:30 PM IST )
Online
Farmington Hills Salesforce Developer User Group
2. Who am I ?
Amit Chaudhary (Salesforce MVP)
• Active on Salesforce Developer Community
• Blogging at http://amitsalesforce.blogspot.in/
• Co-Organizer of – FarmingtonHillsSFDCDug
• Follow us @Amit_SFDC or @ApexHours
#SalesforceApexHours #FarmingtonHillsSFDCDug
3. Our Speaker
#SalesforceApexHours #FarmingtonHillsSFDCDug
Purushottam Bhaigade
• Technical Evangelist, Eternus Solutions
• Content Leader of – Pune Salesforce Developer Group
• Co-Organizer of – Pune Salesforce Architect Summit
• Speaker (IndiaDreamin 2X, Hyderabad Trailblazin, Apex
Hours for Students, Pune Salesforce DUG)
• Blogging at https://forcefeed.dev/
@bhaigadepuru
/in/purushottambhaigade
5. #FarmingtonHillsSFDCdug #SalesforceApexHours
Before Winter’14
• Script limit – Total number of line of Apex code executed
• Separate limits for each Managed Package (Aloha)
• Trigger Context – 200,000 lines
• Async (Batch & Future) – 1,000,000 lines
• Workflow, Process Builders, Formulas has no limits at all
Trigger
8. #FarmingtonHillsSFDCdug #SalesforceApexHours
CPU Time Limits
Asynchronous Context
60 Seconds
Apply to All code, including Package
code
Apply to declarative :Workflows, Process
Builder, Flows, Validation Rules, Formulas
etc.
Trigger Context
10 Seconds
Apply to All code, including Package
code
Apply to declarative :Workflows,
Process Builder, Flows, Validation Rules,
Formulas etc.
Enough time
for everyone
13. #FarmingtonHillsSFDCdug #SalesforceApexHours
Consequences
• You have installed more number of packages? Probably
you are at greater risk of running into limits.
• Your managed packaged was release before winter’14
and have passed script limit rule it doesn’t mean now it will
not hit CPU limit.
15. #FarmingtonHillsSFDCdug #SalesforceApexHours
Why Software Benchmarking is
hard?
• Time measurement can’t trusted always, because system
performance varies based on time of day and momentary load.
• Debug log cost CPU time
• You don’t have control on background operations like garbage
collection
• Result might change with each patch release
17. #FarmingtonHillsSFDCdug #SalesforceApexHours
Static vs Dynamic Apex
public static void StaticVsDynamicDML()
{
Integer startCPUTime;
// Grab a contact
Contact ct = [Select Id, AccountId from Contact where LastName Like 'testcontact%' Limit 1];
ID sourceid = ct.AccountID;
ID actid;
startCPUTime = Limits.getCpuTime();
for(Integer x=0; x< 100000; x++)
{
// Put what you are measuring in here
}
System.debug(LoggingLevel.Error, ' ElapsedCPUTime = ' + String.ValueOf(Limits.getCpuTime()-
startCPUTime));
}
20. #FarmingtonHillsSFDCdug #SalesforceApexHours
Consequences
• Assignment from static field reference is 20x time slower than
variable assignment
• Assignment from dynamic field reference is 30x time slower
than static assignment
• Use temporary variable instead of referencing a field
multiple times!
22. #FarmingtonHillsSFDCdug #SalesforceApexHours
Describe information is cached, but not always as much as
you’ll need
Account.sObjectType.getDescirbe
1 ms first time, 0.015 ms subsequent
getGlobalDescribe 343 items:
13 ms first time, 1 ms subsequent
getGlobalDescribe 863 items:
22 ms first time, 6 ms subsequent
Conclusion :
You can rely on Describe caching for
most cases, but never put
getGlobalDescibe in loop. Do your own
caching – use singleton pattern
ISV’s use extra care – you don’t know
how big the org will be
23. #FarmingtonHillsSFDCdug #SalesforceApexHours
Doing lots of Calculations ? Then use doubles instead of decimals
Doubles are 200 times faster
Array Iteration
for (Account acc : array) – 4 microseconds (with assignment)
for (Integer count=0; count < array.size(); count++) – 2.5 microseconds
Integer arraySize = array.size();
for (Integer count=0; count < arraySize; count++) – 0.75 microseconds
24. #FarmingtonHillsSFDCdug #SalesforceApexHours
Serializing data
Conclusion :
Serializing data can eat up CPU time, depending
on the amount of data being serialized
The amount of CPU time it takes to serialize does
not necessarily double when the amount of data
is doubled – there’s a lot of variation
Similar result were found when serializing a list of
IDs
JSON.serialize() on 1 string: ~73 microseconds
JSON.serialize() on list of 50 string: ~1 ms (~20
microseconds per item)
JSON.serialize() on list of 100 string: ~1.9 ms (~18-
19 microseconds per item)
JSON.serialize() on list of 200 string: ~3.4 ms (~16-
17 microseconds per item)
JSON.serialize() on list of 400 string: ~7 ms (~17-18
microseconds per item)
28. #FarmingtonHillsSFDCdug #SalesforceApexHours
To benchmark declarative you need After
Trigger
public class LeadTriggerHandler {
public static Integer startTime;
public static void processLeadTrigger(){
if(startTime == null){
startTime = Limits.getCpuTime();
return;
}
system.debug(Logginglevel.error, 'Elapsed CPU time :'+String.valueOf(Limits.getCpuTime()-
startTime));
system.debug(Logginglevel.error, 'isInsert '+trigger.isInsert+'isUpdate
'+trigger.isUpdate+'isBefore'+trigger.isBefore+'isAfter
'+trigger.isAfter);
}
}
30. #FarmingtonHillsSFDCdug #SalesforceApexHours
Code vs Declarative Performance
Test Case CPU Time
Insert 200 leads – No code 1.1 ms per lead
Insert 200 leads – Apex code does logic in a
before update trigger
2.2 ms per lead (+1.1 ms)
Insert 200 leads – Workflow Rule updates fields
2.8 ms per lead (+1.7 ms)
Insert 200 leads – Process updates fields
8.2 ms per lead (+7.1 ms)
32. #FarmingtonHillsSFDCdug #SalesforceApexHours
Consequences
• The cost of sending email notifications via Workflows can add up 15 ms/email
(10 field merge template). Ultimately that is 3 seconds for 200 records !
• What is Alternative then?
Create task for user – only 0.5 ms per record (tested with basic formula
workflow)
• Validation Rules
Each validation formula function takes about 30 microseconds.
Example : 10 validation rules, each with 10 functions, applied to 200 records =
600 ms
35. #FarmingtonHillsSFDCdug #SalesforceApexHours
In Trigger Context..
App A
2 seconds
App B
2 seconds
App C
2 seconds
Workflow
field update
2 seconds
App A
2 seconds
App B
2 seconds
CPU timeout here.. Random App gets
the blame(depending on trigger
order)
You are innocent doesn’t mean they
aren’t going to frame you
36. #FarmingtonHillsSFDCdug #SalesforceApexHours
Lets try out This
Have after insert trigger on Lead – (mention in earlier slide)
Add invocable method with below logic and call it from process builder
@InvocableMethod
public static void delay(List<Id> leadIds){
List<Integer> largeArray = new List<Integer>();
for(Integer x=0;x<10000;x++){
largeArray.add(x);
}
for(Id leadId :leadIds){
String jsonStr = JSON.serialize(largeArray);
}
}
38. #FarmingtonHillsSFDCdug #SalesforceApexHours
Process Silently Fail – Trigger Cause Exception
& Rollback
Insert 400
Leads
After insert
trigger
(first 200)
Process
Call Apex
Process
Updates
Leads
After insert
trigger
(last 200)
Serialize
large arrays
CPU timeout happens
here..
Aborts this action silently
CPU timeout detected
here..
Apex error blames the
trigger
39. #FarmingtonHillsSFDCdug #SalesforceApexHours
How to prevent this?
• Use Limits.getCPUTime() to find out you are getting into trouble, then
either exit or go async if you are getting close
• Learn more advance architectures to detect reentrancy
• Try to use Platform event in your solution for Async operation
• Accept that you still may get the blame
40. #FarmingtonHillsSFDCdug #SalesforceApexHours
Some Good news !!
• Figuring out how to optimize applications is fun seriously
• Figuring out what went wrong is also fun (unless you’re
getting blame )
• You have great job security (unless, of course you’re
getting blame )