10

I have a twist on this question. What is the relative performance of each of the following methods?

Via Peter Knolle:

public static Set<String> deserializeToStrings(Set<Id> input)
{
    return (Set<String>)JSON.deserialize(JSON.serialize(input), Set<String>.class);
}

Via Jenny B (and sfdcfox):

public static Set<String> stringify(Set<Id> input)
{
    return new Set<String>(String.join(new List<id>(input), ',').split(','));
}

Via E.J. Wilburn:

public static Set<String> castToStrings(Set<Id> input)
{
    return new Set<String>((List<String>)new List<Id>(input));
}

I have been using the third methodology and I am curious if it is more or less efficient than the others.

  • Adrian -- option 2 fails when the input is an empty list; the resulting set<String> has one element with value equal to empty string because split() on an empty list results in a list of size 1 with member[0] value = empty string. Option 2 has to be written more 'cleverly' – cropredy Mar 7 '16 at 0:39
  • @crop1645 I think they're all missing null checks too. I just wanted to analyze what was out there from a performance perspective. Good point though, it can certainly be improved (which would probably slow it down a tad bit more). – Adrian Larson Mar 7 '16 at 1:48
  • options 1 and 3 are fine as is on empty set<id> input; only option 2 has the issue – cropredy Mar 7 '16 at 2:05
11

Approach

This should be my final update. I reran my tests, setting all Debug Levels to NONE except Apex, which I set to ERROR. The findings are more or less unchanged: casting beats all other approaches. I added more data, however, to demonstrate the level of difference more rigorously.

I used the following strategy to profile the above in Execute Anonymous:

static Set<Id> generateDummyIds(Integer size)
{
    Set<Id> ids = new Set<Id>();
    for (Integer i=0; i<size; i++)
    {
        String suffix = String.valueOf(i).leftPad(15).replace(' ', '0');
        ids.add(Id.valueOf('001' + suffix));
    }
    return ids;
}
final Integer SET_ELEMENTS = 0;
final Integer ITERATIONS = 1000;

Set<Id> input = generateDummyIds(SET_ELEMENTS);
Long start = Datetime.now().getTime();
for (Integer i = 0; i < ITERATIONS; i++) PerformanceTesting.deserializeToStrings(input);
system.debug(LoggingLevel.ERROR, Datetime.now().getTime() - start);

I used the following value combinations ten times each:

  • SET_ELEMENTS = 0 with ITERATIONS = 1000 for 10k sample size
  • SET_ELEMENTS = 1 with ITERATIONS = 1000 for 10k sample size
  • SET_ELEMENTS = 1000 with ITERATIONS = 50 for 500 sample size
  • SET_ELEMENTS = 100000 with ITERATIONS = 1 for 10 sample size

Modifications

I added a fourth method out of curiosity, even though OP of that question requested to avoid it. This method provides a baseline against which to measure alternatives.

public static Set<String> loopToStrings(Set<Id> input)
{
    Set<String> output = new Set<String>();
    for (Id id : input) output.add(id);
    return output;
}

I also modified the stringify method to produce the correct results as noted by @crop1645:

public static Set<String> stringify(Set<Id> input)
{
    return (input == null || input.isEmpty()) ? new Set<String>() :
        new Set<String>(String.join(new List<id>(input), ',').split(','));
}

Data

Of note is that the deserializeToStrings and stringify methods both failed with 100k elements in the set. The former failed due to heap size exceeded, the latter got a REGEX_TOO_COMPLICATED error.

First let's take a look at milliseconds per call:

                  0       1      1k         100k
deserialize  0.2498  0.2118  14.290   HEAP ERROR
stringify    0.0657  0.1023   2.276  REGEX ERROR
loop         0.0465  0.0914  17.164       1705.4
cast         0.0385  0.0594   2.000        210.0

Chart - Milliseconds Per Call

Next let's examine calls per second:

                 0        1     1k           100k
deserialize   4003     4272     70     HEAP ERROR
stringify    15221     9775    439    REGEX ERROR
loop         21505    10941     58           0.59
cast         25974    16835    500           4.76

Chart - Calls Per Second

  • 6
    So, I'd go with the direct cast-to-strings, then. I learned something new today. – sfdcfox Mar 6 '16 at 18:15
  • In fact, it is the only approach that significantly beats out looping. – Adrian Larson Mar 6 '16 at 18:29
  • there have been a rash of these 'which is better' posts recently - perhaps you could collect them all into a consolidated blog post that is SEO; as noted in my comment to the OP, option 2 needs enhancement – cropredy Mar 7 '16 at 0:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.