Yes, PHP is faster than C#

So, I got an interesting spam comment on my post today:

“It gets even crazier when you actually benchmark the two languages only to discover in some real-world cases, PHP outperforms C#.”

I triple dare you to show code examples so we can explain why you’re wrong.

Quadruple dare

Jesus christ, how did you think this was true

By: No McNoington

This person couldn’t even be bothered to put in a decent pseudonym to call them by, but Mr./Mrs. McNoington, prepare to be blown away…

See, there’s something very common that all developers must do, and that is read files… we need to parse things, transform file formats, or whatever. So, let’s compare the two languages.

function test()
{
    $file = fopen("/file/file.bin", 'r');
    $counter = 0;
    $timer = microtime(true);
    while ( ! feof($file)) {
        $buffer = fgets($file, 4096);
        $counter += substr_count($buffer, '1');
    }
    $timer = microtime(true) - $timer;
    fclose($file);
    printf("counted %s 1s in %s milliseconds\n", number_format($counter), number_format($timer * 1000, 4));
}

test();
using System.Diagnostics;
using System.Text;

var test = () => {
    using var file = File.OpenText("/file/file.bin");
    var counter = 0;
    var sw = Stopwatch.StartNew();
    while(!file.EndOfStream)
    {
        if(file.Read() == '1')
        {
            counter++;
        }
    }
    sw.Stop();
    Console.WriteLine($"Counted {counter:N0} 1s in {sw.Elapsed.TotalMilliseconds:N4} milliseconds");
};

test();

Personally, I feel like this is a pretty fair assessment of each language. We will synchronously read a 4Mib file, byte-by-byte, and count the 1’s in the file. There’s very little user-land code going on here, so we’re just trying to test the very fundamentals of a language: reading a file. We’re only adding the counting here to prevent clever optimizing compilers (opcache in PHP, release mode in C#) from cheating and removing the code.

“But Rob,” I hear you say, “they’re not reading it byte-by-byte in the PHP version!” and I’d reply with, “but we’re not reading it byte-by-byte in the C# version either!”

Let’s see how it goes:

PHP: 32.49 ms (avg over 10 runs)
C#: 37.30 ms (avg over 10 runs)

That’s pretty crazy… I mean, we just read four megs, which is about the size of a decent photo. What about something like a video clip that might be 2.5 gigs?

PHP: 24.82 s (avg over 10 runs)
C#: 26.67 s (avg over 10 runs)

Now, I need to process quite a bit of incoming files from banks and bills and stuff for my household budgeting system, which is how I discovered this earlier last year as I was porting things over from a hodgepodge of random stuff to Dapr and Kubernetes. PHP is actually faster than C# at reading files, who knew?!

Does this mean you should drop everything and just rewrite all your file writing stuff in PHP (or better, C)? no. Not at all. A few milliseconds isn’t going to destroy your day, but if your bottleneck is i/o, maybe it’s worth considering :trollface:?

Nah, don’t kid yourself. But if you’re already a PHP dev, now you know that PHP is faster than C#, at least when it comes to reading files…

Feel free to peruse some experiments here (or if you want to inspect the configuration): withinboredom/racer: racing languages (github.com)

Can this C# be written to be faster, sure! Do libraries implement “the faster way?” Not usually.

Addendum

Many people have pointed out that the C# version isn’t reading it in binary mode and the function call overhead are to blame. Really? C# is many order of magnitudes faster than PHP at function calls. I promise you that isn’t the problem. Here’s the code for binary mode on the 2.5gb file:

using System.Diagnostics;
using System.Text;

var binTest = () =>
{
    using var file = File.OpenRead("/file/file.bin");
    var counter = 0;
    var buffer = new byte[4096];
    var numRead = 0;
    var sw = Stopwatch.StartNew();
    while ((numRead = file.Read(buffer, 0, buffer.Length)) != 0)
    {
        counter += buffer.Take(numRead).Count((x) => x == '1');
    }
    sw.Stop();
    Console.WriteLine($"Counted {counter:N} 1s in {sw.Elapsed.TotalMilliseconds} milliseconds");
};

binTest();

If you now want to complain that it’s all Linq’s fault, we can just remove the .Take and double count things because I need to get to work and I’m not putting any more time to telling people the sky is blue.

with .Take: 38.40s (2.5gb file)
without .Take: 23.5s (2.5gb file — but incorrect implementation)

So yeah, if an incorrect implementation is the proof you need that PHP is slower, go for it. Time to go to work.

Addendum 2

Since people come here wanting to optimize the C# without optimizing the PHP version, here is an implementation ONLY looking at file performance:

function test()
{
    $file = fopen("/file/file.bin", 'r');
    $counter = 0;
    $timer = microtime(true);
    while (stream_get_line($file, 4096) !== false) {
        ++$counter;
    }
    $timer = microtime(true) - $timer;
    fclose($file);
    printf("counted %s 1s in %s milliseconds\n", number_format($counter), number_format($timer * 1000, 4));
}
test();
var binTest = () =>
{
    using var file = File.OpenRead("/file/file.bin");
    var counter = 0;
    var buffer = new byte[4096];
    var sw = Stopwatch.StartNew();
    while (file.Read(buffer, 0, buffer.Length) != 0)
    {
        counter += 1;
    }
    sw.Stop();
    Console.WriteLine($"Counted {counter:N} 1s in {sw.Elapsed.TotalMilliseconds} milliseconds");
};

binTest();

And here are the results:

PHP: 423.50 ms (avg over 10 runs)
C#: 530.42 ms (avg over 10 runs)