Backend Development 7 min read

Performance Comparison of cjson, rapidjson, and yyjson in OpenResty with a Lua Wrapper

The article benchmarks cjson, rapidjson, and yyjson JSON libraries on a 6‑core OpenResty setup, shows that combining rapidjson's encode with yyjson's decode reduces 500,000 mixed encode/decode operations to 11 seconds, and describes a Lua wrapper that adds schema validation and improves maintainability.

IEG Growth Platform Technology Team
IEG Growth Platform Technology Team
IEG Growth Platform Technology Team
Performance Comparison of cjson, rapidjson, and yyjson in OpenResty with a Lua Wrapper

The team using the OpenResty framework discovered that the default cjson library consumed a large portion of CPU time during JSON encode/decode, so they evaluated alternative libraries for performance gains.

Benchmark results (500,000 operations each on a 6‑core machine):

Library

Encode

Decode

Mixed (encode+decode)

yyjson

4.3 s

2.8 s

13.0 s

rapidjson

2.3 s

3.6 s

13.7 s

cjson

6.9 s

3.6 s

19 s

The mixed test combines encode and decode operations; the test data size is about 1.2 KB.

From the table, yyjson decodes faster than rapidjson, while rapidjson encodes faster than yyjson.

Mixed 500k Test Code (cjson example)

local cjson = require "cjson"
local strjson = [[
{ "birthday":658944000, "activetime":1486388964, "validtime":0, "headupdatetime":1478856609, "name":"xxxyyyy", "jobauth":400, "degree":200, "head":"http://campusx-10046755.image.myqcloud.com/dfe792fc-c4c2-41a7-98c2-5cf6bc4", "college":{ "name":"信息工程学院 ", "id":12023002 }, "homecity":"长沙", "uid":"D8CXXFD86971B50442F93389C39C4A5A", "helpauthuser":"", "job":"", "headauth":400, "liked_num":11, "school":{ "name":"深圳大学", "id":12023 }, "yunsoid":1057690, "homeprovince":"湖南", "updatetime":1486388964, "grade":2015, "lng":114.12576180217, "dynamichead":"", "valid":0, "clientinfo":"platform=ios,version=2060301,device=CF48F34D-CCEB-4C79-ADBD-F291656AD,model=iPhone 6 Plus/10.2", "heads":"", "lat":22.61024865925, "gender":1, "jobauthurl":["http://campusx-10046755.image.myqcloud.com/ed7da731-63d-xx4ea-b487-4c7f7216bxa"], "registertime":1478856609, "mobile":"", "blocktime":"0", "yunsoutime":1484562092 }
]]
for i=1,500000 do
    local dec_json = cjson.decode(strjson)
    dec_json.encode_i = i
    local enc_str = cjson.encode(dec_json)
end

To achieve the best performance, the final solution wraps rapidjson's encode method with yyjson's decode method into a custom Lua library.

Links to the libraries used:

lua‑rapidjson: https://github.com/xpol/lua-rapidjson/blob/master/API.md lua‑yyjson: https://github.com/mah0x211/lua-yyjson

The wrapper brings several benefits:

Performance boost: mixed 500k operations complete in only 11 seconds.

Easy extensibility: future JSON libraries can be swapped by modifying a single file, improving controllability.

Schema support: rapidjson's encode supports JSON‑Schema validation.

Brief Introduction to JSON‑Schema

JSON‑Schema provides a standard way to describe and validate the structure of JSON data. Example in Lua:

local schema = {
  type = "object",
  properties = {
    count = {type = "integer", minimum = 0},
    time_window = {type = "integer", minimum = 0},
    key = {type = "string", enum = {"remote_addr", "server_addr"}},
    rejected_code = {type = "integer", minimum = 200, maximum = 600},
  },
  additionalProperties = false,
  required = {"count", "time_window", "key", "rejected_code"},
}
local t = {count = 1, time_window = 2, key = "remote_addr", rejected_code = 230}
local schema_json = json.SchemaDocument(schema)
local validator = json.SchemaValidator(schema_json)
local d = json.Document(t)
local ok, msg = validator:validate(d)
print(tostring(ok) .. "----" .. tostring(msg))

Refer to the official schema documentation for details.

Schema spec: https://json-schema.apifox.cn/#一、本文中使用的约定

Benefits of using schema:

Front‑end developers can reuse the same schema for UI development and parameter validation.

Back‑end developers can validate request payloads with lua‑rapidjson 's SchemaValidator without extra code.

Potential Issues When Replacing Libraries

(1) ngx.null in OpenResty causes rapidjson's encode to panic. The solution applied was to modify and re‑compile the lua‑rapidjson source.

(2) cjson's encode prints both array and map parts of a table, while rapidjson and yyjson only output the array part when the table size > 1 and contains an array. Example screenshots illustrate the difference. The fix is to ensure keys that should be strings are converted with tostring(key) before assignment.

PerformanceJSONLuaOpenRestycjsonrapidjsonYYJSON
IEG Growth Platform Technology Team
Written by

IEG Growth Platform Technology Team

Official account of Tencent IEG Growth Platform Technology Team, showcasing cutting‑edge achievements across front‑end, back‑end, client, algorithm, testing and other domains.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.