bson

Tests Documentation

swift-bson 库是一个可移植的、不依赖 Foundation 的库,用于处理 BSON

文档 · 许可证

要求

swift-bson 库需要 Swift 6.0 或更高版本。

平台 状态
🐧 Linux Tests
🍏 Darwin Tests
🍏 Darwin (iOS) iOS
🍏 Darwin (tvOS) tvOS
🍏 Darwin (visionOS) visionOS
🍏 Darwin (watchOS) watchOS

检查部署最低要求

什么是 BSON?

BSON 是一种通用的二进制序列化格式,是 JSON 的超集。 解析 BSON 比解析 JSON 需要更少的内存,并且该格式是可遍历的,这使得可以提取嵌套在 BSON 文档深处的单个字段,而无需实际解析整个文件。

BSON 最初由 MongoDB 开发,它也是 MongoDB 的原生数据格式。 但是,文件格式本身并不与 MongoDB 绑定,可以在任何需要高性能、低内存序列化格式的系统中使用。

我为什么需要这个库?

如果您正在使用 MongoKitten,您的 MongoDB 驱动程序已经包含一个基于标准库的 Codable 系统的 BSON 解析器,该解析器的优点是可以自动生成大部分反序列化代码。 但是,Codable 具有众所周知的性能限制,不适用于高吞吐量用例。

使用此库的另一个原因是它具有可移植性并且依赖性很少。 MongoDB 驱动程序提供的 BSON 解析器依赖于诸如 ByteBuffer 之类的网络原语,这需要您链接 SwiftNIO 库。 对于仅将 BSON 用作存储格式的应用程序,这可能不是理想的。

它比 Codable 更快吗?

该解码器比默认的 MongoKitten 解码器快大约 3 到 6 倍。 编码器的吞吐量与 Codable 相似。

(基准测试源代码)

性能对比
Host 'vscode' with 12 'x86_64' processors with 30 GB memory, running:
#202408030740 SMP PREEMPT_DYNAMIC Sat Aug  3 07:53:03 UTC 2024

====================
VsMongoKittenDefault
====================

Decode BSON with MongoKitten Default
╒═════════════════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                              │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═════════════════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ (Alloc + Retain) - Release Δ *      │    1476 │    1500 │    1506 │    1512 │    1520 │    1529 │    1536 │     376 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Object allocs *                     │    2334 │    2459 │    2493 │    2521 │    2545 │    2605 │    2634 │     376 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Releases (K) *                      │      21 │      21 │      22 │      22 │      22 │      22 │      22 │     376 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Retains (K) *                       │      17 │      18 │      18 │      18 │      18 │      18 │      18 │     376 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (#)              │     617 │     592 │     583 │     572 │     562 │     415 │     234 │     376 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (wall clock) (μs) *            │    1620 │    1690 │    1714 │    1747 │    1781 │    2408 │    4276 │     376 │
╘═════════════════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

Decode BSON with This Library
╒═════════════════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                              │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═════════════════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ (Alloc + Retain) - Release Δ *      │    7917 │    8035 │    8071 │    8099 │    8123 │    8167 │    8187 │    1000 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Object allocs *                     │     892 │     969 │     984 │     997 │    1011 │    1034 │    1061 │    1000 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Releases (K) *                      │      13 │      14 │      14 │      14 │      14 │      14 │      14 │    1000 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Retains *                           │    4406 │    4523 │    4555 │    4583 │    4607 │    4643 │    4697 │    1000 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (#)              │    1915 │    1820 │    1791 │    1748 │    1669 │    1175 │    1061 │    1000 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (wall clock) (μs) *            │     522 │     549 │     559 │     572 │     599 │     810 │     943 │    1000 │
╘═════════════════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

Encode BSON with MongoKitten Default
╒═════════════════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                              │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═════════════════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ (Alloc + Retain) - Release Δ *      │    5054 │    5247 │    5307 │    5379 │    5427 │    5519 │    5598 │     513 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Object allocs *                     │    2993 │    3113 │    3153 │    3199 │    3229 │    3279 │    3323 │     513 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Releases (K) *                      │      39 │      41 │      41 │      42 │      42 │      43 │      44 │     513 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Retains (K) *                       │      31 │      33 │      33 │      33 │      34 │      34 │      35 │     513 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (#)              │     198 │     189 │     185 │     182 │     177 │     162 │     132 │     513 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (wall clock) (μs) *            │    5039 │    5300 │    5394 │    5501 │    5661 │    6181 │    7598 │     513 │
╘═════════════════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

Encode BSON with This Library
╒═════════════════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                              │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═════════════════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ (Alloc + Retain) - Release Δ *      │    5066 │    5251 │    5311 │    5375 │    5427 │    5531 │    5614 │     514 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Object allocs *                     │    3003 │    3123 │    3153 │    3199 │    3229 │    3293 │    3353 │     514 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Releases (K) *                      │      39 │      41 │      41 │      42 │      42 │      43 │      44 │     514 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Retains (K) *                       │      31 │      33 │      33 │      33 │      34 │      34 │      35 │     514 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (#)              │     198 │     189 │     186 │     183 │     178 │     159 │      95 │     514 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (wall clock) (μs) *            │    5061 │    5280 │    5366 │    5464 │    5612 │    6275 │   10509 │     514 │
╘═════════════════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

我真的应该使用 BSON 吗?

BSON 并不适合所有人。 下面的理由不是采用 BSON 的好理由,至少单独来说不是。

节省磁盘空间

BSON 会在解析时节省内存,但在典型用例中,BSON 文件将占用与等效 JSON 文件相似的空间量,并提供相似的压缩率。

为 Web 提供服务

BSON 通常被认为是服务器端格式,并且几乎没有令人信服的理由仅仅为了向浏览器提供内容而合成它。

也就是说,存在用于解析 BSON 的 JavaScript 库,因此可以在客户端使用它。 这样做的一个好理由是,如果您将 BSON 对象存储为可从 CDN 访问的静态资源,并且希望客户端能够从 CDN 下载 BSON,而不是通过您的 HTTP 服务器动态将其转换为 JSON。

值得付出努力吗?

学习此库将使您能够在各种平台上使用高性能的二进制序列化格式。 该库很小,用纯 Swift 编写,并围绕一些关键模式组织,这些模式强调大型代码库中的可维护性。

尽管 swift-bson 不能为您合成序列化代码,但它的习惯用法是可预测的,并且可以很容易地被 GitHub Copilot 等 LLM“绘制”。

代码是什么样的?

在“真实”的代码库中,BSON 模型类型看起来像这样

struct ExampleModel:BSONDocumentEncodable, BSONDocumentDecodable
{
    let id:Int64
    let name:String?
    let rank:Rank

    /// A custom enum type.
    enum Rank:Int32, BSONEncodable, BSONDecodable
    {
        case newModel
        case risingStar
        case aspiringModel
        case fashionista
        case glamourista
        case fashionMaven
        case runwayQueen
        case trendSetter
        case runwayDiva
        case topModel
    }

    /// The schema definition.
    enum CodingKey:String, Sendable
    {
        case id = "_id" // Chosen for compatibility with MongoDB
        case name = "D"
        case rank = "R"
    }

    /// The serialization logic.
    func encode(to bson:inout BSON.DocumentEncoder<CodingKey>)
    {
        bson[.id] = self.id
        bson[.name] = self.name
        bson[.rank] = self.rank == .newModel ? nil : self.rank
    }

    /// The deserialization logic.
    init(bson:BSON.DocumentDecoder<CodingKey>) throws
    {
        self.id = try bson[.id].decode()
        self.name = try bson[.name]?.decode()
        self.rank = try bson[.rank]?.decode() ?? .newModel
    }
}

实际将其与原始数据之间进行往返的代码看起来像这样

let models:[ExampleModel] = [
    .init(id: 0, name: "Gigi", rank: .topModel),
    .init(id: 1, name: nil, rank: .newModel),
]

/// Round-trip one model
let document:BSON.Document = .init(encoding: models[0])
let _:ArraySlice<UInt8> = document.bytes
let model:ExampleModel = try .init(bson: document)

/// Round-trip a list of models
let list:BSON.List = .init(elements: models)
let _:ArraySlice<UInt8> = list.bytes
let array:[ExampleModel] = try .init(bson: list)

教程

许可证

swift-bson 库采用 Apache 2.0 许可证。