PInvoke of _wtof_l in C# faster than natively invoking it from C++

Viewed 175

I was writing some high performance code in C# and I wanted to compare my implementation to native C++ one, as I was pinvoking msvcrt function a lot. To my suprise it appears that C# version of the code is faster than it's native version (!). Can someone explain this behavior?

C# version:

using System.Diagnostics;
using System.Security;
using System.Runtime.InteropServices;

class Program
{
    [DllImport("msvcrt.dll", EntryPoint = "_wtof_l", CallingConvention = CallingConvention.Cdecl)]
    [SuppressUnmanagedCodeSecurity]
    private extern unsafe static double _wtof_l(char* str, IntPtr locale);

    [DllImport("msvcrt.dll", EntryPoint = "_create_locale", CallingConvention = CallingConvention.Cdecl)]
    private extern static IntPtr CreateLocale(int category, string locale);
    private const int LC_NUMERIC = 4;

    static unsafe void Main(string[] args)
    {
        var locale = CreateLocale(LC_NUMERIC, "C");
        fixed (char* test = "1.2")
        {
            int x = 10;
            while (x-- > 0)
            {
                var sw = Stopwatch.StartNew();

                double sum = 0;
                for (int i = 0; i < 10_000_000; i++)
                {
                    sum += _wtof_l(test, locale);
                }

                Console.WriteLine(sum + " " + sw.ElapsedMilliseconds);
            }
        }

        Console.ReadLine();
    }
}

C++ version:

#include <locale.h>
#include <stdio.h>  
#include <chrono>
#include <string>
#include <iostream>

int main()
{

    auto test = L"1.2";
    _locale_t locale = _create_locale(LC_NUMERIC, "C");

    int x = 10;
    while (x--)
    {
        auto start = std::chrono::high_resolution_clock::now();

        double sum = 0;
        for (int i = 0; i < 10000000; i++)
        {
            sum += _wtof_l(test, locale);
        }

        auto end = std::chrono::high_resolution_clock::now();
        std::chrono::duration<double> diff = end - start;

        std::cout << sum << " " << diff.count() << std::endl;

    }

    std::getline(std::cin, std::string());

    return 0;
}

Both applications were compiled on x86 Release with VS2017, and both were run multiple times with VisualStudio turned off. Below are results on my machine. As you can see, C# version is faster by about 30%:

enter image description here

Can someone explain this confusing behavior? My guess would be either: Some optimizations are not turned on in default Win32 C++ ConsoleApplication project, or C++ runtime does some initialization code in C++ application that slows down invocations of _wtof_l.

1 Answers

I knocked a 0 off so I wouldn't have to wait so long. Median values:

C#  x86: 520 msec
C#  x64: 395 msec 
C++ x86: 408 msec
C++ x64: 273 msec

That I'll buy. Note how the 64-bit version of the C# program could beat the 32-bit C++ program. So that's one explanation.

But the bigger difference you see might well be because you are not comparing the same wtof() implementations. Your C++ program uses the one that came included with the VS install (typically msvcrxxx.dll), not msvcrt.dll. There was a very big rewrite in VS2015. I measured the CRT for VS2017 rtm, msvcrt.dll from Win10 Anniversary.

Bigger picture conclusions that I often see back: the pinvoke marshaller does not suck, 64-bit code does not suck, managed code tends to be 80% of native code. And YMMV.

Related