Introducing swift-bson, a high-performance binary serialization library for memory-constrained environments

hi Swifties,

i’m pleased to announce that swift-bson has reached its first major release, 1.0.0.


The swift-bson library offers enormous performance gains over standard JSON and even the default MongoKitten BSON decoder. You can learn more about BSON and the swift-bson library by reading the project’s README, and get started by following the reference guide.

You can browse its full API documentation on Swiftinit.

The swift-bson library requires Swift 6.0, and supports Linux and all Darwin platforms. Its build matrix is available on Swift Package Index.

If you run into any problems, please don’t hesitate to reply to this thread, or open an issue on GitHub.

16 Likes

Very nice!

Curious if you did any side by side benchmarks with other bson libs? (Some Java one maybe?)

4 Likes

thanks!

this is a complex question to answer, because the amount of memory/CPU cycles you spend decoding BSON (or any serialization) format depends strongly on your decoding strategy, which is difficult to compare across languages because they have different ideas about Codable, Serde, etc.

for example, it’s more “efficient” to decode Object-like structures by iterating and assigning like this (pseudocode):

var field1:Field1?
var field2:Field2?
var field3:Field3?

for field in document
{
    switch field.key
    {
    case "field1": field1 = try field.decode()
    ...
    }
}

guard 
let field1:Field1,
let field2:Field2,
let field3:Field3
else
...

but this doesn’t mesh well with Swift’s ideas about uninitialized state, and doing this in a way that respects initialization rules doesn’t scale very well in a real code base because you would have to write out a bespoke decoding function for every model type.¹ so instead, when going through the BSONDocumentDecodable path, it constructs a Dictionary of the fields for you and provides that as a more developer-friendly API, even though it costs a dictionary allocation.

(this can be avoided by dropping down to the BSONKeyspaceDecodable level, but then you would have to write out all that decoding and error-handling logic.)

libraries like libbson basically don’t bother trying to solve the last-mile decoding problem, so they just dump the complexity on the users, who can then choose any point on the effort–performance curve they like.

what i tried to do with swift-bson is make the “performant” (non-allocating) patterns easier to apply in a real codebase, to try and shift that effort–performance curve to the left. but ultimately, it is challenging to formulate a fair comparison unless both systems share similar ideas about Codable.


[1] One day when Swift Macros have matured and can view definitions of types defined in other modules, and can run without building SwiftSyntax from source, we might be able to generate all this code at build time…
1 Like

Does this use Codable at all?

there is an overlay in the BSONLegacy module that extends BSON.AnyValue to conform to Swift.Decoder, which allows it to be used with Codable types, although the benchmarks are written against the BSONDecodable system.

Swift.Encoder support isn’t implemented yet.

Have you done an apples-to-apples comparison by excluding the Codable layer of my library?

i wasn’t aware of a way to decode structures with your implementation without going through Codable. do you have an example i can refer to?

The non-codable API is pretty simple to use, it works like a mix of Dictionary/Array (as expected of a Document).

I've also found some huge performance bottlenecks in my BSON library thanks to you. I've improved it about 6-7x.