overleaf/services/document-updater/benchmarks/multi_vs_mget_mset.rb
2021-04-13 11:47:10 +01:00

188 lines
5.9 KiB
Ruby

require "benchmark"
require "redis"
N = (ARGV.first || 1).to_i
DOC_ID = (ARGV.last || "606072b20bb4d3109fb5b122")
@r = Redis.new
def get
@r.get("doclines:{#{DOC_ID}}")
@r.get("DocVersion:{#{DOC_ID}}")
@r.get("DocHash:{#{DOC_ID}}")
@r.get("ProjectId:{#{DOC_ID}}")
@r.get("Ranges:{#{DOC_ID}}")
@r.get("Pathname:{#{DOC_ID}}")
@r.get("ProjectHistoryId:{#{DOC_ID}}")
@r.get("UnflushedTime:{#{DOC_ID}}")
@r.get("lastUpdatedAt:{#{DOC_ID}}")
@r.get("lastUpdatedBy:{#{DOC_ID}}")
end
def mget
@r.mget(
"doclines:{#{DOC_ID}}",
"DocVersion:{#{DOC_ID}}",
"DocHash:{#{DOC_ID}}",
"ProjectId:{#{DOC_ID}}",
"Ranges:{#{DOC_ID}}",
"Pathname:{#{DOC_ID}}",
"ProjectHistoryId:{#{DOC_ID}}",
"UnflushedTime:{#{DOC_ID}}",
"lastUpdatedAt:{#{DOC_ID}}",
"lastUpdatedBy:{#{DOC_ID}}",
)
end
def set
@r.set("doclines:{#{DOC_ID}}", "[\"@book{adams1995hitchhiker,\",\" title={The Hitchhiker's Guide to the Galaxy},\",\" author={Adams, D.},\",\" isbn={9781417642595},\",\" url={http://books.google.com/books?id=W-xMPgAACAAJ},\",\" year={1995},\",\" publisher={San Val}\",\"}\",\"\"]")
@r.set("DocVersion:{#{DOC_ID}}", "0")
@r.set("DocHash:{#{DOC_ID}}", "0075bb0629c6c13d0d68918443648bbfe7d98869")
@r.set("ProjectId:{#{DOC_ID}}", "606072b20bb4d3109fb5b11e")
@r.set("Ranges:{#{DOC_ID}}", "")
@r.set("Pathname:{#{DOC_ID}}", "/references.bib")
@r.set("ProjectHistoryId:{#{DOC_ID}}", "")
@r.set("UnflushedTime:{#{DOC_ID}}", "")
@r.set("lastUpdatedAt:{#{DOC_ID}}", "")
@r.set("lastUpdatedBy:{#{DOC_ID}}", "")
end
def mset
@r.mset(
"doclines:{#{DOC_ID}}", "[\"@book{adams1995hitchhiker,\",\" title={The Hitchhiker's Guide to the Galaxy},\",\" author={Adams, D.},\",\" isbn={9781417642595},\",\" url={http://books.google.com/books?id=W-xMPgAACAAJ},\",\" year={1995},\",\" publisher={San Val}\",\"}\",\"\"]",
"DocVersion:{#{DOC_ID}}", "0",
"DocHash:{#{DOC_ID}}", "0075bb0629c6c13d0d68918443648bbfe7d98869",
"ProjectId:{#{DOC_ID}}", "606072b20bb4d3109fb5b11e",
"Ranges:{#{DOC_ID}}", "",
"Pathname:{#{DOC_ID}}", "/references.bib",
"ProjectHistoryId:{#{DOC_ID}}", "",
"UnflushedTime:{#{DOC_ID}}", "",
"lastUpdatedAt:{#{DOC_ID}}", "",
"lastUpdatedBy:{#{DOC_ID}}", "",
)
end
def benchmark_multi_get(benchmark, i)
benchmark.report("#{i}: multi get") do
N.times do
@r.multi do
get
end
end
end
end
def benchmark_mget(benchmark, i)
benchmark.report("#{i}: mget") do
N.times do
mget
end
end
end
def benchmark_multi_set(benchmark, i)
benchmark.report("#{i}: multi set") do
N.times do
@r.multi do
set
end
end
end
end
def benchmark_mset(benchmark, i)
benchmark.report("#{i}: mset") do
N.times do
mset
end
end
end
# init
set
Benchmark.bmbm do |benchmark|
3.times do |i|
benchmark_multi_get(benchmark, i)
benchmark_mget(benchmark, i)
benchmark_multi_set(benchmark, i)
benchmark_mset(benchmark, i)
end
end
=begin
# Results
I could not max out the redis-server process with this benchmark.
The ruby process hit 100% of a modern i7 CPU thread and the redis-server process
barely hit 50% of a CPU thread.
Based on the timings below, mget is about 3 times faster and mset about 4 times
faster than multiple get/set commands in a multi.
=end
=begin
$ redis-server --version
Redis server v=5.0.7 sha=00000000:0 malloc=jemalloc-5.2.1 bits=64 build=636cde3b5c7a3923
$ ruby multi_vs_mget_mset.rb 100000
Rehearsal ------------------------------------------------
0: multi get 12.132423 4.246689 16.379112 ( 16.420069)
0: mget 4.499457 0.947556 5.447013 ( 6.274883)
0: multi set 12.685936 4.495241 17.181177 ( 17.225984)
0: mset 2.543401 0.913448 3.456849 ( 4.554799)
1: multi get 13.397207 4.581881 17.979088 ( 18.027755)
1: mget 4.551287 1.160531 5.711818 ( 6.579168)
1: multi set 13.018957 4.927175 17.946132 ( 17.987502)
1: mset 2.561096 1.048416 3.609512 ( 4.780087)
2: multi get 13.224422 5.014475 18.238897 ( 18.284152)
2: mget 4.664434 1.051083 5.715517 ( 6.592088)
2: multi set 12.972284 4.600422 17.572706 ( 17.613185)
2: mset 2.621344 0.984123 3.605467 ( 4.766855)
------------------------------------- total: 132.843288sec
user system total real
0: multi get 13.341552 4.900892 18.242444 ( 18.289912)
0: mget 5.056534 0.960954 6.017488 ( 6.971189)
0: multi set 12.989880 4.823793 17.813673 ( 17.858393)
0: mset 2.543434 1.025352 3.568786 ( 4.723040)
1: multi get 13.059379 4.674345 17.733724 ( 17.777859)
1: mget 4.698754 0.915637 5.614391 ( 6.489614)
1: multi set 12.608293 4.729163 17.337456 ( 17.372993)
1: mset 2.645290 0.940584 3.585874 ( 4.744134)
2: multi get 13.678224 4.732373 18.410597 ( 18.457525)
2: mget 4.716749 1.072064 5.788813 ( 6.697683)
2: multi set 13.058710 4.889801 17.948511 ( 17.988742)
2: mset 2.311854 0.989166 3.301020 ( 4.346467)
=end
=begin
# multi get/set run at about O(65'000) operations per second
$ redis-cli info | grep 'instantaneous_ops_per_sec'
instantaneous_ops_per_sec:65557
# mget runs at about O(15'000) operations per second
$ redis-cli info | grep 'instantaneous_ops_per_sec'
instantaneous_ops_per_sec:14580
# mset runs at about O(20'000) operations per second
$ redis-cli info | grep 'instantaneous_ops_per_sec'
instantaneous_ops_per_sec:20792
These numbers are pretty reasonable:
multi: 100'000 * 12 ops / 18s = 66'666 ops/s
mget : 100'000 * 1 ops / 7s = 14'285 ops/s
mset : 100'000 * 1 ops / 5s = 20'000 ops/s
Bonus: Running three benchmarks in parallel on different keys.
multi get: O(125'000) ops/s and 80% CPU load of redis-server
multi set: O(130'000) ops/s and 90% CPU load of redis-server
mget : O( 30'000) ops/s and 70% CPU load of redis-server
mset : O( 40'000) ops/s and 90% CPU load of redis-server
=end