'surprising c# execution times, IL, vs AOT, vs WASM (and c and JS too)

Writing a WASM app that needs some fast compute power (signal processing). Trying to decide if I should use c, JS or c# (I already have c# library that I wrote). So I decided to do CPU intense workload and compare. Will use Sieve of E

First to get a baseline I decided to just do on desktop, not WASM.

So here is c# code I used.

public static class App {
    static char[] prime = new char[100000];
    public static int SieveOfEratosthenes(int n) {

        Array.Fill<char>(prime, (char)1);


        for (int p = 2; p * p <= n; p++) {
            // If prime[p] is not changed,
            // then it is a prime
            if (prime[p] == 1) {
                // Update all multiples of p
                for (int i = p * p; i <= n; i += p)
                    prime[i] = (char)0;
            }
        }
        int count = 0;
        // Print all prime numbers
        for (int i = 2; i <= n; i++) {
            if (prime[i] == 1)
                count++;
        }
        return count;


    }
    public static void Run() {

        for (int i = 2; i < 99999; i++) {
            SieveOfEratosthenes(i);
        }
        // Driver Code

    }
    static void Main() {
        var start = DateTime.Now;
        Run();
        var end = DateTime.Now;
        var time = (end - start).TotalMilliseconds;
        Console.WriteLine(time);

    }
}   
  • Run release build using regular compiled output = ~14 seconds
  • Run crossgen2 x64 release output = ~22 seconds (!)
  • run crossgen2 x86 release output = ~12 seconds

Crossgen2 done by doing 'publish' in vs2022 and choose specific platform

I am surprised to see that the x64 is so slow. Any thoughts?

Another baseline I did was the same calculation in c and JS (still on desktop)

  • run c equivalent code = ~4 seconds
  • JS code in node = ~26 seconds

JS code

prime = new Uint8Array(100000);
function sieveOfEratosthenes(n)
{
     prime.fill(1);
    for (p = 2; p * p <= n; p++)
    {
        // If prime[p] is not changed, then it is a
        // prime
        if (prime[p] == 1)
        {
            // Update all multiples of p
            for (i = p * p; i <= n; i += p)
                prime[i] = 0;
        }
    }
    var count = 0;
    // Print all prime numbers

    for (i = 2; i <= n; i++)
    {
        if (prime[i] == 1)
           count++;
    }
    return count;
}
globalThis.sieve = () => {
    // Driver Code
    var n = 30;
    console.time("sieve");
    for (var j = 3; j < 99999; j++) {
        var count = sieveOfEratosthenes(j);
        //console.log(count);
    }
    console.timeEnd("sieve");
}
globalThis.sieve();

C code

#include <memory.h>
#include <stdio.h>
#include <time.h>

int SieveOfEratosthenes(int n)
{

   static   char prime[100000];
    memset(prime, 1, sizeof(prime));

    for (int p = 2; p * p <= n; p++)
    {
        // If prime[p] is not changed,
        // then it is a prime
        if (prime[p] == 1)
        {

            for (int i = p * p; i <= n; i += p)
                prime[i] = 0;
        }
    }
    int count = 0;
    // Print all prime numbers
    for (int p = 2; p <= n; p++)
        if (prime[p])
            count++;
    return count;

}

// Driver Code
int sieve()
{
    int start = time(NULL);
    for (int i = 3; i < 99999;i++)
    {

        int count = SieveOfEratosthenes(i);
       // printf("%d ", i/count);
    }
    int end = time(NULL);
   // printf("time=%d", end - start);
    return 0;
}
int main(int argc, char * argv[])
{
    sieve();
}

Second surprise is how much slower c# is that C. I didnt expect that much of a hit. Nor did I expect the JS version to give c# such a close race.

FYI, I WASMd the C code and the c# code and ran in Blazor App

  • c code ~4 seconds
  • JS code ~27 secs (as expected)
  • c# code 26 minutes !!
  • c# code AOT ~26 seconds

Lessons

  • C# blazor not AOT is horrifically slow
  • AOT c# ~= JS
  • C is best (a surprise that its so much faster)

Anybody think I messed anything up getting these numbers, as I said there are a few surprises in there.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source