3 ms·
@fallat: I started to take a look at your problem, but I wasn't able to run the code that you posted to test. For the C code, the "#include" statements at the
by nkurz 7y ago
@fallat:
I started to take a look at your problem, but I wasn't able to run the code that you posted to test.
For the C code, the "#include" statements at the top are missing their <header.h>. Easy to solve now that you posted the link to the original, but maybe you could fix?
For the assembly, you are relying on "list.macro", which I couldn't find. Could you post this, or even better link to a repository that can be used?
Separately, it would probably be helpful to post the exact model number of your processor. Speed doesn't (shouldn't?) really matter, but the "generation" is essential.
Also, I don't think you mention the number of iterations you are running for your timing. [I see now in the assembly that you seem to be using 5000000?]
Post a bit more info, and someone here (maybe me, maybe not) will figure it what's causing the slowdown.
- fallat 7y agoOh whoops, I'll add it in. EDIT: It's a damn blogger encoding error. Yep, it's 5 million iterations. I will update the post with list.macros immediately! This is my CPU: Intel(R) Pentium(R) CPU N3700 @ 1.60GHz Edit: it is updated. :) list.macros has been inlined in the snippet.
- nkurz 7y agoGreat, I'll take a look in a bit, although it might take me until tomorrow to have time to do much with it. In the meantime, I'll mention that my first quick discovery is that clang seems to be significantly faster than gcc on the standard C code. The ratio changes with different versions and compilation options, but on Skylake with "-Ofast -march=native" I find clang-6.0 to be almost twice as fast as gcc-8. So if you have clang installed, check and see if it might be a better baseline. Also, what system are you running? This shouldn't make a difference with execution speed, but will make it easier to make tool suggestions. If you are running some sort of Linux, now would be a good time to get familiar with 'perf record'! Edit: > Intel(R) Pentium(R) CPU N3700 @ 1.60GHz Hmm, that's a "Braswell" part, which unfortunately isn't covered in Agner's standard guide to instruction timings (https://www.agner.org/optimize/microarchitecture.pdf https://www.agner.org/optimize/microarchitecture.pdf) and I'm not familiar with it's characteristics. This might make profiling a little more approximate.
- fallat 7y agoDebian buster; and if you're going to make a comparison, I'm just saying this just in case, you must force it to only use SSE3 and no later. :) I added the build instruction for the assembly but I'll add it here too: gcc nbodies.s -no-pie -o nbodies.out Shoot me an email if you get around to it! :D I'll check out perf record.
- nkurz 7y agoOK, I'm now able to test your code! The preliminary results I get disagree with what you are seeing. I'm not sure if this is my error, your error, or just genuine differences between processors. Specifically, on Skylake, I get your assembly to be much faster than GCC, although slightly slower than Clang. And that's without trying to use options to limit the compiler: gcc-8 -Ofast -march=native bodies.c -Wall -lm -o bodies_gcc_Ofast_native_c perf stat bodies_gcc_Ofast_native_c 5000000 2,031,645,589 cycles # 3.691 GHz 2,181,200,813 instructions # 1.07 insns per cycle gcc bodies.S -o bodies_S perf stat bodies_S 1,293,433,853 cycles # 3.691 GHz 2,641,011,827 instructions # 2.04 insns per cycle clang-6.0 -Ofast -march=native bodies.c -Wall -lm -o bodies_clang_Ofast_native_c perf stat bodies_clang_Ofast_native_c 5000000 1,158,569,067 cycles # 3.691 GHz 2,331,116,659 instructions # 2.01 insns per cycle Not having understood the assembly yet, my guess from skimming is that the compiler isn't able to vectorize this code well, and thus the SSE/AVX distinction isn't going to matter much. "-Ofast" should be comparable to "-O3" here, although I don't recall the exact differences. I didn't use "-no-pie" with your assembly, but don't think this matters. Are you able to do a similar comparison and report results with "perf stat"? Cycles and "instructions per cycle" are going to be better metrics to compare than clock time. No hurry. I'm East Coast US, and done for the night. Edit: A quick glance at "perf record" and "perf report" suggests that clang is slightly faster than you (on Skylake, using these options) because it's making use of fused multiply-adds. Which slightly contradicts what I said about the SSE/AVX distinction not mattering, although it's only a minor effect. For both routines, the majority of the time spent is in the chain starting with the division. I'm wondering if there is some major difference in architecture with Braswell --- perhaps it only has a single floating point multiplication unit or something? Or one of the operations is relative _much_ slower. Edit 2: Looking at https://en.wikichip.org/wiki/intel/cores/braswell https://en.wikichip.org/wiki/intel/cores/braswell, I now see that Braswell is the name of the "system on a chip", and the CPU microarchitecture is Airmont. This isn't in Agner either, but at least I've heard of it! https://en.wikichip.org/wiki/intel/microarchitectures/airmont https://en.wikichip.org/wiki/intel/microarchitectures/airmon... says Airmont is basically the same as Silvermont, which Agner does cover, so we should be able to figure out timings. See page 320 here: https://www.agner.org/optimize/instruction_tables.pdf https://www.agner.org/optimize/instruction_tables.pdf. Edit 3: I hadn't actually looked at your code yet. So you are trying to vectorize, it's just that you are limited in doing so because you can only do 2 doubles at a time. But since you are working in 3D, this doesn't fit evenly, so you do [x,y] then [z,null]. Thus you expected it to be something like 1.5x faster on your processor, and instead it comes out 2x slower. I think the issue might be that your processor doesn't really have full vector units --- instead it's doing some sort of emulation. Look closely at the timings on Page 338 of the instruction table I linked in Edit 2. Note that DIVPD takes just about twice as long as DIVSD. Then check Skylake on Page 252 -- same time for packed and single XMM. While this isn't the full answer, I think it's a strong hint at the issue. The quickest fix (if you actually want to make this faster on your processor) is to use the single instruction for the [z,null] case. This isn't going to help much overall, since it takes twice as long for the packed, but it might at least get them back to parity with the compiler! If you actually want higher speed from your vectorization, you may have to switch to a processor that has better vectorized speeds.