Skip to content

Performance

Gwendal Roué edited this page Mar 23, 2024 · 82 revisions

Comparing the Performances of Swift SQLite libraries

Last updated March 23, 2024

Below are performance benchmarks made on for GRDB 6.26.0, FMDB 2.7.9, and SQLite.swift 0.15.0. They are compared to Core Data, Realm 10.49.1 and the raw use of the SQLite C API from Swift.

This report was generated on a MacBookPro18,1, with Xcode 15.3, by running the following command:

make test_performance | Tests/parsePerformanceTests.rb | Tests/generatePerformanceReport.rb

All tests use the default settings of each library. For each library, we:

  • Build and consume database rows with raw SQL and column indexes (aiming at the best performance)
  • Build and consume database rows with column names (sacrificing performance for maintainability)
  • Build and consume records values to and from database rows (aiming at the shortest code from database to records)
  • Build and consume records values to and from database rows, with help from the Codable standard protocol
  • Build and consume records values to and from database rows, with change tracking (records know if they have unsaved changes)

As a bottom line, the raw SQLite C API is used as efficiently as possible, without any error checking.

GRDB Raw SQLite FMDB SQLite.swift Core Data Realm
Column indexes
Fetch 0.06 0.04 0.05 0.29 ¹ ¹
Insert 0.06 0.02 0.22 0.15 ¹ ¹
Column names
Fetch 0.12 ¹ 0.23 0.76 ¹ ¹
Insert 0.12 ¹ 1.00 0.68 ¹ ¹
Records
Fetch 0.13 0.04 1.80 0.76 ¹ ¹
Insert 0.22 ¹ ¹ ¹ ¹ ¹
Codable Records
Fetch 0.23 ¹ ¹ ¹ ¹ ¹
Insert 0.27 ¹ ¹ ¹ ¹ ¹
Optimized Records
Fetch 0.07 ¹ ¹ ¹ ¹ ¹
Insert 0.06 ¹ ¹ ¹ ¹ ¹
Records with change tracking
Fetch 0.23 ¹ ¹ ¹ 0.41 0.10
Insert 0.26 ¹ ¹ ¹ 0.39 0.49

¹ Not applicable

  • Column indexes:

    • Fetch (source)

      This test fetches 200000 rows of 10 ints and extracts each int given its position in the row.

      It uses FMDB's -[FMResultSet longForColumnIndex:], GRDB's Row.value(atIndex:), and the low-level SQL API of SQLite.swift.

    • Insert (source)

      This test inserts 50000 rows of 10 ints, by setting query arguments given their position.

      It uses FMDB's -[FMDatabase executeUpdate:withArgumentsInArray:] with statement caching, GRDB's UpdateStatement.execute(arguments:Array), and the low-level SQL API of SQLite.swift.

  • Column names:

    • Fetch (source)

      This test fetches 200000 rows of 10 ints and extracts each int given its column name.

      It uses FMDB's -[FMResultSet longForColumn:], GRDB's Row.value(named:), and the high-level query builder of SQLite.swift.

    • Insert (source)

      This test inserts 50000 rows of 10 ints, by setting query arguments given their argument name.

      It uses FMDB's -[FMDatabase executeUpdate:withParameterDictionary:] with statement caching, GRDB's UpdateStatement.execute(arguments:Dictionary), and the high-level query builder of SQLite.swift.

  • Records:

    • Fetch (source)

      This test fetches an array of 200000 record objects initiated from rows of 10 ints.

      It builds records from FMDB's -[FMResultSet resultDictionary], GRDB's built-in FetchableRecord protocol, and the values returned by the high-level query builder of SQLite.swift.

    • Insert (source)

      This tests inserts 50000 records with the persistence method provided by GRDB's PersistableRecord protocol.

  • Codable Records:

  • Optimized Records:

    • Fetch (source)

      This test shows how to optimize Decodable Records for fetching.

    • Insert (source)

      This test shows how to optimize Encodable Records for batch inserts.

  • Records with change tracking:

    • Fetch (source)

      This test fetches an array of 200000 record objects initiated from rows of 10 ints.

      It builds records from FMDB's -[FMResultSet resultDictionary], GRDB's built-in Record class.

    • Insert (source)

      This tests inserts 50000 records with the persistence method provided by GRDB's Record class.

Clone this wiki locally