7

Possible Duplicate:
F# seems slower than other languages… what can I do to speed it up?

I am a bit curious about the performance of pattern match, so I did the following test:

poolEven contains 10000 elements of 0,1,2,3, (2500 equal)

testSize = 100000

IfelseEven(100000) takes 650ms (switch would be faster but I didn't attach the code) while MatchEven(100000) takes 7000ms that's 10x time

Does the performance degradation come from Array.Fold? I am 100% sure that if I go for IEnumerable.Aggregate the speed would greatly decrease. But I thought F# handled Array.Fold better than C# with IEnumerable.Aggregate. I want to compare performance of most common (equivalent) ways of coding in 2 languages but not rigid ways to make them identical.

The tests are done in x64 release, with 10+ trials taken average with proper warm up

C#:

public void IfelseEven(int testSize)
{
    Ifelse(testSize, poolEven);
}

void Ifelse(int testSize, int[] pool)
{
    long sum = 0;
    for (int i = 0; i < testSize; i++)
    {
        for (int j = 0; j < poolCapacity;j++ )
        {
            var item = pool[j];
            if (item == 0)
            {
                sum += 5;
            }
            else if (item == 1)
            {
                sum += 1;
            }
            else if (item == 2)
            {
                sum += 2;
            }
            else if (item == 3)
            {
                sum += 3;
            }
            else
            {
                sum += 4;
            }
        }
    }
}

public void MatchEven(int testSize)
{
    PatternMatch.myMatch(testSize, poolEven);
}

F#:

module PatternMatch

let mat acc elem =
    acc +
    match elem with
    | 0 -> 5L
    | 1 -> 1L
    | 2 -> 2L
    | 3 -> 3L
    | _ -> 4L

let sum (pool:int[])=
    Array.fold mat 0L pool;

let myMatch testSize pool=
    let mutable tmp = 0L
    for i=0 to testSize do
        tmp <- sum(pool) + tmp
    tmp
Community
  • 1
  • 1
colinfang
  • 20,909
  • 19
  • 90
  • 173
  • 3
    Why dont you try the C# side with the same F# code, ie use Aggregate/Fold (on both sides)? – leppie Aug 29 '12 at 16:34
  • I dont know F#, but the `Fold` should be easy to convert to a loop. – leppie Aug 29 '12 at 16:37
  • Also: why are you calling `sum(pool)` on every loop? I dont see anything in `pool` changing. – leppie Aug 29 '12 at 16:39
  • @leppie yes switch is faster. Why i don't use Aggregate is because I want to compare performance of most common ways (equivalent) of coding in 2 languages but not exactly identical ways. sum(pool) is used for more loops... cus it is too fast for each 10000 iterations. – colinfang Aug 29 '12 at 16:46
  • 3
    As you can see, there is a significant performance penalty on 'tight' (do they even need to be tight with today's big CPU caches?) loops using higher-order functions (as with `Array.Fold`). Changing that to a simple tail recursive loop (and you dont even need to change that many characters) would be trivial, and would likely bring the performance within par (you can even try adding `inline`). – leppie Aug 29 '12 at 16:51
  • 3
    @colinfang That's an interesting comparison then, but it doesn't isolate pattern matching versus if/else. – Sean U Aug 29 '12 at 17:17
  • `match` compiles to the same branching instructions as `if`/`else`. Any perceived difference is due to something else in your code. – Daniel Aug 29 '12 at 19:00

2 Answers2

11

Voting to close—we could play this game all day long. For more commentary on why different code might have different execution times see this question and answers. If you just want to speed up your F# function, try this:

let ifElse testSize (pool: _[]) = 
  let mutable sum = 0L
  for i = 0 to testSize - 1 do
    for j = 0 to pool.Length - 1 do
      match pool.[j] with
      | 0 -> sum <- sum + 5L
      | 1 -> sum <- sum + 1L
      | 2 -> sum <- sum + 2L
      | 3 -> sum <- sum + 3L
      | _ -> sum <- sum + 4L
  sum

By my measurements this handily licks the C# function (and it's still shorter and more readable):

C# 5655
F# 4003

Incidentally, leppie nailed it in the comments. I profiled your code and 78% of the time was spent in Array.fold—not good in a tight loop.

Community
  • 1
  • 1
Daniel
  • 47,404
  • 11
  • 101
  • 179
7

As mentioned in the comments, by using an entirely different pattern, you are not really getting a comparison of match vs if/elseif in isolation. Comparison of loops vs fold vs recurse is an entirely different question.

Using a more direct comparison (loops, same as Daniel's response), I got below results. Release build, .NET 4.5, x64 target arch.

  • C# and F# if\elseif approaches run pretty much exactly the same (in this contrived example).

  • F# match was consistently faster than if\elseif in either language (in this contrived example)

  • C# switch was consistently fastest (in this contrived example).

latkin
  • 16,402
  • 1
  • 47
  • 62