Flatbuffers: [Swift] Optimize the implementation

Created on 10 Mar 2020  路  12Comments  路  Source: google/flatbuffers

The current library runs a bit slow compared to other programming languages, what we can do is see how the following would allow the swift implementation to improve
Flatbuffers 1.13

Most helpful comment

when the following #6326 lands for swift the implementation will be faster in terms of struct. which was reduced from 1.8 seconds to 0.72 seconds serializing 3 million tables with a vector of 5 structs in each one of them.

|   Name        |   Scores      |
-----------------------------------------
|   500_000     |   0.070299s   | 
-----------------------------------------
|   10 str      |   0.000212s   | 
-----------------------------------------
|   3M strc     |   0.729939s   | 
-----------------------------------------
|   100 str     |   0.000256s   | 
-----------------------------------------

All 12 comments

first PR to address this is #5835

second PR to address this is #5888

current: 
writing 500_000 objects is 123 ms
----------------------------------
improve reallocation function: 
writing 500_000 objects is 112 ms
----------------------------------

If we remove Generics from the swift implementation we get a speed up that would reach to 95ms instead of the current 110ms

@aardappel, @mzaks I've been doing a lot of tests for swift implementation of FlatBuffers. regarding how swift uses generics and how it performs without it.

As you can see in the first screenshot, the generics would be calling FlatBuffersBuilder.add in 795ms
Screen Shot 2020-06-11 at 8 20 26 PM

however in the following screenshot we can see that FlatBuffersBuilder.add is called in 583ms
Screen Shot 2020-06-11 at 8 32 17 PM

Knowing that swift compilation would eventually create them by itself, but still would make a request to the witness table thus the 10ms increase. So the question now is. should we remove generic functions like:

func add<T: Scaler>(v: T, def: T, off: VOffsets>{}

in favor of the following

func add(v: UInt32, def: UInt32, off: VOffsets> {}
func add(v: Int32, def: Int32, off: VOffsets> {}

fyi, cpp runs with the same benchmark for 64ms with heavy optimization

I'd say if that's code you have to write just once, and the codebloat is not too crazy, yes. Many language implementations have these functions duplicated.

But wow, 10x slower than C++? That's surprising to me for a language that also goes thru LLVM.

@aardappel
I mean the code is going to be a bit bloated since the function add contains the following code

mutating public func add(element: Int, def: Int, at position: VOffset) {
   if element == def {
       track(offset: 0, at: position)
       return
    }
    let off = push(element: element)
    track(offset: off, at: position)
}

we can also write the if statement in one line but that's not swift standards, so I would like to avoid it.
but other than that, it would be an add function for each scaler we have:

mutating public func add(element: Int64, def: Int64, at position: VOffset) {
   if element == def { track(offset: 0, at: position); return }
    track(offset: push(element: element), at: position)
}
mutating public func add(element: Int32, def: Int32, at position: VOffset) {
   if element == def { track(offset: 0, at: position); return }
    track(offset: push(element: element), at: position)
}

I think swift would improve to be around 95ms, which if I remember correctly is similar to csharp if not a bit better. however it might never reach the cpp performance that runs the same benchmark with 64ms. Plus swift has automatic garbage collection which might be the reason swift is 10x slower than cpp

track(offset: element == def ? 0 : push(element: element), at: position)
or whatever the Swift equivalent syntax is :)

Why does it still need to call track btw? in C++ and many other languages its just if (element == def) return.

Yep, that was still based on the csharp implementation where we had to fill in an array

@aardappel I just downloaded Xcode 12, and was following WWDC, and thought of re-benchmarking the swift library again without changes. the results were quite surprising tbh.

current compiled with swift 5.2:
average of 95 ms which was compared to go/CSharp

current compiled with swift 5.3 
average of 77 ms which makes it lay between cpp and the other languages

Ooh, nice.. that's some progress :)

when the following #6326 lands for swift the implementation will be faster in terms of struct. which was reduced from 1.8 seconds to 0.72 seconds serializing 3 million tables with a vector of 5 structs in each one of them.

|   Name        |   Scores      |
-----------------------------------------
|   500_000     |   0.070299s   | 
-----------------------------------------
|   10 str      |   0.000212s   | 
-----------------------------------------
|   3M strc     |   0.729939s   | 
-----------------------------------------
|   100 str     |   0.000256s   | 
-----------------------------------------
Was this page helpful?
0 / 5 - 0 ratings

Related issues

dakom picture dakom  路  8Comments

dleslie picture dleslie  路  9Comments

rw picture rw  路  3Comments

NN--- picture NN---  路  6Comments

tymcauley picture tymcauley  路  3Comments