Salesforce Benchmark Package

My previous two blog posts, Salesforce Record Automation Benchmarking and Salesforce Single VS Multi-Object Flow Field Update Benchmark, contain various Salesforce benchmarks that answered some performance questions among the varying declarative record automation options. With those posts already long, a separate article detailing how the benchmarks were implemented was prudent.

Source Code

The Salesforce Benchmark Github repo contains the metadata for those two benchmarks performed so far. When other benchmarks are added, their metadata will be added too.

Benchmark Installation

To run these benchmarks yourself, first install the Benchmark 1.1 unmanaged package into your org. This should be a sandbox, personal developer edition org, or a trailhead org. Don’t install this into production with real users.

Installation

  1. Login to Salesforce as an admin.
  2. In your browser’s url, append /packaging/installPackage.apexp?p0=04t4W000002vlYg after the domain and click enter. This should open the package installation page.
    • For example, if your browser says https://lfreeland-dev1-dev-ed.lightning.force.com/lightning/page/home, replace /lightning/page/home with /packaging/installPackage.apexp?p0=04t4W000002vlYg
  3. When the package installation page is shown, ensure that the “Admins Only” option is checked and click Install. This will take a few minutes and then should say complete.

Benchmark 1.1 Production / Dev Edition Package – This will open the installation page but will ask for you to login first. Works in production orgs and dev edition orgs that aren’t sandboxes.

Benchmark 1.1 Sandbox Package – This will open the installation page for standboxes but will ask for you to login first.

Design Overview

Each benchmark operation is performed on its own custom object that has an Auto Number name. This makes the setup & teardown easier by making it programmatic. Some people use a single custom object with multiple record automation solutions on it but then activate and deactivate them which is time consuming. With a custom object per benchmark, everything can stay activated and isolated. The trade-off is a little more initial setup time and more metadata.

The RecordAutomationBenchmarking apex class has various run*Benchmark methods that run the benchmark and insert the result into the Benchmark Result custom object. One runs these benchmark methods using VS Code with the “SFDX: Execute Anonymous Apex with Editor Contents” from a separate file. Each Benchmark Result record counts as a run in the Benchmark articles.

Many of the other community benchmarks available make one enable debug logs and inspect the log files for the timings. That’s an option but takes more time to compile results and may not be as accurate according to the Dark Art of CPU Benchmarking presentation by Dan Appleman & Robert Watson. Storing the results in a custom object lets us see what the performance is like over time and with varying amounts of records. It should also be more accurate but I haven’t compared those timings with debugging enabled.

Same-Record Field Update Details

With the high-level design specified, let’s dig into the record automation details of the same-record field update.

Before-Save Flow

The “Benchmark – Before Insert Flow Text Assignment” flow on the “Before Save Field Update” custom object runs when a record is inserted only without any criteria and it uses an assignment to set the “Text 1” field to a default value.

Run this benchmark by invoking this code in a new file using the Execute Anonymous SFDX command in VS Code:

new RecordAutomationBenchmarking().runBeforeSaveFlowOnInsertBenchmark();

That will insert 200 “Before Save Field Update” records”, record the insertion time taken in milliseconds, and store the results as a “Before Save Flow On Insert” Benchmark Result record.

Process Builder

The “Benchmark – PB Field Update” process builder runs when a “Process Builder” custom object record is inserted. Without any criteria, it updates the “Text 1” custom field to a custom value.

Run this benchmark by invoking this code in a new file using the Execute Anonymous SFDX command in VS Code:

new RecordAutomationBenchmarking().runProcessBuilderOnInsertBenchmark();

That will insert 200 “Process Builder” records”, record the insertion time taken in milliseconds, and store the results as a “Process Builder No Criteria On Insert” Benchmark Result record.

Apex Trigger

The “Benchmark_Before_Save_Field_Update” Apex Trigger on the “Before_Trigger_Field_Update__c” custom object runs only before inserting a record and it sets the “Test_1__c” custom field to a default value.

trigger Benchmark_Before_Save_Field_Update on Before_Trigger_Field_Update__c (before insert) {
    for (Before_Trigger_Field_Update__c newRecord : Trigger.new) {
        newRecord.Test_1__c = 'Benchmark Test';
    }
}

Run this benchmark by invoking this code in a new file using the Execute Anonymous SFDX command in VS Code:

new RecordAutomationBenchmarking().runTriggerUpdateFieldOnInsertBenchmark();

That will insert 200 “Before Trigger Field Update” records”, record the insertion time taken in milliseconds, and store the results as a “Trigger Update Field On Insert” Benchmark Result record.

After-Save Flow

The “Benchmark – After Insert Flow Text Assignment – V1” flow runs on after insert of an “After Save Field Update” record. It has no criteria and update the “Text 1” custom field to a default value using an Update Records.

Run this benchmark by invoking this code in a new file using the Execute Anonymous SFDX command in VS Code:

new RecordAutomationBenchmarking().runAfterSaveFlowOnInsertBenchmark();

That will insert 200 “After Save Field Update” records”, record the insertion time taken in milliseconds, and store the results as a “After Save Flow On Insert” Benchmark Result record.

Workflow

The “Benchmark – Field Update” workflow rule runs when a “Workflow Field Update” record is inserted and runs without any criteria. It updates the “Text 1” field to a default value.

Run this benchmark by invoking this code in a new file using the Execute Anonymous SFDX command in VS Code:

new RecordAutomationBenchmarking().runWorkflowUpdateFieldOnInsertBenchmark();

That will insert 200 “Workflow Field Update” records”, record the insertion time taken in milliseconds, and store the results as a “After Save Flow On Insert” Benchmark Result record.

Before Delete Benchmark Details

This benchmark captures how long inserting an “audit” record takes using a before delete flow and a before delete apex trigger.

Before Delete Flow

The “Benchmark – Before Delete Insert Child Record – V1” flow runs before the “Before Delete Example” record is deleted without any criteria and inserts a “Before Delete Child” record using a Create Records action.

Run this benchmark by invoking this code in a new file using the Execute Anonymous SFDX command in VS Code:

new RecordAutomationBenchmarking().runBeforeDeleteFlowInsertChildRecordsBenchmark();

That will insert 200 “Before Delete Example” records” and then delete them, record the deletion time taken in milliseconds, and store the results as a “Before Delete Flow Insert Child Records” Benchmark Result record.

Before Delete Trigger

The “Benchmark_Before_Delete_Trigger_Insert_Child_Records” apex trigger on the “Before_Delete_Trigger_Example__c” custom object runs on before delete and inserts the same “Before Delete Child” record.

trigger Benchmark_Before_Delete_Trigger_Insert_Child_Records on Before_Delete_Trigger_Example__c (Before Delete) {
    List<Before_Delete_Child__c> childrenToInsert = new List<Before_Delete_Child__c>();
    
    for (Before_Delete_Trigger_Example__c deletedRecord : Trigger.Old) {
        Before_Delete_Child__c childToInsert = new Before_Delete_Child__c(
            Description__c = 'Before Delete Benchmark'
        );
        
        childrenToInsert.add(childToInsert);
    }
    
    insert childrenToInsert;
}

Run this benchmark by invoking this code in a new file using the Execute Anonymous SFDX command in VS Code:

new RecordAutomationBenchmarking().runBeforeDeleteTriggerInsertChildRecordsBenchmark();

That will insert 200 “Before Delete Trigger Example” records” and then delete them, record the deletion time taken in milliseconds, and store the results as a “Before Delete Trigger Insert Child Records” Benchmark Result record.

Remove “Before Delete Child” Records Periodically

Every so often, the “Before Delete Child” records have to be cleaned out since the benchmark doesn’t currently do that. With a personal developer edition, my org was filling up quickly. To remove them periodically, run the following in Execute Anonymous:

delete [Select Id from Before_Delete_Child__c Limit 10000];

Note: This will only delete up to 10,000 records each time so run it repeatedly as needed.

Single VS Multi-Object Flow Field Update Benchmark Details

This benchmark simulates multiple conditions being evaluated but only one action being taken and see what the performance was in a single flow with multiple outcomes in 1 decision compared to multiple flows but each with only one criteria being evaluated each. See Benchmark Article For Methodology Details.

Single Flow

The “Benchmark – Before Save Single Flow Multi Eval Field Update – V1” before save flow runs when “Before Save Single Flow Multi Update” records are inserted. It uses a decision element to see which “Field To Use” is requested to have its default value populated.

The decision has 10 outcomes listed from Field 1, Field 2, … to Field 10. Each outcome has an assignment to update the corresponding text field to a Default value. Here’s the Field 10 Assignment:

Run this benchmark by invoking this code in a new file using the Execute Anonymous SFDX command in VS Code:

new RecordAutomationBenchmarking().runSingleFlowMultipleCriteriaEvalOnInsertBenchmark();

That will insert 200 “Before Save Single Flow Multi Update” records”, record the insertion time taken in milliseconds, and store the results as a “Single Flow Multi Criteria Field Update” Benchmark Result record.

Muli-Object Flow

This benchmark simulates having multiple, before-save flows on the same object to set the default value on the requested “Field To Update”. The “Before Save Multiple Flow Single Update” custom object was created for this. It has 10 Before-Save On insert flows to handle that:

Each of these flows correspond to a particular requested field. Let’s look at “Benchmark – Before Save Multi Flow Single Eval Field Update 1”.

The start element run before-save without any criteria. It delegates to the decision element to see which “Field To Update” was requested. If the “Field 1” should be populated, the Update Field 1 assignment sets the “Field 1” to a default value.

Run this benchmark by invoking this code in a new file using the Execute Anonymous SFDX command in VS Code:

new RecordAutomationBenchmarking().runMultipleFlowSingleCriteriaEvalOnInsertBenchmark();

That will insert 200 “Before Save Multiple Flow Single Update” records”, record the insertion time taken in milliseconds, and store the results as a “Multiple Flow Single Criteria Field Update” Benchmark Result record.

Next Steps / Further Research

  • Add Multiple Run Support. Currently one has to run the desired benchmark method via execute anonymous. Eventually, I’d like to add a feature where one can select the benchmark to run and how many times to run it and then show the results.
  • Extensible Benchmark Framework. I’d like to enhance the code so one can more easily plugin just the benchmark code and the framework will automatically record the results.
  • Apex Profiling Benchmarks. I’m especially interested in more Apex profiling and this would help to see how things perform over time across releases. Adrian Larson’s Limits Profiler may be helpful but have to dig into it further since reinventing the wheel should be avoided.
  • Capture Salesforce release version. I’d like to capture the current Salesforce release version the benchmark is running on in order to compare the benchmarks over time. A little research was done to see how to determine that using Apex but couldn’t find anything. If one knows an Apex solution, please share!
  • Centralized Benchmark Results – It would be awesome if there was a centralized database storing the benchmark results from across the community with version information, org info, pod info, and so forth so the community could see trends of various operations over time.

Thoughts on the benchmark design and details? Was it helpful? What other benchmarks would be helpful? Let me know in the comments.