Rust language is being clamined to be "a system programming language that runs blazingly fast, prevents almost all crashes*, and eliminates data races".
I thought of running same fibonnaci algorithm against Rust to see how blazingly fast is it.
You can find fib.rs source code here on lang-compare on my github. I compared it against fib.go and fib.c
FIBONACCI - 50
Language C (gcc-4.9.1):
gcc-4.9 -O3 fib.c
/usr/bin/time -lp ./a.out 50
LANGUAGE C: 12586269025
real 52.87
user 52.83
sys 0.01
557056 maximum resident set size
0 average shared memory size
0 average unshared data size
0 average unshared stack size
163 page reclaims
0 page faults
0 swaps
0 block input operations
0 block output operations
0 messages sent
0 messages received
0 signals received
0 voluntary context switches
497 involuntary context switches
Language Go (go1.4beta1 darwin/amd64):
go build fib.go
/usr/bin/time -lp ./fib 50
LANGUAGE GO: 12586269025
real 96.56
user 96.47
sys 0.06
1232896 maximum resident set size
0 average shared memory size
0 average unshared data size
0 average unshared stack size
300 page reclaims
0 page faults
0 swaps
0 block input operations
0 block output operations
0 messages sent
0 messages received
0 signals received
9010 voluntary context switches
1338 involuntary context switches
Language Rust (0.13.0-nightly (45cbdec41 2014-11-07 00:02:18 +0000)):
/usr/local/bin/rustc --opt-level 3 fib.rs
/usr/bin/time -lp ./fib 50
LANGUAGE Rust: 12586269025
real 101.53
user 101.44
sys 0.01
1040384 maximum resident set size
0 average shared memory size
0 average unshared data size
0 average unshared stack size
229 page reclaims
46 page faults
0 swaps
0 block input operations
0 block output operations
0 messages sent
0 messages received
0 signals received
1 voluntary context switches
914 involuntary context switches
Surprisingly results are as below where CPU usage (real) is as C (52.87) > Go (96.56) > Rust (101.53).
gcc-4.9 -O3 fib.c
/usr/bin/time -lp ./a.out 50
LANGUAGE C: 12586269025
real 52.87
user 52.83
sys 0.01
557056 maximum resident set size
0 average shared memory size
0 average unshared data size
0 average unshared stack size
163 page reclaims
0 page faults
0 swaps
0 block input operations
0 block output operations
0 messages sent
0 messages received
0 signals received
0 voluntary context switches
497 involuntary context switches
Language Go (go1.4beta1 darwin/amd64):
go build fib.go
/usr/bin/time -lp ./fib 50
LANGUAGE GO: 12586269025
real 96.56
user 96.47
sys 0.06
1232896 maximum resident set size
0 average shared memory size
0 average unshared data size
0 average unshared stack size
300 page reclaims
0 page faults
0 swaps
0 block input operations
0 block output operations
0 messages sent
0 messages received
0 signals received
9010 voluntary context switches
1338 involuntary context switches
Language Rust (0.13.0-nightly (45cbdec41 2014-11-07 00:02:18 +0000)):
/usr/local/bin/rustc --opt-level 3 fib.rs
/usr/bin/time -lp ./fib 50
LANGUAGE Rust: 12586269025
real 101.53
user 101.44
sys 0.01
1040384 maximum resident set size
0 average shared memory size
0 average unshared data size
0 average unshared stack size
229 page reclaims
46 page faults
0 swaps
0 block input operations
0 block output operations
0 messages sent
0 messages received
0 signals received
1 voluntary context switches
914 involuntary context switches
Surprisingly results are as below where CPU usage (real) is as C (52.87) > Go (96.56) > Rust (101.53).
Language | CPU(real) | Memory(Max resident size) |
C | 52.87 | 544K |
Go | 96.56 | 1204K |
Rust | 101.53 | 1016K |
NOTE: Is there a better way to read command line argument and convert into integer? I felt it was very painful to figure that out.
E.g.
In C language:
long n = atol(argv[1]);
In Go language:
n, _ := strconv.Atoi(os.Args[1])
In Rust language:
let args = os::args(); let args = args.as_slice(); let n :u64 = from_str::<u64>(args[1].as_slice()).unwrap();
E.g.
In C language:
long n = atol(argv[1]);
In Go language:
n, _ := strconv.Atoi(os.Args[1])
In Rust language:
let args = os::args(); let args = args.as_slice(); let n :u64 = from_str::<u64>(args[1].as_slice()).unwrap();
Considering how unoptimized the code is, this says more about how smart the compiler is at optimizing then anything else, and you're comparing to a still-in-development language to a released one. Try the following rust program (not to say this is optimal, it's just less bad):
ReplyDeleteuse std::os;
// fib returns a function that returns
// successive Fibonacci numbers.
fn fib(n: u64) -> u64 {
fn f(n: u64, a: u64, b: u64) -> u64 {
if n == 0 {
a
}
else {
f(n-1, b, a+b)
}
}
f(n, 0, 1)
}
fn main() {
let args = os::args();
let args = args.as_slice();
let n :u64 = from_str::(args[1].as_slice()).unwrap();
let result = fib(n);
println!("LANGUAGE Rust: {0}", result);
}
First off, optimized Rust isn't slower than Go. There should basically be no time when it is.
ReplyDeleteSecondly, while I don't necessarily want to say don't use recursion, you definitely don't want to use u64 if you want your algorithm to run quickly. Use u32.
NOTE: Is there a better way to read command line argument and convert into integer?
ReplyDeleteI submitted a pull request to your repository with a shorter version.
I think if you handle the cases of there not being enough arguments passed, and the argument not being a number, you will not find the rust version more verbose
Thanks for the optimized version of the algorithm.
ReplyDeleteI want to compare similarly implemented code in different language rather than language specific optimization. So I intentionally wrote the code this way. I could have compared with C++ const_expression/compile time computation.
http://cpptruths.blogspot.com/2011/07/want-speed-use-constexpr-meta.html
You need more knowledge about the languages to be able to write such a benchmark fairly. I don't like the Computer Language Benchmarks Game very much, but it does at least ensure that the same algorithm is used across languages. It makes it pretty clear that Rust is on par with C++ and Go is slower. This is also clear if you examine how the languages are implemented internally.
ReplyDeleteI'm not really arguing that there aren't certain algorithms that naively might be faster when written in Go than Rust, more that sensationalist titles like "Rust slower than Go?" are misleading and the conclusions are factually inaccurate.
How does Delphi XE7 compare?
ReplyDeleteHere's a free pascal implementation:
ReplyDeleteprogram fib;
uses
Math,
SysUtils;
const
SQRT5 = Sqrt(5);
function fib(const n : Double) : Double;
begin
fib := (Power((1 + SQRT5), n) -
Power((1 - SQRT5), n)) /
(SQRT5 * Power(2, n));
end;
var
iParam : Integer;
begin
iParam := Max(StrToIntDef(ParamStr(1), 0), 0);
writeln('LANGUAGE PASCAL: ' + FloatToStr(fib(iParam)));
end.
Compile and run:
$ fpc -ofib-pas -XXs -O3 fib.pas
Free Pascal Compiler version 2.6.2 [2013/08/08] for x86_64
Copyright (c) 1993-2012 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling fib.pas
Linking fib-pas
/usr/bin/ld: warning: link.res contains output sections; did you forget -T?
23 lines compiled, 0.3 sec
$ time ./fib-pas 50
LANGUAGE PASCAL: 12586269025
real 0m0.001s
user 0m0.001s
sys 0m0.000s
Yes, I'm cheating by using a different algorithm. ;-) Using the same recursive algorithm was slower with free pascal than Java and C++. I'd expect Delphi to be around about the same speed as Java and C++.