Skip to content
Geode

Geode benchmarks

Results

Updated · from 10 runs on builds 0.5.0, 0.5.0+format, 0.6.0, 0.6.1

Load, save and release

Live Roblox servers

Each run loads five fresh profiles, saves each, releases each after a change, loads them again and releases them again, with the steps on one key at least 8 seconds apart. Times are seconds from calling the library until its DataStore request finished.

Load, new profile

TimeSeconds per sample. The median, lowest and highest, the samples and how many failed.

Median (the upright tick) and min to max of every sample (the bar). Medians less than one timing step (16.7 ms (1/60 s)) apart are ties.

Table: Time
Time
SubjectMedianMinMaxnFailedTie with
Geode283 ms216 ms433 ms250Geode, mailbox = true
Geode, mailbox = true299 ms182 ms383 ms250Geode, ProfileStore
ProfileStore300 ms216 ms366 ms250Geode, mailbox = true, ProfileService
ProfileService316 ms183 ms400 ms250ProfileStore
DataStore2, Standard saving83 ms65 ms117 ms250–
Historical build
Geode 0.5, mailbox read after every load383 ms283 ms516 ms250–
DataStore requestsRequests the library sent for the key during the step, retries included, as the bench's stand-in for DataStoreService counted them.

Requests per sample, by method. A range means the count varied between samples. 2 methods used.

DataStore requests
SubjectUpdateAsyncGetAsyncnCounts
Geode1–25Same in every sample
Geode, mailbox = true1125Same in every sample
ProfileStore1–25Same in every sample
ProfileService1–25Same in every sample
DataStore2, Standard saving–125Same in every sample
Historical build
Geode 0.5, mailbox read after every load1125Same in every sample

Load, stored profile

TimeSeconds per sample. The median, lowest and highest, the samples and how many failed.

Median (the upright tick) and min to max of every sample (the bar). Medians less than one timing step (16.7 ms (1/60 s)) apart are ties.

Table: Time
Time
SubjectMedianMinMaxnFailedTie with
Geode266 ms166 ms317 ms250Geode, mailbox = true, ProfileStore
Geode, mailbox = true250 ms200 ms283 ms250Geode, ProfileStore
ProfileStore266 ms200 ms350 ms250Geode, Geode, mailbox = true
ProfileService283 ms199 ms350 ms250–
DataStore2, Standard saving33 ms16 ms83 ms250–
Historical build
Geode 0.5, mailbox read after every load333 ms266 ms400 ms250–
DataStore requestsRequests the library sent for the key during the step, retries included, as the bench's stand-in for DataStoreService counted them.

Requests per sample, by method. A range means the count varied between samples. 2 methods used.

DataStore requests
SubjectUpdateAsyncGetAsyncnCounts
Geode1–25Same in every sample
Geode, mailbox = true1125Same in every sample
ProfileStore1–25Same in every sample
ProfileService1–25Same in every sample
DataStore2, Standard saving–125Same in every sample
Historical build
Geode 0.5, mailbox read after every load1125Same in every sample

Save

TimeSeconds per sample. The median, lowest and highest, the samples and how many failed.

Median (the upright tick) and min to max of every sample (the bar). Medians less than one timing step (16.7 ms (1/60 s)) apart are ties.

Table: Time
Time
SubjectMedianMinMaxnFailedTie with
Geode233 ms165 ms301 ms250Geode, mailbox = true
Geode, mailbox = true233 ms166 ms317 ms250Geode
ProfileStore250 ms133 ms300 ms250ProfileService
ProfileService266 ms150 ms316 ms250ProfileStore
DataStore2, Standard saving300 ms199 ms416 ms250–
Historical build
Geode 0.5, mailbox read after every load233 ms151 ms300 ms250–
DataStore requestsRequests the library sent for the key during the step, retries included, as the bench's stand-in for DataStoreService counted them.

Requests per sample, by method. A range means the count varied between samples. 1 method used.

DataStore requests
SubjectUpdateAsyncnCounts
Geode125Same in every sample
Geode, mailbox = true125Same in every sample
ProfileStore125Same in every sample
ProfileService125Same in every sample
DataStore2, Standard saving125Same in every sample
Historical build
Geode 0.5, mailbox read after every load125Same in every sample

Release

TimeSeconds per sample. The median, lowest and highest, the samples and how many failed.

Median (the upright tick) and min to max of every sample (the bar). Medians less than one timing step (16.7 ms (1/60 s)) apart are ties.

Table: Time
Time
SubjectMedianMinMaxnFailedTie with
Geode267 ms183 ms766 ms250Geode, mailbox = true, ProfileStore, ProfileService, DataStore2, Standard saving
Geode, mailbox = true266 ms216 ms450 ms250Geode, ProfileStore, DataStore2, Standard saving
ProfileStore266 ms183 ms350 ms250Geode, Geode, mailbox = true, DataStore2, Standard saving
ProfileService283 ms198 ms350 ms250Geode, DataStore2, Standard saving
DataStore2, Standard saving267 ms200 ms350 ms250Geode, Geode, mailbox = true, ProfileStore, ProfileService
Historical build
Geode 0.5, mailbox read after every load250 ms183 ms400 ms250–
DataStore requestsRequests the library sent for the key during the step, retries included, as the bench's stand-in for DataStoreService counted them.

Requests per sample, by method. A range means the count varied between samples. 1 method used.

DataStore requests
SubjectUpdateAsyncnCounts
Geode125Same in every sample
Geode, mailbox = true125Same in every sample
ProfileStore125Same in every sample
ProfileService125Same in every sample
DataStore2, Standard saving125Same in every sample
Historical build
Geode 0.5, mailbox read after every load125Same in every sample

Server hops

Live Roblox servers

Two Luau Execution tasks at once play the old and the new server. The old server loads a profile, saves 777, waits 8 seconds, then changes it to 888 (999 when it keeps the profile) without saving. Times are seconds from the new server's load call until it has the data.

Old server releases

The old server releases when the new one starts loading, the way a server releases when a player leaves for another one.

Saw the final saveHops in which the new server saw 888, the old server's last change.
Saw the final save
SubjectSaw the final save
Geode15 of 15
Geode, mailbox = true15 of 15
ProfileStore15 of 15
ProfileService15 of 15
DataStore2, Standard saving0 of 15
Historical build
Geode 0.5, mailbox read after every load15 of 15
TimeSeconds per sample. The median, lowest and highest, the samples and how many failed.

Median (the upright tick) and min to max of every sample (the bar). Medians less than one timing step (16.7 ms (1/60 s)) apart are ties.

Table: Time
Time
SubjectMedianMinMaxnFailedTie with
Geode0.70 s0.48 s0.88 s150–
Geode, mailbox = true0.68 s0.50 s0.98 s150–
ProfileStore5.50 s0.63 s5.63 s150–
ProfileService7.30 s0.68 s7.42 s150–
DataStore2, Standard saving0.03 s0.02 s0.05 s150–
Historical build
Geode 0.5, mailbox read after every load4.95 s4.55 s5.17 s150–
DataStore requestsRequests the library sent for the key during the step, retries included, as the bench's stand-in for DataStoreService counted them.

Requests per sample, by method. A range means the count varied between samples. 3 methods used.

DataStore requests
SubjectUpdateAsyncGetAsyncGetAsync past the cachenCounts
Geode2–115Same in every sample
Geode, mailbox = true22115Same in every sample
ProfileStore1 to 2––15Varied between samples
ProfileService1 to 2––15Varied between samples
DataStore2, Standard saving–1–15Same in every sample
Historical build
Geode 0.5, mailbox read after every load41–15Same in every sample

Old server keeps it

The old server never releases on its own. 999 means the library got it to hand the profile over with its last change, 777 means the new server went ahead without it.

Value seenWhat the new server read, and how often.
Value seen
SubjectValue seen
Geode999 in 5 of 5
Geode, mailbox = true999 in 5 of 5
ProfileStore999 in 5 of 5
ProfileService999 in 5 of 5
DataStore2, Standard saving777 in 5 of 5
Historical build
Geode 0.5, mailbox read after every load999 in 5 of 5
TimeSeconds per sample. The median, lowest and highest, the samples and how many failed.

Median (the upright tick) and min to max of every sample (the bar). Medians less than one timing step (16.7 ms (1/60 s)) apart are ties.

Table: Time
Time
SubjectMedianMinMaxnFailedTie with
Geode0.73 s0.65 s0.83 s50–
Geode, mailbox = true0.72 s0.63 s1.05 s50–
ProfileStore5.50 s5.43 s5.63 s50–
ProfileService21.35 s21.28 s21.37 s50–
DataStore2, Standard saving0.03 s0.02 s0.03 s50–
Historical build
Geode 0.5, mailbox read after every load4.98 s4.62 s5.20 s50–
DataStore requestsRequests the library sent for the key during the step, retries included, as the bench's stand-in for DataStoreService counted them.

Requests per sample, by method. A range means the count varied between samples. 3 methods used.

DataStore requests
SubjectUpdateAsyncGetAsyncGetAsync past the cachenCounts
Geode2–15Same in every sample
Geode, mailbox = true2215Same in every sample
ProfileStore2––5Same in every sample
ProfileService4––5Same in every sample
DataStore2, Standard saving–1–5Same in every sample
Historical build
Geode 0.5, mailbox read after every load41–5Same in every sample

Throttling

Live Roblox servers

A burst of 160 or 161 UpdateAsync calls on throwaway keys uses up the server's request budget, then the library loads, saves and releases one profile back to back. One sample per run.

Load

TimeSeconds per sample. The median, lowest and highest, the samples and how many failed.

Shown as a range only: the shortest and longest sample, no median.

Table: Time
Time
SubjectMinMaxnFailed
Geode32.71 s32.88 s50
Geode, mailbox = true33.70 s34.10 s50
ProfileStore31.20 s31.35 s50
ProfileService31.17 s31.59 s50
DataStore2, Standard saving31.04 s31.10 s50
Historical build
Geode 0.5, mailbox read after every load33.79 s34.11 s50
DataStore requestsRequests the library sent for the key during the step, retries included, as the bench's stand-in for DataStoreService counted them.

Requests per sample, by method. A range means the count varied between samples. 2 methods used.

DataStore requests
SubjectUpdateAsyncGetAsyncnCounts
Geode1–5Same in every sample
Geode, mailbox = true115Same in every sample
ProfileStore2–5Same in every sample
ProfileService1–5Same in every sample
DataStore2, Standard saving–615Same in every sample
Historical build
Geode 0.5, mailbox read after every load115Same in every sample

Save

TimeSeconds per sample. The median, lowest and highest, the samples and how many failed.

Shown as a range only: the shortest and longest sample, no median.

Table: Time
Time
SubjectMinMaxnFailed
Geode1.13 s1.32 s50
Geode, mailbox = true0.22 s1.23 s50
ProfileStore0.18 s0.28 s50
ProfileService0.13 s0.35 s50
DataStore2, Standard saving1.08 s1.28 s50
Historical build
Geode 0.5, mailbox read after every load0.20 s1.23 s50
DataStore requestsRequests the library sent for the key during the step, retries included, as the bench's stand-in for DataStoreService counted them.

Requests per sample, by method. A range means the count varied between samples. 1 method used.

DataStore requests
SubjectUpdateAsyncnCounts
Geode15Same in every sample
Geode, mailbox = true15Same in every sample
ProfileStore15Same in every sample
ProfileService15Same in every sample
DataStore2, Standard saving15Same in every sample
Historical build
Geode 0.5, mailbox read after every load15Same in every sample

Release

TimeSeconds per sample. The median, lowest and highest, the samples and how many failed.

Shown as a range only: the shortest and longest sample, no median.

Table: Time
Time
SubjectMinMaxnFailed
Geode0.17 s0.40 s50
Geode, mailbox = true0.22 s0.27 s50
ProfileStore0.17 s0.28 s50
ProfileService6.98 s7.28 s50
DataStore2, Standard saving0.12 s0.20 s50
Historical build
Geode 0.5, mailbox read after every load0.17 s0.28 s50
DataStore requestsRequests the library sent for the key during the step, retries included, as the bench's stand-in for DataStoreService counted them.

Requests per sample, by method. A range means the count varied between samples. 1 method used.

DataStore requests
SubjectUpdateAsyncnCounts
Geode15Same in every sample
Geode, mailbox = true15Same in every sample
ProfileStore15Same in every sample
ProfileService15Same in every sample
DataStore2, Standard saving15Same in every sample
Historical build
Geode 0.5, mailbox read after every load15Same in every sample

Geode edit time

Lune, Geode only

Geode only, in Lune against Geode's test universe on the machine that runs the bench, not on a Roblox server. One loaded profile with the same data as the other suites takes batches of 1000 profile:increment("coins") calls, after one batch that isn't counted. Each edit runs through the profile's transaction pipeline and is recorded for the next save, without a DataStore request. One sample per batch.

Increment

Time per editMicroseconds per edit, from a batch's time divided by its edits.

Median (the upright tick) and min to max of every sample (the bar).

Table: Time per edit
Time per edit
SubjectMedianMinMaxn
Geode10.97 µs7.89 µs11.45 µs15

Not measured

Lapis
Lapis sends every DataStore request from a RunService.PostSimulation handler, and in a Luau Execution task PostSimulation, PreSimulation and Stepped never fired while Heartbeat fired about 62 times a second. Measuring it needs a server that steps physics, such as a live game server.
Lock takeover
Taking over a crashed server's lock waits for the lock to time out (Geode 300 s, ProfileStore 630 s, ProfileService and Lapis 30 minutes), longer than a five minute task.
DataStore2, OrderedBackups
DataStore2's default OrderedBackups method needs DataStores named per player, which the bench's one allowed DataStore rules out. Only the Standard method ran.
Memory per loaded profile
Every run still records it, but the numbers are noise. A script can't make Luau finish a garbage collection cycle, and a cycle that ends between the two heap readings moves the heap by more than twenty profiles take, in either direction. Two runs on the same build gave Geode -0.5 and 4.0 KB per profile, ProfileStore 11.3 and 0.5, DataStore2 10.1 and 21.9, so the order of the libraries flips from one run to the next.
Edit time across libraries
Only Geode's edits are timed. A Geode edit runs through its transaction pipeline, while ProfileStore and ProfileService change a plain table and DataStore2 calls :Update, so per-edit times side by side would compare different work.
Servers with players
Request budgets grow with the player count, and every task here had none.

Every run

Newest first. The results above come from the runs marked as used.