Removed CLP(Z) from library(json), achieving a 100x speedup in JSON parsing.

This commit is contained in:
panasenco
2021-04-20 16:58:33 -07:00
parent 5b1b12ec9e
commit c87e3eac08
4 changed files with 54 additions and 17 deletions

View File

@@ -40,7 +40,6 @@
:- use_module(library(assoc)).
:- use_module(library(between)).
:- use_module(library(charsio)).
:- use_module(library(clpz)).
:- use_module(library(dcgs)).
:- use_module(library(dif)).
:- use_module(library(error)).
@@ -164,7 +163,7 @@ json_character(PrintChar) -->
{ dif(PrintChar, '"'),
dif(PrintChar, '\\'),
char_code(PrintChar, PrintCharCode),
PrintCharCode in 32..1114111 /* 20.10FFFF */ }.
PrintCharCode >= 32 /* 20.10FFFF */ }.
json_character(EscapeChar) --> "\\", json_escape(EscapeChar).
json_escape(EscapeChar) -->
@@ -173,29 +172,28 @@ json_escape(EscapeChar) -->
member(EscapeChar-PrintChar, EscapeMap) }.
json_escape(EscapeChar) -->
"u",
/* Logic: Define the domain of the escape character as well as the relationship between the escape character
and the four hexes */
{ [H1, H2, H3, H4] ins 0..15,
EscapeCharCode in 0..65535,
EscapeCharCode #= H1 * 16^3 + H2 * 16^2 + H3 * 16 + H4,
/* Control: Get the code of the escape character if we can. Otherwise we'll end up backtracking over 65,536
{ /* Control: Get the code of the escape character if we can. Otherwise we'll end up backtracking over 65,536
possible hex values.
Logic: Only the first 32 Unicode characters not escaped in the escape map are eligible for \u-escaping
when generating. However, we want to be able to parse any of the 65,536 \u-escaped values when parsing. */
( nonvar(EscapeChar) ->
char_code(EscapeChar, EscapeCharCode),
EscapeCharCode in 0..31,
EscapeCharCode < 32,
escape_map(EscapeMap),
\+ member(EscapeChar-_, EscapeMap)
\+ member(EscapeChar-_, EscapeMap),
H1 = 0,
H2 = 0,
H3 is EscapeCharCode // 16,
H4 is EscapeCharCode mod 16
; true
)
},
) },
json_hex(H1),
json_hex(H2),
json_hex(H3),
json_hex(H4),
/* Control + Logic: Get the escape character atom from the character code computed from the hexes. */
{ ( var(EscapeChar) ->
EscapeCharCode is H1 * 16^3 + H2 * 16^2 + H3 * 16 + H4,
char_code(EscapeChar, EscapeCharCode)
; true
) }.
@@ -234,14 +232,14 @@ json_integer(Digit) --> json_digit(Digit).
json_integer(TotalValue) -->
json_onenine(FirstDigit),
json_digits(RemainingValue, Power),
{ TotalValue #= FirstDigit * 10 ^ (Power + 1) + RemainingValue }.
{ TotalValue is FirstDigit * 10 ^ (Power + 1) + RemainingValue }.
json_digits(Digit, 0) --> json_digit(Digit).
json_digits(Value, Power) -->
json_digit(FirstDigit),
json_digits(RemainingValue, NextPower),
{ Power #= NextPower + 1,
Value #= FirstDigit * 10^Power + RemainingValue }.
{ Power is NextPower + 1,
Value is FirstDigit * 10^Power + RemainingValue }.
json_digit(0) --> "0".
json_digit(Digit) --> json_onenine(Digit).
@@ -267,7 +265,7 @@ json_exponent(Exponent) -->
json_exponent_signifier,
json_sign(Sign),
json_digits(Value, _),
{ Exponent #= Sign * Value }.
{ Exponent is Sign * Value }.
json_exponent_signifier --> "E".
json_exponent_signifier --> "e".

13
src/tests/json/README.md Normal file
View File

@@ -0,0 +1,13 @@
## Benchmarks
### With CLP(Z):
```
?- test_json_read.
% CPU time: 41.522 seconds
```
### After removing CLP(Z):
```
?- test_json_read.
% CPU time: 0.444 seconds
```

View File

@@ -0,0 +1,27 @@
:- module(test_json, [test_json_read/0]).
:- use_module(library(json)).
:- use_module(library(lists)).
:- use_module(library(os)).
:- use_module(library(pio)).
:- use_module(library(time)).
test_path(TestName, TestPath) :-
getenv("SCRYER_JSON_TESTS_PATH", JsonPath),
append(JsonPath, TestName, TestPathChars),
atom_chars(TestPath, TestPathChars).
name_parse(Name, Json) :-
test_path(Name, Path),
once(phrase_from_file(json_chars(Json), Path)).
test_json_read :-
name_parse("pass_null.json", _),
name_parse("pass_alnum.json", _),
name_parse("pass_special.json", _),
name_parse("pass_mandatory_escapes.json", _),
name_parse("pass_forward_slash.json", _),
name_parse("pass_hex.json", _),
name_parse("pass_smallfloat.json", _),
name_parse("pass_bigfloat.json", _),
time(name_parse("pass_everything.json", _)).