Database ACID Persist Write one Write trans Write batch Read one Read all
internal SQLite3 file Yes Yes 8 15k 16k 10k 170k
internal SQLite3 mem No No 30k 35k 45k 44k 170k
TObjectList static No Yes 97k 145k 147k 100k 234k
TObjectList virtual No Yes 97k 145k 147k 99k 234k
external SQLite3 file Yes Yes 8 13k 15k 45k 160k
external SQLite3 mem No No 26k 32k 40k 47k 160k
external Oracle Yes Yes 460 715 53k 800 72k
external Jet No Yes 688 900 900 1000 76k

Due to its ACID implementation, SQLite3 process on file waits for the hard-disk to have finished flushing its data, therefore it is the reason why it is so slow (less than 10 objects per second) outside the scope of a transaction. So if you want to reach the best writing performance in your application with the default engine, you should better use transactions and regroup all writing into services or a BATCH process. Another possibility could be to execute DB.Synchronous := smOff at SQLite3 engine level before process: in case of power loss at wrong time it may corrupt the database file, but it will increase the rate up to 400 objects per second.

Therefore, the typical use may be the following:

Database Created by Use
int. SQLite3 file default General safe data handling
int. SQLite3 mem :memory: Fast data handling with no persistence (e.g. for testing)
TObjectList static StaticDataCreate Best possible performance for small amount of data, without ACID nor SQL
TObjectList virtual VirtualTableRegister Best possible performance for small amount of data, if ACID is not required nor complex SQL (since no index is available but for IDs, search may be slow)
ext. SQLite3 file VirtualTableExternalRegister External back-end, e.g. for disk spanning
ext. SQLite3 mem VirtualTableExternalRegister Fast external back-end (e.g. for testing)
ext. Oracle VirtualTableExternalRegister Fast, secure and industry standard; can be shared outside mORMot
ext. Jet VirtualTableExternalRegister Could be used as a data exchange format (e.g. with Office applications)

In the above list, the MS SQL Server is not integrated, but may be used instead of Oracle (minus the fact that BULK insert is not implemented yet for it, whereas array binding boosts Oracle writing BATCH process performance by 100 times).

Benchmark was run on a good old Core 2 Duo workstation (no SSD), with anti-virus and background applications, over a 100 Mb corporate network, linked to a shared Oracle 11g database.
So it was a development environment, very similar to production site, not dedicated to give best performance.
As a result, rates and timing may vary depending on network and server load, but you get results similar to what could be expected on customer side.

Results were created with the same software sample as used for Oracle Array Binding benchmarking.
So you can compile the provided 15 - External DB performance sample code, and check by your own the framework speed.

Insertion speed

  SQLite3
(file)
SQLite3
(mem)
TObjectList
(static)
TObjectList
(virtual)
SQLite3
(ext file)
SQLite3
(ext mem)
Oracle Jet
Direct 8 30117 96887 98380 9 26132 464 688
Batch 8 37179 145112 147102 8 31380 52451 713
Trans 14830 35630 99145 97625 13470 32091 715 905
Batch Trans 15852 45574 147784 148337 14955 40077 53202 917

Read speed

  SQLite3
(file)
SQLite3
(mem)
TObjectList
(static)
TObjectList
(virtual)
SQLite3
(ext file)
SQLite3
(ext mem)
Oracle Jet
By one 10457 43952 100166 99391 45602 47955 809 1052
All Virtual 169050 172848 234356 98561 82561 82661 50887 49241
All Direct 168135 170328 233863 239854 160164 162032 72396 76045

For both writing and reading, TObjectList / TSQLRestServerStaticInMemory engine gives impressive results, but have the weakness of being in-memory: you can write its content on disk either as JSON or as compressed binary, but every transaction is not ACID in case of power loss. With the latest version of the framework, TSQLRestServerStaticInMemory does also feature very efficient hashed indexes for very fast lookup of values. When declared as virtual table (via a VirtualTableRegister call), you have the full power of SQL (including JOINs) at hand, with incredibly fast CRUD operations: 100,000 requests per second for objects read and write, including serialization and Client-Server communication!

Note that all those tests were performed locally and in-process, via a TSQLRestClientDB instance. For both insertion and reading, a Client-Server architecture (e.g. using HTTP/1.1 for mORMot clients) will give even better results for BATCH and retrieve all modes. During the tests, internal caching was disabled, so you may expect speed enhancements for real applications, when data is more read than written: for instance, when an object is retrieved from the cache, you achieve 450,000 read requests per second, whatever database is used.

Since all the results above are on an average desktop computer, you may find out incredible speed achieved with a Server hardware, e.g. using SSD or a NAS storage. Just imagine how fast your server side may be, when using mORMot and Delphi instead of other technologies! And more clients on the same hardware often allow increasing ROI.

Feedback is welcome on our forum, as usual.