diff options
author | Mark Pulford <mark@kyne.com.au> | 2011-05-03 00:39:41 +0930 |
---|---|---|
committer | Mark Pulford <mark@kyne.com.au> | 2011-05-03 00:39:41 +0930 |
commit | a9bb5006d228539a1ec5028df7f660f862b0f2cc (patch) | |
tree | 3216f31e1e0d7479dd024108c7a809b31335eead | |
parent | b07c4162f4e1c5056e73500147a17f4e63abaaf4 (diff) | |
download | lua-cjson-a9bb5006d228539a1ec5028df7f660f862b0f2cc.tar.gz lua-cjson-a9bb5006d228539a1ec5028df7f660f862b0f2cc.tar.bz2 lua-cjson-a9bb5006d228539a1ec5028df7f660f862b0f2cc.zip |
Implement minor performance improvements
- Use strbuf_append_mem() for small static strings (~2% speedup).
- Use &json_config_key for storing registry data. It's more unique
and faster than a text string.
- Use strbuf_append_char_unsafe() for string quotes (~4% speedup).
- Use strbuf_append_number() instead of strbuf_append_fmt(). It is
much simpler and avoids the potential for 2 expensive calls to
vsnprintf().
- Make encoding buffer persistent across calls to avoid extra
malloc/free (~4% speedup on example2.json).
These performance improvements can be much more pronounced depending
on the data. Eg, small strings, numbers, booleans, etc..
-rw-r--r-- | lua_cjson.c | 92 |
1 files changed, 50 insertions, 42 deletions
diff --git a/lua_cjson.c b/lua_cjson.c index 2cef3a8..cab1922 100644 --- a/lua_cjson.c +++ b/lua_cjson.c | |||
@@ -45,7 +45,6 @@ | |||
45 | 45 | ||
46 | #include "strbuf.h" | 46 | #include "strbuf.h" |
47 | 47 | ||
48 | #define CJSON_CONFIG_KEY "cjson_configdata" | ||
49 | #define DEFAULT_SPARSE_RATIO 2 | 48 | #define DEFAULT_SPARSE_RATIO 2 |
50 | #define DEFAULT_MAX_DEPTH 20 | 49 | #define DEFAULT_MAX_DEPTH 20 |
51 | 50 | ||
@@ -87,6 +86,7 @@ static const char *json_token_type_name[] = { | |||
87 | typedef struct { | 86 | typedef struct { |
88 | json_token_type_t ch2token[256]; | 87 | json_token_type_t ch2token[256]; |
89 | char ch2escape[256]; | 88 | char ch2escape[256]; |
89 | strbuf_t encode_buf; | ||
90 | int sparse_ratio; | 90 | int sparse_ratio; |
91 | int max_depth; | 91 | int max_depth; |
92 | int current_depth; | 92 | int current_depth; |
@@ -113,11 +113,14 @@ typedef struct { | |||
113 | 113 | ||
114 | /* ===== CONFIGURATION ===== */ | 114 | /* ===== CONFIGURATION ===== */ |
115 | 115 | ||
116 | static int json_config_key; | ||
117 | |||
116 | static json_config_t *json_fetch_config(lua_State *l) | 118 | static json_config_t *json_fetch_config(lua_State *l) |
117 | { | 119 | { |
118 | json_config_t *cfg; | 120 | json_config_t *cfg; |
119 | 121 | ||
120 | lua_getfield(l, LUA_REGISTRYINDEX, CJSON_CONFIG_KEY); | 122 | lua_pushlightuserdata(l, &json_config_key); |
123 | lua_gettable(l, LUA_REGISTRYINDEX); | ||
121 | cfg = lua_touserdata(l, -1); | 124 | cfg = lua_touserdata(l, -1); |
122 | if (!cfg) | 125 | if (!cfg) |
123 | luaL_error(l, "BUG: Unable to fetch cjson configuration"); | 126 | luaL_error(l, "BUG: Unable to fetch cjson configuration"); |
@@ -244,15 +247,14 @@ static void json_create_config(lua_State *l) | |||
244 | cfg->sparse_ratio = DEFAULT_SPARSE_RATIO; | 247 | cfg->sparse_ratio = DEFAULT_SPARSE_RATIO; |
245 | cfg->max_depth = DEFAULT_MAX_DEPTH; | 248 | cfg->max_depth = DEFAULT_MAX_DEPTH; |
246 | cfg->strict_numbers = 1; | 249 | cfg->strict_numbers = 1; |
250 | |||
251 | strbuf_init(&cfg->encode_buf, 0); | ||
247 | } | 252 | } |
248 | 253 | ||
249 | /* ===== ENCODING ===== */ | 254 | /* ===== ENCODING ===== */ |
250 | 255 | ||
251 | static void json_encode_exception(lua_State *l, strbuf_t *json, int lindex, | 256 | static void json_encode_exception(lua_State *l, int lindex, const char *reason) |
252 | const char *reason) | ||
253 | { | 257 | { |
254 | strbuf_free(json); | ||
255 | |||
256 | luaL_error(l, "Cannot serialise %s: %s", | 258 | luaL_error(l, "Cannot serialise %s: %s", |
257 | lua_typename(l, lua_type(l, lindex)), reason); | 259 | lua_typename(l, lua_type(l, lindex)), reason); |
258 | } | 260 | } |
@@ -290,7 +292,7 @@ static inline char *json_escape_char(int c) | |||
290 | * Returns nothing. Doesn't remove string from Lua stack */ | 292 | * Returns nothing. Doesn't remove string from Lua stack */ |
291 | static void json_append_string(lua_State *l, strbuf_t *json, int lindex) | 293 | static void json_append_string(lua_State *l, strbuf_t *json, int lindex) |
292 | { | 294 | { |
293 | char *p; | 295 | const char *p; |
294 | int i; | 296 | int i; |
295 | const char *str; | 297 | const char *str; |
296 | size_t len; | 298 | size_t len; |
@@ -301,9 +303,9 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex) | |||
301 | * This buffer is reused constantly for small strings | 303 | * This buffer is reused constantly for small strings |
302 | * If there are any excess pages, they won't be hit anyway. | 304 | * If there are any excess pages, they won't be hit anyway. |
303 | * This gains ~5% speedup. */ | 305 | * This gains ~5% speedup. */ |
304 | strbuf_ensure_empty_length(json, len * 6); | 306 | strbuf_ensure_empty_length(json, len * 6 + 2); |
305 | 307 | ||
306 | strbuf_append_char(json, '\"'); | 308 | strbuf_append_char_unsafe(json, '\"'); |
307 | for (i = 0; i < len; i++) { | 309 | for (i = 0; i < len; i++) { |
308 | p = json_escape_char(str[i]); | 310 | p = json_escape_char(str[i]); |
309 | if (p) | 311 | if (p) |
@@ -311,7 +313,7 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex) | |||
311 | else | 313 | else |
312 | strbuf_append_char_unsafe(json, str[i]); | 314 | strbuf_append_char_unsafe(json, str[i]); |
313 | } | 315 | } |
314 | strbuf_append_char(json, '\"'); | 316 | strbuf_append_char_unsafe(json, '\"'); |
315 | } | 317 | } |
316 | 318 | ||
317 | /* Find the size of the array on the top of the Lua stack | 319 | /* Find the size of the array on the top of the Lua stack |
@@ -355,13 +357,11 @@ static int lua_array_length(lua_State *l, int sparse_ratio) | |||
355 | return max; | 357 | return max; |
356 | } | 358 | } |
357 | 359 | ||
358 | static void json_encode_descend(lua_State *l, json_config_t *cfg, | 360 | static void json_encode_descend(lua_State *l, json_config_t *cfg) |
359 | strbuf_t *json) | ||
360 | { | 361 | { |
361 | cfg->current_depth++; | 362 | cfg->current_depth++; |
362 | 363 | ||
363 | if (cfg->current_depth > cfg->max_depth) { | 364 | if (cfg->current_depth > cfg->max_depth) { |
364 | strbuf_free(json); | ||
365 | luaL_error(l, "Cannot serialise, excessive nesting (%d)", | 365 | luaL_error(l, "Cannot serialise, excessive nesting (%d)", |
366 | cfg->current_depth); | 366 | cfg->current_depth); |
367 | } | 367 | } |
@@ -378,14 +378,14 @@ static void json_append_array(lua_State *l, json_config_t *cfg, strbuf_t *json, | |||
378 | { | 378 | { |
379 | int comma, i; | 379 | int comma, i; |
380 | 380 | ||
381 | json_encode_descend(l, cfg, json); | 381 | json_encode_descend(l, cfg); |
382 | 382 | ||
383 | strbuf_append_string(json, "[ "); | 383 | strbuf_append_mem(json, "[ ", 2); |
384 | 384 | ||
385 | comma = 0; | 385 | comma = 0; |
386 | for (i = 1; i <= array_length; i++) { | 386 | for (i = 1; i <= array_length; i++) { |
387 | if (comma) | 387 | if (comma) |
388 | strbuf_append_string(json, ", "); | 388 | strbuf_append_mem(json, ", ", 2); |
389 | else | 389 | else |
390 | comma = 1; | 390 | comma = 1; |
391 | 391 | ||
@@ -394,7 +394,7 @@ static void json_append_array(lua_State *l, json_config_t *cfg, strbuf_t *json, | |||
394 | lua_pop(l, 1); | 394 | lua_pop(l, 1); |
395 | } | 395 | } |
396 | 396 | ||
397 | strbuf_append_string(json, " ]"); | 397 | strbuf_append_mem(json, " ]", 2); |
398 | 398 | ||
399 | cfg->current_depth--; | 399 | cfg->current_depth--; |
400 | } | 400 | } |
@@ -405,9 +405,9 @@ static void json_append_number(lua_State *l, strbuf_t *json, int index, | |||
405 | double num = lua_tonumber(l, index); | 405 | double num = lua_tonumber(l, index); |
406 | 406 | ||
407 | if (strict && (isinf(num) || isnan(num))) | 407 | if (strict && (isinf(num) || isnan(num))) |
408 | json_encode_exception(l, json, index, "must not be NaN or Inf"); | 408 | json_encode_exception(l, index, "must not be NaN or Inf"); |
409 | 409 | ||
410 | strbuf_append_fmt(json, LUA_NUMBER_FMT, num); | 410 | strbuf_append_number(json, num); |
411 | } | 411 | } |
412 | 412 | ||
413 | static void json_append_object(lua_State *l, json_config_t *cfg, | 413 | static void json_append_object(lua_State *l, json_config_t *cfg, |
@@ -415,17 +415,17 @@ static void json_append_object(lua_State *l, json_config_t *cfg, | |||
415 | { | 415 | { |
416 | int comma, keytype; | 416 | int comma, keytype; |
417 | 417 | ||
418 | json_encode_descend(l, cfg, json); | 418 | json_encode_descend(l, cfg); |
419 | 419 | ||
420 | /* Object */ | 420 | /* Object */ |
421 | strbuf_append_string(json, "{ "); | 421 | strbuf_append_mem(json, "{ ", 2); |
422 | 422 | ||
423 | lua_pushnil(l); | 423 | lua_pushnil(l); |
424 | /* table, startkey */ | 424 | /* table, startkey */ |
425 | comma = 0; | 425 | comma = 0; |
426 | while (lua_next(l, -2) != 0) { | 426 | while (lua_next(l, -2) != 0) { |
427 | if (comma) | 427 | if (comma) |
428 | strbuf_append_string(json, ", "); | 428 | strbuf_append_mem(json, ", ", 2); |
429 | else | 429 | else |
430 | comma = 1; | 430 | comma = 1; |
431 | 431 | ||
@@ -436,12 +436,12 @@ static void json_append_object(lua_State *l, json_config_t *cfg, | |||
436 | * convert the value in the callers data structure. */ | 436 | * convert the value in the callers data structure. */ |
437 | strbuf_append_char(json, '"'); | 437 | strbuf_append_char(json, '"'); |
438 | json_append_number(l, json, -2, cfg->strict_numbers); | 438 | json_append_number(l, json, -2, cfg->strict_numbers); |
439 | strbuf_append_string(json, "\": "); | 439 | strbuf_append_mem(json, "\": ", 3); |
440 | } else if (keytype == LUA_TSTRING) { | 440 | } else if (keytype == LUA_TSTRING) { |
441 | json_append_string(l, json, -2); | 441 | json_append_string(l, json, -2); |
442 | strbuf_append_string(json, ": "); | 442 | strbuf_append_mem(json, ": ", 2); |
443 | } else { | 443 | } else { |
444 | json_encode_exception(l, json, -2, | 444 | json_encode_exception(l, -2, |
445 | "table key must be a number or string"); | 445 | "table key must be a number or string"); |
446 | /* never returns */ | 446 | /* never returns */ |
447 | } | 447 | } |
@@ -452,7 +452,7 @@ static void json_append_object(lua_State *l, json_config_t *cfg, | |||
452 | /* table, key */ | 452 | /* table, key */ |
453 | } | 453 | } |
454 | 454 | ||
455 | strbuf_append_string(json, " }"); | 455 | strbuf_append_mem(json, " }", 2); |
456 | 456 | ||
457 | cfg->current_depth--; | 457 | cfg->current_depth--; |
458 | } | 458 | } |
@@ -471,9 +471,9 @@ static void json_append_data(lua_State *l, json_config_t *cfg, strbuf_t *json) | |||
471 | break; | 471 | break; |
472 | case LUA_TBOOLEAN: | 472 | case LUA_TBOOLEAN: |
473 | if (lua_toboolean(l, -1)) | 473 | if (lua_toboolean(l, -1)) |
474 | strbuf_append_string(json, "true"); | 474 | strbuf_append_mem(json, "true", 4); |
475 | else | 475 | else |
476 | strbuf_append_string(json, "false"); | 476 | strbuf_append_mem(json, "false", 5); |
477 | break; | 477 | break; |
478 | case LUA_TTABLE: | 478 | case LUA_TTABLE: |
479 | len = lua_array_length(l, cfg->sparse_ratio); | 479 | len = lua_array_length(l, cfg->sparse_ratio); |
@@ -483,17 +483,17 @@ static void json_append_data(lua_State *l, json_config_t *cfg, strbuf_t *json) | |||
483 | json_append_object(l, cfg, json); | 483 | json_append_object(l, cfg, json); |
484 | break; | 484 | break; |
485 | case LUA_TNIL: | 485 | case LUA_TNIL: |
486 | strbuf_append_string(json, "null"); | 486 | strbuf_append_mem(json, "null", 4); |
487 | break; | 487 | break; |
488 | case LUA_TLIGHTUSERDATA: | 488 | case LUA_TLIGHTUSERDATA: |
489 | if (lua_touserdata(l, -1) == NULL) { | 489 | if (lua_touserdata(l, -1) == NULL) { |
490 | strbuf_append_string(json, "null"); | 490 | strbuf_append_mem(json, "null", 4); |
491 | break; | 491 | break; |
492 | } | 492 | } |
493 | default: | 493 | default: |
494 | /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, | 494 | /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, |
495 | * and LUA_TLIGHTUSERDATA) cannot be serialised */ | 495 | * and LUA_TLIGHTUSERDATA) cannot be serialised */ |
496 | json_encode_exception(l, json, -1, "type not supported"); | 496 | json_encode_exception(l, -1, "type not supported"); |
497 | /* never returns */ | 497 | /* never returns */ |
498 | } | 498 | } |
499 | } | 499 | } |
@@ -501,7 +501,6 @@ static void json_append_data(lua_State *l, json_config_t *cfg, strbuf_t *json) | |||
501 | static int json_encode(lua_State *l) | 501 | static int json_encode(lua_State *l) |
502 | { | 502 | { |
503 | json_config_t *cfg; | 503 | json_config_t *cfg; |
504 | strbuf_t buf; | ||
505 | char *json; | 504 | char *json; |
506 | int len; | 505 | int len; |
507 | 506 | ||
@@ -510,12 +509,13 @@ static int json_encode(lua_State *l) | |||
510 | cfg = json_fetch_config(l); | 509 | cfg = json_fetch_config(l); |
511 | cfg->current_depth = 0; | 510 | cfg->current_depth = 0; |
512 | 511 | ||
513 | strbuf_init(&buf, 0); | 512 | /* Reset persistent encode_buf. Avoids temporary allocation |
514 | json_append_data(l, cfg, &buf); | 513 | * for a single call. */ |
515 | json = strbuf_free_to_string(&buf, &len); | 514 | cfg->encode_buf.length = 0; |
515 | json_append_data(l, cfg, &cfg->encode_buf); | ||
516 | json = strbuf_string(&cfg->encode_buf, &len); | ||
516 | 517 | ||
517 | lua_pushlstring(l, json, len); | 518 | lua_pushlstring(l, json, len); |
518 | free(json); | ||
519 | 519 | ||
520 | return 1; | 520 | return 1; |
521 | } | 521 | } |
@@ -761,8 +761,9 @@ static void json_next_token(json_parse_t *json, json_token_t *token) | |||
761 | return; | 761 | return; |
762 | } | 762 | } |
763 | 763 | ||
764 | if (token->type == T_END) | 764 | if (token->type == T_END) { |
765 | return; | 765 | return; |
766 | } | ||
766 | 767 | ||
767 | /* Found a known single character token, advance index and return */ | 768 | /* Found a known single character token, advance index and return */ |
768 | if (token->type != T_UNKNOWN) { | 769 | if (token->type != T_UNKNOWN) { |
@@ -851,8 +852,9 @@ static void json_parse_object_context(lua_State *l, json_parse_t *json) | |||
851 | json_next_token(json, &token); | 852 | json_next_token(json, &token); |
852 | 853 | ||
853 | /* Handle empty objects */ | 854 | /* Handle empty objects */ |
854 | if (token.type == T_OBJ_END) | 855 | if (token.type == T_OBJ_END) { |
855 | return; | 856 | return; |
857 | } | ||
856 | 858 | ||
857 | while (1) { | 859 | while (1) { |
858 | if (token.type != T_STRING) | 860 | if (token.type != T_STRING) |
@@ -874,8 +876,9 @@ static void json_parse_object_context(lua_State *l, json_parse_t *json) | |||
874 | 876 | ||
875 | json_next_token(json, &token); | 877 | json_next_token(json, &token); |
876 | 878 | ||
877 | if (token.type == T_OBJ_END) | 879 | if (token.type == T_OBJ_END) { |
878 | return; | 880 | return; |
881 | } | ||
879 | 882 | ||
880 | if (token.type != T_COMMA) | 883 | if (token.type != T_COMMA) |
881 | json_throw_parse_error(l, json, "comma or object end", &token); | 884 | json_throw_parse_error(l, json, "comma or object end", &token); |
@@ -899,8 +902,9 @@ static void json_parse_array_context(lua_State *l, json_parse_t *json) | |||
899 | json_next_token(json, &token); | 902 | json_next_token(json, &token); |
900 | 903 | ||
901 | /* Handle empty arrays */ | 904 | /* Handle empty arrays */ |
902 | if (token.type == T_ARR_END) | 905 | if (token.type == T_ARR_END) { |
903 | return; | 906 | return; |
907 | } | ||
904 | 908 | ||
905 | for (i = 1; ; i++) { | 909 | for (i = 1; ; i++) { |
906 | json_process_value(l, json, &token); | 910 | json_process_value(l, json, &token); |
@@ -908,8 +912,9 @@ static void json_parse_array_context(lua_State *l, json_parse_t *json) | |||
908 | 912 | ||
909 | json_next_token(json, &token); | 913 | json_next_token(json, &token); |
910 | 914 | ||
911 | if (token.type == T_ARR_END) | 915 | if (token.type == T_ARR_END) { |
912 | return; | 916 | return; |
917 | } | ||
913 | 918 | ||
914 | if (token.type != T_COMMA) | 919 | if (token.type != T_COMMA) |
915 | json_throw_parse_error(l, json, "comma or array end", &token); | 920 | json_throw_parse_error(l, json, "comma or array end", &token); |
@@ -995,8 +1000,11 @@ int luaopen_cjson(lua_State *l) | |||
995 | { NULL, NULL } | 1000 | { NULL, NULL } |
996 | }; | 1001 | }; |
997 | 1002 | ||
1003 | /* Use json_fetch_config as a pointer. | ||
1004 | * It's faster than using a config string, and more unique */ | ||
1005 | lua_pushlightuserdata(l, &json_config_key); | ||
998 | json_create_config(l); | 1006 | json_create_config(l); |
999 | lua_setfield(l, LUA_REGISTRYINDEX, CJSON_CONFIG_KEY); | 1007 | lua_settable(l, LUA_REGISTRYINDEX); |
1000 | 1008 | ||
1001 | luaL_register(l, "cjson", reg); | 1009 | luaL_register(l, "cjson", reg); |
1002 | 1010 | ||