Earlier quoted context omitted.
Different languages' benchmarks might not be equally well-written / optimized. In particular, I'd expect C and Rust to be very close to each other, and a 20% gap between them is a red flag. Rules like "code should be simple, as in, easy to read and understand" are also hard to judge, especially near the top of the list where there's a lot of pressure to optimize. Is SIMD easy to understand? What if it's in a library?…
Not necessarily. Because C allows the pointer manipulation, the compiler can in general not make assumptions about pointer aliasing. This prevents some optimizations. In Rust, the compiler has more information/control over memory layout/lifetime and can therefore make stronger optimizations. Automatic vectorization is an area where this helps a lot, and raytracing can benefit a lot here. 20% sounds reasonable to me.
Programming language ray tracing benchmarks project
51–60 of 90 posts
Re: Programming language ray tracing benchmarks project
#52Earlier quoted context omitted.
That's a big jump between OCaml and Go. I'm not familiar with ray tracing, but skimming the source code it mostly looks like it's doing floating point math; it doesn't look like it's using the runtime (no allocations, no virtual function calls, no scheduling, etc), so I'm surprised that Go is performing relatively poorly. I wonder if the performance gap is attributable to some overhead in Go's function calls? I know…
There's a variety of possibilities. Lerc mentions GC as one possibility, which could definitely be the case. Another one that would be high on my "first guess" list is that everything above it has much better optimizers, and raytracing code is one of the places this is really going to show. Go does basically very little optimization, because it prioritizes fast compilation. (Where Go "wants" to play is that same benc…
Also, as I edited, I updated the Go version to pass by reference and that put it on par with C (and also per my update, I may have mistranslated somehow).
Re: Programming language ray tracing benchmarks project
#53Earlier quoted context omitted.
Julia was astonishing. It's a high level language that's performing almost like C. Last time I checked, many years back, the spec was changing and the run time did crash. Guess it has gone a long way since. The other one is Lua. My assumption was that it's one of the lightest and fastest language around. Looks like "fastest" isn't true in some cases.
shouldn't rust being faster than C be something of a red flag that they aren't quite the same algorithm? Or that the algorithm is sub-optimal?
Re: Programming language ray tracing benchmarks project
#54Ordered by realtime, fastest to slowest for those like me who got annoyed by the scrolling up and down trying to compare: Rust (1.13.0-nightly) 1m32.392s Nim (0.14.2) 1m53.320s C 1m59.116s Julia (0.4.6) 2m01.166s Crystal (0.18.7) 2m01.735s C Double Precision 2m26.546s Java (1.7.0_111) 2m36.949s Nim Double Precision (0.14.2) 3m19.547s OCaml 3m59.597s Go 1.6 6m44.151s node.js (6.2.1) 7m59.041s node.js (5.7.1) 8m49.170s…
That's a big jump between OCaml and Go. I'm not familiar with ray tracing, but skimming the source code it mostly looks like it's doing floating point math; it doesn't look like it's using the runtime (no allocations, no virtual function calls, no scheduling, etc), so I'm surprised that Go is performing relatively poorly. I wonder if the performance gap is attributable to some overhead in Go's function calls? I know…
EDIT: Look at C code assembly, it's generating mostly SIMD instructions and using xmm registers. That's why it's faster. Golang compiler still do not have autovectorization implemented that's why it's so much slower in this case.
EDIT2: It seems Go version also uses SSE here, which is nice. So probably unnecessary allocation from my original post was the reason.
Re: Programming language ray tracing benchmarks project
#55Earlier quoted context omitted.
I'm not familiar with go but I seem to recall it is garbage collected. If so it may be something to do with the creation of new vectors on the heap instead of the stack. The compiler would have to determine the full lifetime of the vector value to be able to bump it to the stack. That's an optimization, and sometimes it's just not possible (but probably is here). In the C instance no such optimization is necessary. T…
Is it creating vectors in the hot path? I'm not seeing it. Go does some escape analysis (the optimization you're referring to), but it's pretty conservative.
func v3_add (a, b v3) v3 {
return v3{x: a.x + b.x, y: a.y + b.y, z: a.z + b.z}
}
A sensible approach for maintainable code but without knowing if a and b are used elsewhere the compiler can't reuse the space they occupy. If C can't figure it out, it can just stick a new one on the stack which doesn't cost too much.This is, of course, Rust's bread and butter which is probably why it takes the top spot.
Re: Programming language ray tracing benchmarks project
#56Earlier quoted context omitted.
That's a big jump between OCaml and Go. I'm not familiar with ray tracing, but skimming the source code it mostly looks like it's doing floating point math; it doesn't look like it's using the runtime (no allocations, no virtual function calls, no scheduling, etc), so I'm surprised that Go is performing relatively poorly. I wonder if the performance gap is attributable to some overhead in Go's function calls? I know…
Probably because Go version is different, compare C vector operations with Go vector operations. C version is operating on pointer without allocating new vector, Go version is allocation new vector on every op. EDIT: Look at C code assembly, it's generating mostly SIMD instructions and using xmm registers. That's why it's faster. Golang compiler still do not have autovectorization implemented that's why it's so much…
Re: Programming language ray tracing benchmarks project
#57Earlier quoted context omitted.
Is it creating vectors in the hot path? I'm not seeing it. Go does some escape analysis (the optimization you're referring to), but it's pretty conservative.
All of the vector operations return new vectors instead of mutating. func v3_add (a, b v3) v3 { return v3{x: a.x + b.x, y: a.y + b.y, z: a.z + b.z} } A sensible approach for maintainable code but without knowing if a and b are used elsewhere the compiler can't reuse the space they occupy. If C can't figure it out, it can just stick a new one on the stack which doesn't cost too much. This is, of course, Rust's bread a…
Re: Programming language ray tracing benchmarks project
#58The haskell version can be made >= 3x faster by making the computations non-lazy, e.g. -data Vector3 = Vector3 {vx::Float, vy::Float, vz::Float} deriving (Show) +data Vector3 = Vector3 {vx :: !Float, vy :: !Float, vz :: !Float} deriving (Show)
Re: Programming language ray tracing benchmarks project
#59I don't think the C# time is representative. I suspect Mono is really slow here. I just ran it with VS 2015 in 1 min 24 sec.
The C# implementation looks flawed (uses reference types for vectors etc). Using value types and .NET Core should give a much better result than that. Will try to remember doing a PR.
Baseline of the non-multithreaded variant on my machine: 1m56s
Making Vector3 a struct: 1m3s
Making Vector3 a readonly struct: 1m1s
Making Hit and Ray a struct: 1m26s
Will test more tomorrow, I guess, but the most obvious change already yields a 2× speedup. This was also without any profiling, so I don't even know what I did there.
Re: Programming language ray tracing benchmarks project
#60This is awesome! More good press for Nim. There is a big variation in performance, some of which I find surprising. Do you know what exactly causes some languages to be so slow (e.g., small objects being created and garbage collected frequently)?
EDIT: forgot to mention the obvious things: compiler/interpreter maturity and inherent overhead.