In my benchmark, I found Rust slower than C

Hey guys, I did some tests between C and Rust and the results are quite different, Rust is slower than C. For C I got .000002 and for Rust I got .013221 . Unless if I screwed something over its simply so much faster. To compile Rust and C, for Rust I compiled using cargo build --release and for C I used gcc main.c -o rust. I also tested it with clang main.c -o rust and the results were similar.

Keep in mind that the executable using the two languages are named rust because there is a shell script (called benchmark ) that performs the benchmark takes the rust executable name as an argument in order to perform the test. The test was tested within VirtualBox using ArchLinux.

Here is the code for benchmark shellscript:

#!/bin/bash

iterations=1000
sum=0
for i in $(seq 1 $iterations); do 
  new=$(strace -c ./rust 2>&1| tail -n1 | awk -c '{print $2}')
  sum=$(echo "scale=6; $sum + $new" | bc -q)
done
avg=$(echo "scale=6; $sum / $iterations" | bc -q)
echo "$avg"

rust:

fn main()
{
    let mut a: u64 = 0;
    let mut b: u64 = 2;
    let mut c: u64 = 5;

    let divide: u64 = 2;

    while a < 10000
    {
        a += 1;
        
        b += a;
        b /= divide;
        
        c += a;
        c /= divide;

        println!("{}, {}, {}", a, b, c);

        if a % 5 == 0
        {
            b += 1;
            c += 2;
        }
    }
}

and C:

int main()
{
	unsigned long long a = 0;
	unsigned long long b = 2;
	unsigned long long c = 5;

	const unsigned long long divide = 2;

	while (a < 10000)
	{
		a += 1;

		b += a;
		b /= divide;

		c += a;
		c /= divide;

		printf("%llu, %llu, %llu\n", a, b, c);
		
		if (a % 5 == 0)
		{
			b += 1;
			c += 2;
		}
	}
}

I am honestly not sure if I made a mistake somewhere or something but I have no idea why C is performing much better than Rust. Can anyone advise me on this?

General advice: Mistrust your benchmark skript, look into hyperfine or something.

Specific advice: Artificial benchmarks are just that, and benchmarking is hard. You're basically measuring the performance of printf versus println!. The latter is known to be slow by design (needs to take a global lock on every invocation). There are ways to remedy that (I don't know them, but they should be goggleable), but the benchmark performance will still be dominated by stdio, and it's not clear that this is what you want.

11 Likes

printf and println! don't do the same thing. To be closer to the C it should be something like :

use std::io::Write;
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
while condition {
    ...
    writeln!(stdout, "{}" x);
    ...
}

Though, printf is underspecified, so it's not quite possible to know what it actually does.

And, the std::fmt design is pretty complex compared to printf. And strace measures system calls not wall clock time. You should use something like hyperfine.

9 Likes

When optimized Rust code is slower than unoptimized C code, that is extremely suspicious. Did you look at where the two programs spend most of their time? (It might well be a difference in I/O as others suggested, but it's usually not fruitful to try and speculate about performance.)

3 Likes

This isn't measuring the runtime of the program, it's only measuring system calls. The C program buffers all the output and performs only a few system calls.

11 Likes

Using cargo build --release and gcc -O3 with hyperfine and your original code, I get:

'./a.out' ran
    2.08 ± 0.15 times faster than 'target/release/bm'

Changing the Rust to lock stdout first shaved a small amount of time off:

'./a.out' ran
    1.97 ± 0.11 times faster than 'target/release/bm'

Using buffered files instead (fopen() in C, File::create() and BufWriter in Rust):

'target/release/bm' ran
    1.35 ± 0.56 times faster than './a.out'

So as suggested above, you’re mainly seeing the differences between how your libc and Rust’s stdlib handle stdout, mostly due to buffering, thread-safety and the resulting number of syscalls.

6 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.