Live data from Hacker News

10 thousand times faster Swift

medium.com

11–20 of 37 posts

Re: 10 thousand times faster Swift

#11
post #9

Bad news: the optimizer is moving your functions outside the loop. for _ in 0.. Looking at the assembly... the call to `flatuseStruct` is moved outside the loop in Release builds. You're only measuring 1 thousand iterations of `flatuseStruct`, not 1 million. Your red flag should have been this: > One million times decoding of a small object graph took 0.35ms That's literally impossible. That's doing 2.8 billion itera…

I Updated the run bench function https://gist.github.com/mzaks/e3a2dc7ccdfc2397bc26c55eb6dc8a... the output is now: Eager run ================================= 1557 ms encode 264 ms decode 34 ms use 206 ms dealloc 504 ms decode+use+dealloc 0,38 ms direct 0,32 ms using struct ================================= Total counter1 is 8644311667000000 Total counter2 is 8644311667000000 Total counter3 is 8644311667000000 Encod…

[deleted]

Re: 10 thousand times faster Swift

#14
> ...Swift being as fast or even faster than C...

Rather unlikely. To get “faster than C” you need to hand-code in assembler, and know your target CPU really well to outsmart the compiler.

One advantage of C is that it's relatively easy to see what the CPU does when you look at the source. In Swift, this is no longer the case. So, unless you know Swift really well, being “as fast as C” doesn't come easily. One thing that helps you here is looking at your compiler's assembly output. Sadly, with Swift this is not as convenient in Xcode as it is with (Objective-)C.

Re: 10 thousand times faster Swift

#15
post #3

Use C or C++ instead?

RTFA dude. The end product was _faster_ than C.

> _faster_ than C

How do you know? There is no data on the respective machines and environments. A number from a tweet and a number from a blog post do not make such data.

Re: 10 thousand times faster Swift

#16

I might be misreading this, but that number doesn't seem possible. He says he can do 1 million decodings in 0.35ms, but that means each decoding is done in less than a third of a nanosecond , which sounds unreasonable. I'm not familiar with FlatBuffers, but surely there needs to be some sort of validation step for the data, right?

maybe he has a 10GHz CPU

Re: 10 thousand times faster Swift

#17
post #9

Bad news: the optimizer is moving your functions outside the loop. for _ in 0.. Looking at the assembly... the call to `flatuseStruct` is moved outside the loop in Release builds. You're only measuring 1 thousand iterations of `flatuseStruct`, not 1 million. Your red flag should have been this: > One million times decoding of a small object graph took 0.35ms That's literally impossible. That's doing 2.8 billion itera…

I Updated the run bench function https://gist.github.com/mzaks/e3a2dc7ccdfc2397bc26c55eb6dc8a... the output is now: Eager run ================================= 1557 ms encode 264 ms decode 34 ms use 206 ms dealloc 504 ms decode+use+dealloc 0,38 ms direct 0,32 ms using struct ================================= Total counter1 is 8644311667000000 Total counter2 is 8644311667000000 Total counter3 is 8644311667000000 Encod…

The function call is not being optimized out, it's being hoisted outside the loop. I.e., it is as if the code was written as:

    let result = flatuseStruct(outputData)
    for _ in 0..
The counter will still be correct, but you are not measuring what you think you are measuring.

Re: 10 thousand times faster Swift

#18
post #3

Earlier quoted context omitted.

RTFA dude. The end product was _faster_ than C.

> _faster_ than C How do you know? There is no data on the respective machines and environments. A number from a tweet and a number from a blog post do not make such data.

Performance test run on Travis CI in a virtual machine https://travis-ci.org/mzaks/FlatBuffersSwift

function called for decode+use+dealloc https://github.com/mzaks/FlatBuffersSwift/blob/master/FlatBu...

function called for direct: https://github.com/mzaks/FlatBuffersSwift/blob/master/FlatBu...

function called for using struct: https://github.com/mzaks/FlatBuffersSwift/blob/master/FlatBu...

Everything is on Github, you are welcome to try it out on your own machine.

Re: 10 thousand times faster Swift

#19
post #9

Bad news: the optimizer is moving your functions outside the loop. for _ in 0.. Looking at the assembly... the call to `flatuseStruct` is moved outside the loop in Release builds. You're only measuring 1 thousand iterations of `flatuseStruct`, not 1 million. Your red flag should have been this: > One million times decoding of a small object graph took 0.35ms That's literally impossible. That's doing 2.8 billion itera…

I Updated the run bench function https://gist.github.com/mzaks/e3a2dc7ccdfc2397bc26c55eb6dc8a... the output is now: Eager run ================================= 1557 ms encode 264 ms decode 34 ms use 206 ms dealloc 504 ms decode+use+dealloc 0,38 ms direct 0,32 ms using struct ================================= Total counter1 is 8644311667000000 Total counter2 is 8644311667000000 Total counter3 is 8644311667000000 Encod…

You can see the problem here:

First it cals CFAbsoluteTimeGetCurrent and saves the result.

    0x100272dab : callq  0x1002b7b38 ; CFAbsoluteTimeGetCurrent
    0x100272db0 : movapd %xmm0, -0xa0(%rbp)
Here is the call to flatDecodeDirect. I guess RDI is the input. That's usual for the x64 ABI.

    0x100272db8 : movq   -0x100(%rbp), %rdi
    0x100272dbf : callq  0x10026fb10 ; flatDecodeDirect
I don't know what this next bit is for.

    0x100272dc4 : testq  %rax, %rax
    0x100272dc7 : js     0x10027444c ;  [inlined] generic specialization  of Swift._ContiguousArrayBuffer._checkValidSubscript (Swift.Int) -> ()
0x3e8=1000. The loop counter is in ECX.

    0x100272dcd : movl   $0x3e8, %ecx
I can't figure out what's at these two addresses; lldb didn't seem to accept any reasonable syntax. lldb is terrible. But I'll bet that RBX is holding the value of `total'. I don't know what r14 is, and it doesn't seem to matter since nothing here uses it.

    0x100272dd2 : movq   -0xd8(%rbp), %rbx
    0x100272dd9 : movq   -0x198(%rbp), %r14
Here's the loop. The loop is unrolled 5 times. total+=result. 0x100274454 produces some kind of exception on integer overflow.

    0x100272de0 : addq   %rax, %rbx
    0x100272de3 : jb     0x100274454 ; at flatbench.swift:284
    0x100272de9 : addq   %rax, %rbx
    0x100272dec : jb     0x100274454
    0x100272df2 : addq   %rax, %rbx
    0x100272df5 : jb     0x100274454
    0x100272dfb : addq   %rax, %rbx
    0x100272dfe : jb     0x100274454
    0x100272e04 : addq   %rax, %rbx
    0x100272e07 : jb     0x100274454
The loop was unrolled 5 times, so drop 5 from the loop counter and repeat.

    0x100272e0d : addq   $-0x5, %rcx
    0x100272e11 : jne    0x100272de0 ; at flatbench.swift:276
Get current time.

    0x100272e13 : callq  0x1002b7b38 ; CFAbsoluteTimeGetCurrent
So this code actually times one call to flatDecodeDirect, then 200 iterations of an unrolled do-nothing loop. The compiler has figured out somehow that flatDecodeDirect is going to do exactly the same thing each time, and taken advantage of that by calling it only once. I'm guessing this means that flatDecodeDirect is only called 1,000 times in total.

As a sanity check for this kind of thing - try making a little loop that just increments an integer the appropriate number of times, and see how long that takes. (Check the assembly language output to ensure the generated code is doing what you think - it should be a 2-instruction loop.)

On my laptop that takes 1.8ms. This isn't the absolute limit of how long it takes to do 1,000,000 of anything, but it'll do as a rough estimate. So you should be suspicious if a program suggests it's taking much less time than that to do 1,000,000 of something that's a lot more complicated, as the test did. (It reported 1,000,000 iterations in 0.53ms on my PC.)

(Of course, as with any rough estimate, this only gives you a suspicion, and isn't proof without further investigation.)

Re: 10 thousand times faster Swift

#20
post #9

Bad news: the optimizer is moving your functions outside the loop. for _ in 0.. Looking at the assembly... the call to `flatuseStruct` is moved outside the loop in Release builds. You're only measuring 1 thousand iterations of `flatuseStruct`, not 1 million. Your red flag should have been this: > One million times decoding of a small object graph took 0.35ms That's literally impossible. That's doing 2.8 billion itera…

I Updated the run bench function https://gist.github.com/mzaks/e3a2dc7ccdfc2397bc26c55eb6dc8a... the output is now: Eager run ================================= 1557 ms encode 264 ms decode 34 ms use 206 ms dealloc 504 ms decode+use+dealloc 0,38 ms direct 0,32 ms using struct ================================= Total counter1 is 8644311667000000 Total counter2 is 8644311667000000 Total counter3 is 8644311667000000 Encod…

That's 0.35 nanoseconds to do something.

If it is a nop on a modern cpu it can run x4 nops at the same time. Either you aren't doing useful work or your measurements are wrong.

Unfortunately I can't view gists in work.

Post reply on HN