aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Pulford <mark@kyne.com.au>2011-05-03 00:39:41 +0930
committerMark Pulford <mark@kyne.com.au>2011-05-03 00:39:41 +0930
commita9bb5006d228539a1ec5028df7f660f862b0f2cc (patch)
tree3216f31e1e0d7479dd024108c7a809b31335eead
parentb07c4162f4e1c5056e73500147a17f4e63abaaf4 (diff)
downloadlua-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.c92
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[] = {
87typedef struct { 86typedef 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
116static int json_config_key;
117
116static json_config_t *json_fetch_config(lua_State *l) 118static 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
251static void json_encode_exception(lua_State *l, strbuf_t *json, int lindex, 256static 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 */
291static void json_append_string(lua_State *l, strbuf_t *json, int lindex) 293static 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
358static void json_encode_descend(lua_State *l, json_config_t *cfg, 360static 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
413static void json_append_object(lua_State *l, json_config_t *cfg, 413static 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)
501static int json_encode(lua_State *l) 501static 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