At first, my benchmark code looked like this:
require "json"
require "benchmark"
struct Message
JSON.mapping(
cmd: String,
message: Hash(String, String),
extra: Int32
)
end
JSON_DATA = %({"cmd": "LOGIN", "extra": 123, "message": {"hi": "[1, 2, 3]", "username": "george", "password": "muffin"}})
Benchmark.ips do |x|
x.report("JSON.parse with JSON::Any") {
message = JSON.parse(JSON_DATA)
# We usually have to invoke type checking.. so let's add some
if message["extra"].as_i == 123
end
}
x.report("from_json with struct & JSON.mapping") {
message = Message.from_json(JSON_DATA)
# statically typed as Int32?
if message.extra == 123
end
}
end
I noticed a problem. The results did not show a big difference . JSON.parse ends up being only 1.04x slower. In fact, sometimes the JSON.parse test was faster, which was weird.
JSON.parse with JSON::Any 519.58k ( 1.92µs) (± 3.15%) 1648 B/op 1.06× slower
from_json with struct & JSON.mapping 552.71k ( 1.81µs) (± 4.04%) 1408 B/op fastest
That got me thinking! Eventually, I added in a loop (which makes more sense because if it’s an incoming command from the client to the gameserver, it will be called more often):
New Code:
require "json"
require "benchmark"
TimesToLoop = 250
struct Message
JSON.mapping(
cmd: String,
message: Hash(String, String),
extra: Int32
)
end
JSON_DATA = %({"cmd": "LOGIN", "extra": 123, "message": {"hi": "[1, 2, 3]", "username": "george", "password": "muffin"}})
Benchmark.ips do |x|
x.report("JSON.parse with JSON::Any") {
message = JSON.parse(JSON_DATA)
# We usually have to invoke type checking.. so let's add one
TimesToLoop.times do
if message["extra"].as_i == 123
end
end
}
x.report("from_json with struct & JSON.mapping") {
message = Message.from_json(JSON_DATA)
TimesToLoop.times do
# statically typed as Int32?
if message.extra == 123
end
end
}
end
Output:
JSON.parse with JSON::Any 157.83k ( 6.34µs) (± 0.86%) 1649 B/op 3.57× slower
from_json with struct & JSON.mapping 564.13k ( 1.77µs) (± 2.12%) 1408 B/op fastest
Over 3.5 times SLOWER!