Skip to content

Simple benchmark suite so we can make tortoise go faster

Notifications You must be signed in to change notification settings

tortoise/orm-benchmarks

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

71 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ORM Benchmarks

Qualification criteria is:

  • Needs to support minimum 2 databases, e.g. sqlite + something-else
  • Runs on Python3.7
  • Actively developed
  • Has ability to generate initial DDL off specified models
  • Handle one-to-many relationships

Benchmarks:

These benchmarks are not meant to be used as a direct comparison. They suffer from co-operative back-off, and is a lot simpler than common real-world scenarios.

Tests:

  1. Insert: Single (single entry at a time)
  2. Insert: Batch (many batched in a transaction)
  3. Insert: Bulk (using bulk insert operations)
  4. Filter: Large (a large result set)
  5. Filter: Small (a limit 20 with random offset)
  6. Get
  7. Filter: dict
  8. Filter: tuple
  9. Update: Whole (update the whole object)
  10. Update: Partial (update only a single field of the whole object)
  11. Delete

1) Small table, no relations

model Journal:
    id: autonumber primary key
    timestamp: datetime → now()
    level: small int(enum) → 10/20/30/40/50 (indexed)
    text: varchar(255) → A selection of text (indexed)

2) Small table, with relations

model Journal:
    id: autonumber primary key
    timestamp: datetime → now()
    level: small int(enum) → 10/20/30/40/50 (indexed)
    text: varchar(255) → A selection of text (indexed)

    parent: FK to parent BigTree
    child: reverse-FB to parent BigTree
    knows: M2M to BigTree

3) Large table

model BigTree:
    id: uuid primary key
    created_at: datetime → initial-now()
    updated_at: datetime → always-now()
    level: small int(enum) → 10/20/30/40/50 (indexed)
    text: varchar(255) → A selection of text (indexed)

    # Repeated 2 times with defaults, another 2 times as optional:
    col_float: double
    col_smallint: small integer
    col_int: integer
    col_bigint: big integer
    col_char: char(255)
    col_text: text
    col_decimal: decimal(12,8)
    col_json: json

ORMs:

Django:

https://www.djangoproject.com/

Pros:

  • Provides all the essential features
  • Simple, clean, API
  • Great test framework
  • Excellent documentation
  • Migrations done right™

Cons:

  • Brings whole Django along with it
peewee:
https://github.com/coleifer/peewee
Pony ORM:

https://github.com/ponyorm/pony

Pros:

  • Fast
  • Does cacheing automatically

Cons:

  • Does not support bulk insert.
SQLAlchemy ORM:

http://www.sqlalchemy.org/

Pros:

  • The "de facto" ORM in the python world
  • Supports just about every feature and edge case
  • Documentation re DB quirks is excellent

Cons:

  • Complicated, layers upon layers of leaky abstractions
  • You have to manage transactions manually
  • You have to write a script to get DDL SQL
  • Documentation expects you to be intimate with SQLAlchemy
  • Migrations are add ons
SQLObject:

https://github.com/sqlobject/sqlobject

  • Does not support 16-bit integer for level, used 32-bit instead.
  • Does not support bulk insert.
Tortoise ORM:

https://github.com/tortoise/tortoise-orm

  • Currently the only async ORM as part of this suite.
  • Disclaimer: I'm an active contributor to this project

Results (SQLite)

Results for SQLite, using the SHM in-memory filesystem on Linux, to try and make the tests more CPU limited, but still do FS round-trips. Also more consistent than an SSD.

Py39:

Test 1 Django peewee Pony ORM SQLAlchemy ORM SQLObject Tortoise ORM Max Best ORM
Insert: Single 1497.95 4872.62 1644.55 976.67 1420.88 5347.90 5347.90 Tortoise ORM
Insert: Batch 4479.47 6113.61 17873.20 8009.59 4134.64 6585.81 17873.20 Pony ORM
Insert: Bulk 16263.30 19282.38 17151.81 22266.31 22266.31 Tortoise ORM
Filter: Large 72808.51 37053.46 113551.38 77921.94 30458.51 31078.13 113551.38 Pony ORM
Filter: Small 27091.89 21447.86 14085.46 26718.01 28338.93 18595.55 28338.93 SQLObject
Get 3297.72 3342.47 7482.68 3335.06 6650.92 3286.47 7482.68 Pony ORM
Filter: dict 98377.01 42442.45 91354.29 72774.82 37338.13 98377.01 Django
Filter: tuple 104145.07 44562.58 111963.06 120148.50 35421.57 120148.50 SQLAlchemy ORM
Update: Whole 4390.65 5908.89 23083.68 16193.39 12167.63 8778.30 23083.68 Pony ORM
Update: Partial 4799.47 7789.01 31692.20 29134.53 23880.80 10027.27 31692.20 Pony ORM
Delete 4923.51 10720.91 47743.93 46426.27 1602.24 10481.33 47743.93 Pony ORM
Geometric Mean 12016.85 12731.01 25699.52 19750.85 7934.03 13016.58 30488.23 Pony ORM
Test 2 Django peewee Pony ORM SQLAlchemy ORM SQLObject Tortoise ORM Max Best ORM
Insert: Single 1375.24 4739.11 1560.53 826.58 1360.56 5235.68 5235.68 Tortoise ORM
Insert: Batch 4214.45 6076.06 13350.85 2710.25 4431.66 6643.60 13350.85 Pony ORM
Insert: Bulk 14587.36 18580.24 9334.37 20513.05 20513.05 Tortoise ORM
Filter: Large 70246.54 34803.17 107677.40 68477.31 29510.34 29973.02 107677.40 Pony ORM
Filter: Small 25822.56 18710.87 14796.78 21498.59 26458.48 18192.33 26458.48 SQLObject
Get 3061.19 3254.31 8124.48 2930.53 6272.33 3238.39 8124.48 Pony ORM
Filter: dict 92273.77 45158.31 82152.66 63156.77 37411.62 92273.77 Django
Filter: tuple 92140.65 43432.18 107504.91 104278.85 34485.92 107504.91 Pony ORM
Update: Whole 4041.05 5293.05 22296.49 12227.82 11964.75 8385.37 22296.49 Pony ORM
Update: Partial 4718.18 7761.19 32753.93 18142.24 23089.39 8583.97 32753.93 Pony ORM
Delete 949.35 10826.34 15739.36 1126.62 1258.04 7853.74 15739.36 Pony ORM
Geometric Mean 9708.83 12316.1 22066.86 10365.98 7523.29 12234.09 26072.4 Pony ORM
Test 3 Django peewee Pony ORM SQLAlchemy ORM SQLObject Tortoise ORM Max Best ORM
Insert: Single 1158.99 2579.05 1341.74 795.49 1010.96 3012.69 3012.69 Tortoise ORM
Insert: Batch 2565.09 2814.82 5110.64 5135.04 2652.73 3816.79 5135.04 SQLAlchemy ORM
Insert: Bulk 3899.39 7228.57 11383.03 7544.93 11383.03 SQLAlchemy ORM
Filter: Large 23726.12 12886.49 45353.88 32007.81 16424.68 13818.28 45353.88 Pony ORM
Filter: Small 12567.48 7681.68 4872.43 16312.17 15380.60 10183.42 16312.17 SQLAlchemy ORM
Get 1780.10 1121.88 5418.16 2838.25 3760.11 2064.30 5418.16 Pony ORM
Filter: dict 30270.60 18113.41 23406.96 26928.97 15463.37 30270.60 Django
Filter: tuple 32982.30 18634.59 45589.27 39613.25 14293.40 45589.27 Pony ORM
Update: Whole 2520.12 1460.37 16595.00 13253.82 10368.59 4979.73 16595.00 Pony ORM
Update: Partial 4281.07 7621.57 21649.70 21192.27 22376.78 8353.98 22376.78 SQLObject
Delete 4725.69 10672.44 39093.15 38714.27 1436.19 8972.24 39093.15 Pony ORM
Geometric Mean 5920.37 5764.76 12853.14 12079.92 5509.39 7032.0 15685.54 Pony ORM

PyPy7.3-Py3.6: (Outdated)

Test 1 Django peewee Pony ORM SQLAlchemy ORM Tortoise ORM
Insert: Single 4092.94 6042.45 6166.89 1004.98 6786.58
Insert: Batch 4529.93 6456.81 18247.22 6982.63 26348.64
Insert: Bulk 17961.11 24302.27 21428.22 80531.38
Filter: Large 152801.52 91886.54 295678.67 129700.40 90993.88
Filter: Small 6099.36 65094.77 175134.68 60966.19 45463.05
Get 4255.07 6793.41 8310.16 4339.15 9229.52
Filter: dict 147533.08 116293.38 215108.01 109211.59 94985.63
Filter: tuple 175529.83 122951.45 281181.48 253415.27 130914.54
Update: Whole 6710.01 16514.91 41939.12 22677.70 30434.61
Update: Partial 8089.69 23377.04 51308.13 43023.59 38576.48
Delete 8766.41 29169.88 74799.44 81948.65 42805.28
Geometric Mean 15887.12 27270.66 58524.96 28825.51 39281.41
Test 2 Django peewee Pony ORM SQLAlchemy ORM Tortoise ORM
Insert: Single 4089.62 5982.16 5927.49 818.31 8128.96
Insert: Batch 4582.76 6909.47 15558.25 6012.19 25381.23
Insert: Bulk 16201.10 24021.67 20294.09 77993.66
Filter: Large 138968.39 90818.94 279382.51 118860.29 71640.16
Filter: Small 5439.62 62951.57 168192.03 52251.13 38208.34
Get 4092.11 6989.34 8230.02 3379.57 8430.82
Filter: dict 134900.00 112626.68 202932.98 94477.51 71689.52
Filter: tuple 159685.66 122797.29 274293.13 223882.76 119104.10
Update: Whole 6201.26 11396.24 35644.86 17562.70 28303.72
Update: Partial 7669.88 23086.17 41247.77 25492.40 35430.58
Delete 2087.76 34330.64 38098.81 633.66 369.97
Geometric Mean 13135.78 26719.72 50653.72 15519.07 23445.05
Test 3 Django peewee Pony ORM SQLAlchemy ORM Tortoise ORM
Insert: Single 2194.07 3827.50 4030.12 792.54 5429.88
Insert: Batch 2072.86 3928.59 7509.87 4841.25 15489.04
Insert: Bulk 4747.82 9996.01 15407.30 29085.53
Filter: Large 25016.73 30627.76 122459.86 37727.96 2968.21
Filter: Small 1508.74 24123.13 98162.15 21523.32 2454.28
Get 2231.56 4443.86 6313.33 2312.70 1490.23
Filter: dict 29467.52 40064.73 81433.44 27085.70 3001.12
Filter: tuple 31329.65 46774.06 123617.06 45894.78 8845.51
Update: Whole 4220.60 6984.34 29109.60 10686.28 11302.96
Update: Partial 7346.76 21125.93 33835.74 14716.48 24182.52
Delete 9083.28 31221.47 64601.85 64029.41 41709.27
Geometric Mean 6146.34 14064.74 32867.64 12702.84 7951.76

Results (PostgreSQL)

PostgreSQL 14.2 on my iMac.

Test 1 Django peewee Pony ORM SQLAlchemy ORM SQLObject Tortoise ORM Max Best ORM
Insert: Single 2697.46 2350.95 1438.77 1528.94 2391.13 2127.20 2697.46 Django
Insert: Batch 3275.14 2845.78 4710.80 4742.43 2911.46 10351.19 10351.19 Tortoise ORM
Insert: Bulk 10283.94 12309.04 8018.63 6395.90 12309.04 peewee
Filter: Large 82726.80 63288.60 131699.63 58844.30 50097.69 60394.02 131699.63 Pony ORM
Filter: Small 21555.97 21030.33 15793.05 20046.59 38191.14 27055.55 38191.14 SQLObject
Get 2407.95 2351.78 4403.82 2303.46 5057.52 3289.33 5057.52 SQLObject
Filter: dict 98132.27 84224.66 102139.41 60221.74 84444.54 102139.41 Pony ORM
Filter: tuple 106625.81 85903.08 136295.97 88605.19 80993.53 136295.97 Pony ORM
Update: Whole 2921.10 2971.09 4567.47 6585.60 3444.53 15486.60 15486.60 Tortoise ORM
Update: Partial 2797.47 4698.24 8153.30 9595.10 7100.51 18292.88 18292.88 Tortoise ORM
Delete 3068.43 4037.09 10634.38 9627.40 3026.73 20908.43 20908.43 Tortoise ORM
Geometric Mean 10021.18 10045.33 14237.59 11602.3 6874.29 16828.99 22424.22 Tortoise ORM
Test 2 Django peewee Pony ORM SQLAlchemy ORM SQLObject Tortoise ORM Max Best ORM
Insert: Single 2539.09 2328.50 1411.27 1224.83 2251.15 6191.22 6191.22 Tortoise ORM
Insert: Batch 3047.01 2868.06 4822.54 4451.61 2329.81 11730.02 11730.02 Tortoise ORM
Insert: Bulk 10221.78 13097.38 5323.13 24089.97 24089.97 Tortoise ORM
Filter: Large 76022.33 56985.76 129987.42 55025.55 48907.18 66584.00 129987.42 Pony ORM
Filter: Small 25138.05 23036.83 13473.45 15691.13 35394.67 29498.84 35394.67 SQLObject
Get 2369.76 2428.95 6715.08 2186.10 4299.78 3345.98 6715.08 Pony ORM
Filter: dict 89768.94 77124.33 94526.98 52507.12 84390.74 94526.98 Pony ORM
Filter: tuple 93446.95 76994.08 133410.86 76779.38 76248.52 133410.86 Pony ORM
Update: Whole 2624.86 3177.50 6538.28 4518.49 4239.80 13470.83 13470.83 Tortoise ORM
Update: Partial 2755.20 4391.80 7717.85 6744.77 8232.20 17027.82 17027.82 Tortoise ORM
Delete 705.78 6002.87 6820.62 780.78 1502.48 18380.23 18380.23 Tortoise ORM
Geometric Mean 8432.77 10302.12 14258.86 7650.52 6150.27 20779.73 25428.1 Tortoise ORM
Test 3 Django peewee Pony ORM SQLAlchemy ORM SQLObject Tortoise ORM Max Best ORM
Insert: Single 1659.56 1337.22 1072.47 1197.89 1362.25 2057.49 2057.49 Tortoise ORM
Insert: Batch 1926.09 1720.19 2476.65 3928.80 1634.21 5382.42 5382.42 Tortoise ORM
Insert: Bulk 4083.48 4813.74 5169.09 8218.97 8218.97 Tortoise ORM
Filter: Large 29212.11 15313.77 46597.16 28275.76 23833.91 25190.86 46597.16 Pony ORM
Filter: Small 11936.52 7643.78 6958.63 10994.92 19048.65 14186.21 19048.65 SQLObject
Get 1481.25 905.49 3998.63 1917.48 3190.46 2247.54 3998.63 Pony ORM
Filter: dict 30291.07 22944.69 24043.02 24225.81 28221.54 30291.07 Django
Filter: tuple 37316.20 24910.94 48191.83 35775.67 27661.74 48191.83 Pony ORM
Update: Whole 1660.00 1141.62 5982.29 4790.04 4137.46 7815.69 7815.69 Tortoise ORM
Update: Partial 2902.67 4413.85 6453.72 7099.40 8381.57 17360.03 17360.03 Tortoise ORM
Delete 3392.16 6421.26 10231.13 8968.32 2034.67 19775.12 19775.12 Tortoise ORM
Geometric Mean 5444.3 4644.73 8313.06 7434.18 4673.36 10394.95 12595.03 Tortoise ORM

Results (MySQL)

MySQL 8.0.28 on my iMac.

Test 1 Django peewee Pony ORM SQLAlchemy ORM SQLObject Tortoise ORM Max Best ORM
Insert: Single 1209.92 1001.09 1053.19 1347.27 1908.04 6876.58 6876.58 Tortoise ORM
Insert: Batch 749.83 2558.92 3963.25 1530.72 2953.42 9431.34 9431.34 Tortoise ORM
Insert: Bulk 3987.40 12345.27 10112.36 22422.24 22422.24 Tortoise ORM
Filter: Large 49227.96 57264.03 87150.75 58957.62 46543.39 48015.57 87150.75 Pony ORM
Filter: Small 14872.76 16482.54 4844.00 16099.81 33623.56 27429.86 33623.56 SQLObject
Get 1955.32 2397.74 4140.78 2462.82 4124.88 3611.48 4140.78 Pony ORM
Filter: dict 56656.05 70404.15 60058.48 51717.04 56083.08 70404.15 peewee
Filter: tuple 55412.78 71583.70 86158.07 76517.45 52624.17 86158.07 Pony ORM
Update: Whole 2166.47 1924.82 4370.82 6054.75 3347.90 11290.19 11290.19 Tortoise ORM
Update: Partial 2699.93 4266.03 7172.51 10429.35 8004.78 13482.17 13482.17 Tortoise ORM
Delete 2852.52 4261.78 7971.99 11119.38 1466.69 15008.68 15008.68 Tortoise ORM
Geometric Mean 5843.52 8284.89 9942.11 10279.79 5880.45 17482.86 20324.93 Tortoise ORM
Test 2 Django peewee Pony ORM SQLAlchemy ORM SQLObject Tortoise ORM Max Best ORM
Insert: Single 1635.09 1211.99 1503.17 1193.35 1833.09 6608.95 6608.95 Tortoise ORM
Insert: Batch 2169.06 3424.20 3730.49 3134.71 2808.82 9012.26 9012.26 Tortoise ORM
Insert: Bulk 5156.00 12022.20 6509.26 19639.93 19639.93 Tortoise ORM
Filter: Large 47022.58 50669.92 90269.00 53166.04 42782.43 46612.01 90269.00 Pony ORM
Filter: Small 13620.37 19250.93 7432.71 15309.16 32922.06 26025.83 32922.06 SQLObject
Get 1810.18 1743.56 5457.96 2251.67 5029.05 3303.88 5457.96 Pony ORM
Filter: dict 52659.40 64137.99 71828.88 50016.54 49126.48 71828.88 Pony ORM
Filter: tuple 52679.69 65446.35 90069.34 72336.37 50094.20 90069.34 Pony ORM
Update: Whole 2347.67 1706.10 5001.29 5288.58 3714.30 10855.70 10855.70 Tortoise ORM
Update: Partial 2446.26 4068.74 6731.51 8148.18 7689.34 13793.14 13793.14 Tortoise ORM
Delete 611.18 5633.39 5323.53 805.95 1113.54 15653.61 15653.61 Tortoise ORM
Geometric Mean 5700.18 8359.38 10906.27 7695.76 5728.65 16637.86 20627.76 Tortoise ORM
Test 3 Django peewee Pony ORM SQLAlchemy ORM SQLObject Tortoise ORM Max Best ORM
Insert: Single 1161.88 1197.36 868.83 1080.28 1200.52 3517.80 3517.80 Tortoise ORM
Insert: Batch 1136.71 1782.50 2086.51 2289.90 1775.70 4093.84 4093.84 Tortoise ORM
Insert: Bulk 2947.01 4774.88 5026.13 6676.91 6676.91 Tortoise ORM
Filter: Large 21409.92 15339.61 42885.45 26865.84 22947.41 17383.94 42885.45 Pony ORM
Filter: Small 7377.32 7753.80 5326.22 11604.61 18188.73 9937.04 18188.73 SQLObject
Get 912.97 897.40 3436.21 1932.07 2808.04 1647.64 3436.21 Pony ORM
Filter: dict 23786.67 18481.91 20365.59 23747.48 17939.35 23786.67 Django
Filter: tuple 29319.45 9438.12 42158.68 33278.53 16991.10 42158.68 Pony ORM
Update: Whole 1572.93 1061.51 3674.52 5221.10 3289.43 3302.92 5221.10 SQLAlchemy ORM
Update: Partial 2348.49 3900.11 5711.35 7318.67 7214.52 4440.85 7318.67 SQLAlchemy ORM
Delete 2464.61 5020.07 6816.90 9002.52 1637.52 6402.34 9002.52 SQLAlchemy ORM
Geometric Mean 3941.19 3977.64 6671.87 7018.64 4201.45 6434.92 9893.7 SQLAlchemy ORM

PyPy7.3-Py3.6: (Outdated)

Test 1 Django peewee Pony ORM SQLAlchemy ORM Tortoise ORM
Insert: Single 2479.55 2663.10 3088.68 686.92 3311.91
Insert: Batch 3478.12 4571.76 5194.87 4214.03 13584.52
Insert: Bulk 14553.90 19480.48 15260.41 55214.98
Filter: Large 80983.35 175029.85 479457.80 59215.04 160185.46
Filter: Small 4995.72 37628.16 136060.23 18990.66 24888.53
Get 2868.25 4870.54 6107.97 2630.39 6538.67
Filter: dict 80650.88 219339.95 301358.89 52242.19 183104.50
Filter: tuple 93584.59 257332.26 490594.29 74740.17 175407.06
Update: Whole 3563.43 7760.36 5348.80 5540.23 11161.77
Update: Partial 4536.02 10036.94 11210.07 12264.97 14984.03
Delete 4978.72 10073.98 11107.52 10907.25 12449.24
Geometric Mean 9889.22 20926.09 30192.66 11285.31 26393.92
Test 2 Django peewee Pony ORM SQLAlchemy ORM Tortoise ORM
Insert: Single 2266.32 2663.82 2669.34 597.25 3513.88
Insert: Batch 3328.18 4435.96 6949.97 3773.89 10493.08
Insert: Bulk 14065.42 18684.71 14458.93 55861.34
Filter: Large 80296.63 164763.64 447302.97 54498.39 153077.80
Filter: Small 4800.15 35434.65 130211.62 17627.65 21258.96
Get 2565.44 4543.19 5849.48 2310.20 6251.27
Filter: dict 77842.51 207108.12 280970.71 50958.86 185933.49
Filter: tuple 91267.58 239574.53 438762.04 73630.97 172285.95
Update: Whole 2701.75 5406.82 6975.43 4327.97 9913.26
Update: Partial 4539.54 9879.36 10746.90 10125.79 14495.50
Delete 1176.44 7017.19 6249.98 1387.88 8181.26
Geometric Mean 8165.06 18884.27 28591.56 8489.41 24079.39
Test 3 Django peewee Pony ORM SQLAlchemy ORM Tortoise ORM
Insert: Single 1008.08 1582.11 1617.94 465.03 2258.82
Insert: Batch 1492.21 2833.39 3595.88 2126.38 6739.82
Insert: Bulk 3357.71 6484.93 6410.32 16790.12
Filter: Large 12164.83 40576.68 109275.41 9531.62 34746.82
Filter: Small 2314.81 12551.29 58031.65 5503.53 9873.51
Get 1083.01 2165.71 2800.69 854.14 2358.51
Filter: dict 12742.82 54209.15 73098.84 8640.37 46526.40
Filter: tuple 13728.33 62009.01 107794.63 10255.39 42013.15
Update: Whole 1589.21 2352.14 4388.50 3946.67 5021.95
Update: Partial 3894.78 8822.50 6142.39 7243.80 14487.33
Delete 3791.23 8238.25 8413.57 8394.18 11540.60
Geometric Mean 3367.9 8574.74 13385.55 4134.8 11175.14

Quick analysis

  • Pony ORM is heavily optimised for performance.
  • Django & SQLAlchemy is surprisingly similar in performance.
  • Tortoise ORM is competitive.
  • Get is surprisingly slow for everyone.
  • Pony ORM, SQLAlchemy & SQLObject does merge operations for updates, so is technically always partial updates.
  • Tortoise ORM performance using the asyncpg PostgreSQL driver is really good, winning overall.
  • Tortoise ORM performance using the aiomysql MySQL driver is mediocre, the driver itself is taking the majority of CPU time. PyPy runs this driver a lot faster, which indicates that the slow paths are likely just in Python itself.

PyPy comparison: SQLite

  • peewee and Pony ORM gets a noticeable performance improvement
  • SQLAlchemy ORM and Django performs similarily
  • Tortoise ORM has slow Reads and fast Create, Update & Delete operation
  • SQLObject fails

PyPy comparison: MySQL

  • peewee and Tortoise ORM gets a noticeable performance improvement
  • Pony ORM is marginally faster
  • SQLAlchemy ORM and Django is marginally slower
  • SQLObject fails

Performance of Tortoise

Versions

Note that these benchmarks have since changed, so state is not exactly the same as above. This should only be used as a "guideline" of the improvement in performance since we started with the performance optimization process.

Tortoise ORM: v0.10.6 v0.10.7 v0.10.8 v0.10.9 v0.10.11 v0.11.3 v0.12.1
Seedup (Insert & Big & Small) 19.4, 1.5, 6.1 25.9, 2.0, 6.6 81.8, 2.2, 8.7 95.3, 2.4, 13.1 118.2, 2.7, 14.6 136.9, 2.4, 13.5
Insert 89.89 2180.38 2933.19 7635.42 8297.53 9870.59 14544.81
Insert: atomic 149.59 2481.16 3275.53 11966.53 14791.36 18452.56 18245.26
Insert: bulk 71124.01
Filter: match 55866.14 101035.06 139482.12 158997.41 165398.56 186298.75 160746.73
Filter: contains 76803.14 100536.06 128669.50 142954.66 167127.12 177623.78 159116.08
Filter: limit 20 4583.53 27830.14 29995.23 39170.17 58740.05 65742.82 60285.42
Get 233.69 1868.15 2136.20 2818.41 4411.01 4899.04 5208.50

Perf issues identified from profiling

  • base.executor._field_to_db() could be replaced with a pre-computed dict lookup
  • Queryset.resolve_filters is doing lots of unnecessary stuff, especially for .get() method
  • Get operation is slow (likely slow SQL generation, could be resolved with parametrized query cacheing)

On Queryset performance

Since pypika is immutable, and our Queryset object is as well, we need tests to guarantee our immutability. Then we can aggresively cache querysets.

Also, we can make more queries use parameterised queries, cache SQL generation, and cache prepared queries.

It seems in cases where we can cache the PyPika result (and use prepared statements), PyPy performance increase is even larger than CPython.

Perf fixes applied

  1. aiosqlite polling misalignment (sqlite specific)

    (20-40% speedup for retrieval, 10× — 15× speedup for insertion): omnilib/aiosqlite#12

  2. pypika improved copy implementation (generic)

    (53% speedup for insertion): kayak/pypika#160

  3. tortoise.models.__init__ restructure (generic)

    (25-30% speedup for retrieval) tortoise/tortoise-orm#51

  4. tortoise.models.__init__ restructure (generic)

    (9-11% speedup for retrieval) tortoise/tortoise-orm#52

  5. aiosqlite macros (sqlite specific)

    (1-5% speedup for retrieval, 10-40% speedup for insertion) omnilib/aiosqlite#13

  6. Simple prepared insert statements (generic)

    (35-250% speedup for insertion) omnilib/aiosqlite#13 tortoise/tortoise-orm#54

  7. pre-generate initial pypika query object per model (generic)

    (25-50% speedup for small fetch operations) tortoise/tortoise-orm#54

  8. pre-generate filter map, and standard select for all values per model (generic)

    (15-30% speedup for small fetch operations) tortoise/tortoise-orm#64

  9. More optimal queryset cloning (generic)

    (6-15% speedup for small fetch operations) tortoise/tortoise-orm#64

  10. pypika improved copy implementation (generic)

    (10-15% speedup for small fetch operations) kayak/pypika#205

  11. Optimised inserts/updates & Bulk create (generic)

    (5-40% speedup for small insert operations)
    (350-600% speedup for bulk insert over small insert operations) tortoise/tortoise-orm#142
  12. De-lazied some metadata objects & More efficient queryset manipulation (generic)

    (15-25% speedup for large fetch operations)
    (5-30% speedup for small fetches) tortoise/tortoise-orm#158
  13. Parametrized delete/update (generic)

    (260-280% speedup for delete operations)
    (300-600% speedup for update operations) tortoise/tortoise-orm#177
  14. Lazy Relation properties (generic)

    (15~140% speedup for all on Test 2 (Small & Relational)) tortoise/tortoise-orm#187

  15. Know about default converters & native DB types (generic)

    (20-25% speedup for Fetch operations) tortoise/tortoise-orm#190

  16. Connection Pooling (MySQL & PostgreSQL)

    (30-50% speedup overall) tortoise/tortoise-orm#229

  17. Many small tweaks (generic)

    (5-30% depending on driver) tortoise/tortoise-orm#241