json-as 1.1.19 → 1.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +3 -3
- package/assembly/__benches__/abc.bench.ts +12 -4
- package/assembly/__benches__/lib/bench.ts +19 -7
- package/assembly/index.ts +20 -14
- package/assembly/serialize/simd/string.ts +10 -96
- package/assembly/test.tmp.ts +5 -0
- package/bench/abc.bench.ts +9 -2
- package/bench/lib/bench.ts +33 -18
- package/bench/runners/assemblyscript.js +1 -0
- package/bench.ts +18 -0
- package/package.json +3 -2
- package/run-bench.as.sh +6 -7
- package/run-bench.js.sh +2 -6
- package/bench.js +0 -83
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
7
7
|
█████ ███████ ██████ ██ ████ ██ ██ ███████
|
|
8
8
|
</span>
|
|
9
|
-
AssemblyScript - v1.1.
|
|
9
|
+
AssemblyScript - v1.1.20
|
|
10
10
|
</pre>
|
|
11
11
|
</h6>
|
|
12
12
|
|
|
@@ -391,7 +391,7 @@ These benchmarks compare this library to JavaScript's native `JSON.stringify` an
|
|
|
391
391
|
| Test Case | Size | Serialization (ops/s) | Deserialization (ops/s) | Serialization (MB/s) | Deserialization (MB/s) |
|
|
392
392
|
| --------------- | ---------- | --------------------- | ----------------------- | -------------------- | ---------------------- |
|
|
393
393
|
| Vector3 Object | 38 bytes | 26,611,226 ops/s | 32,160,804 ops/s | 1,357 MB/s | 1,348 MB/s |
|
|
394
|
-
| Alphabet String | 104 bytes |
|
|
394
|
+
| Alphabet String | 104 bytes | 16,916,886 ops/s | 18,390,804 ops/s | 1,759 MB/s | 1,986 MB/s |
|
|
395
395
|
| Small Object | 88 bytes | 24,242,424 ops/s | 12,307,692 ops/s | 2,133 MB/s | 1,083 MB/s |
|
|
396
396
|
| Medium Object | 494 bytes | 4,060,913 ops/s | 1,396,160 ops/s | 2,006 MB/s | 689.7 MB/s |
|
|
397
397
|
| Large Object | 3374 bytes | 479,616 ops/s | 132,802 ops/s | 2,074 MB/s | 448.0 MB/s |
|
|
@@ -401,7 +401,7 @@ These benchmarks compare this library to JavaScript's native `JSON.stringify` an
|
|
|
401
401
|
| Test Case | Size | Serialization (ops/s) | Deserialization (ops/s) | Serialization (MB/s) | Deserialization (MB/s) |
|
|
402
402
|
| --------------- | ---------- | --------------------- | ----------------------- | -------------------- | ---------------------- |
|
|
403
403
|
| Vector3 Object | 38 bytes | 8,791,209 ops/s | 5,369,12 ops/s | 357.4 MB/s | 204.3 MB/s |
|
|
404
|
-
| Alphabet String | 104 bytes |
|
|
404
|
+
| Alphabet String | 104 bytes | 12,830,228 ops/s | 12,140,296 ops/s | 1,334 MB/s | 1,311 MB/s |
|
|
405
405
|
| Small Object | 88 bytes | 8,376,963 ops/s | 4,968,944 ops/s | 737.1 MB/s | 437.2 MB/s |
|
|
406
406
|
| Medium Object | 494 bytes | 2,395,210 ops/s | 1,381,693 ops/s | 1,183 MB/s | 682.5 MB/s |
|
|
407
407
|
| Large Object | 3374 bytes | 222,222 ops/s | 117,233 ops/s | 749.7 MB/s | 395.5 MB/s |
|
|
@@ -5,20 +5,28 @@ import { bench } from "./lib/bench";
|
|
|
5
5
|
const v1 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
6
6
|
const v2 = '"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"';
|
|
7
7
|
|
|
8
|
+
const blackBoxArea = memory.data(64);
|
|
8
9
|
expect(JSON.stringify(v1)).toBe(v2);
|
|
9
10
|
|
|
10
11
|
bench(
|
|
11
12
|
"Serialize Alphabet",
|
|
12
13
|
() => {
|
|
13
|
-
inline.always(JSON.stringify(v1));
|
|
14
|
+
blackbox(inline.always(JSON.stringify(blackbox(v1))));
|
|
14
15
|
},
|
|
15
|
-
|
|
16
|
+
24_000_00,
|
|
17
|
+
v1.length << 1,
|
|
16
18
|
);
|
|
17
19
|
|
|
18
20
|
bench(
|
|
19
21
|
"Deserialize Alphabet",
|
|
20
22
|
() => {
|
|
21
|
-
inline.always(JSON.parse<string>(v2));
|
|
23
|
+
blackbox(inline.always(JSON.parse<string>(blackbox(v2))));
|
|
22
24
|
},
|
|
23
|
-
|
|
25
|
+
24_000_00,
|
|
26
|
+
v2.length << 1,
|
|
24
27
|
);
|
|
28
|
+
|
|
29
|
+
function blackbox<T>(value: T): T {
|
|
30
|
+
store<T>(blackBoxArea, value);
|
|
31
|
+
return load<T>(blackBoxArea);
|
|
32
|
+
}
|
|
@@ -1,20 +1,32 @@
|
|
|
1
|
-
export function bench(description: string, routine: () => void, ops: u64 = 1_000_000): void {
|
|
1
|
+
export function bench(description: string, routine: () => void, ops: u64 = 1_000_000, bytesPerOp: u64 = 0): void {
|
|
2
2
|
console.log(" - Benchmarking " + description);
|
|
3
|
+
|
|
3
4
|
let warmup = ops / 10;
|
|
4
5
|
while (--warmup) {
|
|
5
6
|
routine();
|
|
6
7
|
}
|
|
7
|
-
|
|
8
|
+
|
|
9
|
+
const start = performance.now();
|
|
10
|
+
|
|
8
11
|
let count = ops;
|
|
9
|
-
while (count
|
|
12
|
+
while (count--) {
|
|
10
13
|
routine();
|
|
11
|
-
count--;
|
|
12
14
|
}
|
|
13
|
-
const elapsed = Date.now() - start;
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
const end = performance.now();
|
|
17
|
+
const elapsed = Math.max(1, end - start);
|
|
18
|
+
|
|
19
|
+
const opsPerSecond = f64(ops * 1000) / elapsed;
|
|
20
|
+
|
|
21
|
+
let log = ` Completed benchmark in ${formatNumber(u64(Math.round(elapsed)))}ms at ${formatNumber(u64(Math.round(opsPerSecond)))} ops/s`;
|
|
22
|
+
|
|
23
|
+
if (bytesPerOp > 0) {
|
|
24
|
+
const totalBytes = bytesPerOp * ops;
|
|
25
|
+
const mbPerSec = f64(totalBytes) / (elapsed / 1000) / (1000 * 1000);
|
|
26
|
+
log += ` @ ${formatNumber(u64(Math.round(mbPerSec)))}MB/s`;
|
|
27
|
+
}
|
|
16
28
|
|
|
17
|
-
console.log(
|
|
29
|
+
console.log(log + "\n");
|
|
18
30
|
}
|
|
19
31
|
|
|
20
32
|
function formatNumber(n: u64): string {
|
package/assembly/index.ts
CHANGED
|
@@ -27,6 +27,8 @@ import { serializeObject } from "./serialize/simple/object";
|
|
|
27
27
|
import { deserializeObject } from "./deserialize/simple/object";
|
|
28
28
|
import { serializeRaw } from "./serialize/simple/raw";
|
|
29
29
|
import { deserializeRaw } from "./deserialize/simple/raw";
|
|
30
|
+
import { serializeString_SIMD } from "./serialize/simd/string";
|
|
31
|
+
// import { deserializeString_SIMD } from "./deserialize/simd/string";
|
|
30
32
|
|
|
31
33
|
/**
|
|
32
34
|
* Offset of the 'storage' property in the JSON.Value class.
|
|
@@ -112,11 +114,11 @@ export namespace JSON {
|
|
|
112
114
|
// bs.setBuffer(oldBuf);
|
|
113
115
|
// return changetype<string>(newBuf);
|
|
114
116
|
// }
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
if (ASC_FEATURE_SIMD) {
|
|
118
|
+
serializeString_SIMD(data as string);
|
|
119
|
+
} else {
|
|
120
|
+
serializeString(data as string);
|
|
121
|
+
}
|
|
120
122
|
return bs.out<string>();
|
|
121
123
|
// @ts-ignore: Supplied by transform
|
|
122
124
|
} else if (isDefined(data.__SERIALIZE)) {
|
|
@@ -179,7 +181,7 @@ export namespace JSON {
|
|
|
179
181
|
} else if (isString<T>()) {
|
|
180
182
|
if (dataSize < 4) throw new Error("Cannot parse data as string because it was formatted incorrectly!");
|
|
181
183
|
// if (ASC_FEATURE_SIMD) {
|
|
182
|
-
//
|
|
184
|
+
// // @ts-ignore
|
|
183
185
|
// return changetype<string>(deserializeString_SIMD(dataPtr, dataPtr + dataSize, __new(dataSize - 4, idof<string>())));
|
|
184
186
|
// } else {
|
|
185
187
|
// @ts-ignore
|
|
@@ -426,7 +428,7 @@ export namespace JSON {
|
|
|
426
428
|
// @ts-ignore: type
|
|
427
429
|
private storage: Map<string, JSON.Value> = new Map<string, JSON.Value>();
|
|
428
430
|
|
|
429
|
-
constructor() {}
|
|
431
|
+
constructor() { }
|
|
430
432
|
|
|
431
433
|
// @ts-ignore: decorator
|
|
432
434
|
@inline get size(): i32 {
|
|
@@ -542,7 +544,11 @@ export namespace JSON {
|
|
|
542
544
|
store<u64>(bs.offset, 30399761348886638);
|
|
543
545
|
bs.offset += 8;
|
|
544
546
|
} else if (isString<nonnull<T>>()) {
|
|
545
|
-
|
|
547
|
+
if (ASC_FEATURE_SIMD) {
|
|
548
|
+
serializeString_SIMD(src as string);
|
|
549
|
+
} else {
|
|
550
|
+
serializeString(src as string);
|
|
551
|
+
}
|
|
546
552
|
// @ts-ignore: Supplied by transform
|
|
547
553
|
} else if (isDefined(src.__SERIALIZE_CUSTOM)) {
|
|
548
554
|
// @ts-ignore
|
|
@@ -714,12 +720,12 @@ export namespace JSON {
|
|
|
714
720
|
// bs.setBuffer(oldBuf);
|
|
715
721
|
// return changetype<string>(newBuf);
|
|
716
722
|
// }
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
+
if (ASC_FEATURE_SIMD) {
|
|
724
|
+
serializeString_SIMD(data as string);
|
|
725
|
+
} else {
|
|
726
|
+
bs.saveState();
|
|
727
|
+
serializeString(data as string);
|
|
728
|
+
}
|
|
723
729
|
return bs.cpyOut<string>();
|
|
724
730
|
// @ts-ignore: Supplied by transform
|
|
725
731
|
} else if (isDefined(data.__SERIALIZE)) {
|
|
@@ -3,6 +3,8 @@ import { BACK_SLASH } from "../../custom/chars";
|
|
|
3
3
|
import { SERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
|
|
4
4
|
import { bytes } from "../../util";
|
|
5
5
|
|
|
6
|
+
const U00_MARKER = 13511005048209500;
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* Serializes strings into their JSON counterparts using SIMD operations
|
|
8
10
|
* @param srcStart pointer to begin serializing at
|
|
@@ -13,20 +15,20 @@ export function serializeString_SIMD(src: string): void {
|
|
|
13
15
|
const SPLAT_92 = i16x8.splat(92); /* \ */
|
|
14
16
|
|
|
15
17
|
const SPLAT_32 = i16x8.splat(32); /* [ESC] */
|
|
16
|
-
const SPLAT_0 = i16x8.splat(0); /* 0 */
|
|
17
18
|
|
|
18
19
|
const srcSize = bytes(src);
|
|
19
20
|
let srcStart = changetype<usize>(src);
|
|
20
21
|
const srcEnd = srcStart + srcSize;
|
|
21
22
|
const srcEnd16 = srcEnd - 16;
|
|
22
23
|
|
|
23
|
-
bs.proposeSize(srcSize +
|
|
24
|
+
bs.proposeSize(srcSize + 40);
|
|
24
25
|
|
|
25
26
|
store<u8>(changetype<usize>(bs.offset), 34); /* " */
|
|
26
27
|
bs.offset += 2;
|
|
27
28
|
|
|
28
29
|
while (srcStart <= srcEnd16) {
|
|
29
30
|
const block = v128.load(srcStart);
|
|
31
|
+
|
|
30
32
|
v128.store(bs.offset, block);
|
|
31
33
|
|
|
32
34
|
const backslash_indices = i16x8.eq(block, SPLAT_92);
|
|
@@ -38,21 +40,20 @@ export function serializeString_SIMD(src: string): void {
|
|
|
38
40
|
|
|
39
41
|
while (mask != 0) {
|
|
40
42
|
const lane_index = ctz(mask) << 1;
|
|
41
|
-
const dst_offset = bs.offset + lane_index;
|
|
42
43
|
const src_offset = srcStart + lane_index;
|
|
43
44
|
const code = load<u16>(src_offset) << 2;
|
|
44
45
|
const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + code);
|
|
45
|
-
|
|
46
46
|
mask &= mask - 1;
|
|
47
|
-
|
|
48
47
|
if ((escaped & 0xffff) != BACK_SLASH) {
|
|
49
48
|
bs.growSize(10);
|
|
50
|
-
|
|
49
|
+
const dst_offset = bs.offset + lane_index;
|
|
50
|
+
store<u64>(dst_offset, U00_MARKER);
|
|
51
51
|
store<u32>(dst_offset, escaped, 8);
|
|
52
52
|
v128.store(dst_offset, v128.load(src_offset, 2), 12);
|
|
53
53
|
bs.offset += 10;
|
|
54
54
|
} else {
|
|
55
55
|
bs.growSize(2);
|
|
56
|
+
const dst_offset = bs.offset + lane_index;
|
|
56
57
|
store<u32>(dst_offset, escaped);
|
|
57
58
|
v128.store(dst_offset, v128.load(src_offset, 2), 4);
|
|
58
59
|
bs.offset += 2;
|
|
@@ -63,101 +64,13 @@ export function serializeString_SIMD(src: string): void {
|
|
|
63
64
|
bs.offset += 16;
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
if (rem & 8) {
|
|
68
|
-
const block = v128.load64_zero(srcStart);
|
|
69
|
-
v128.store64_lane(bs.offset, block, 0);
|
|
70
|
-
|
|
71
|
-
const backslash_indices = i16x8.eq(block, SPLAT_92);
|
|
72
|
-
const quote_indices = i16x8.eq(block, SPLAT_34);
|
|
73
|
-
const escape_indices = i16x8.lt_u(block, SPLAT_32);
|
|
74
|
-
const zero_indices = i16x8.eq(block, SPLAT_0);
|
|
75
|
-
const sieve = v128.and(v128.or(v128.or(backslash_indices, quote_indices), escape_indices), v128.not(zero_indices));
|
|
76
|
-
|
|
77
|
-
let mask = i16x8.bitmask(sieve);
|
|
78
|
-
while (mask != 0) {
|
|
79
|
-
let lane_index = ctz(mask) << 1;
|
|
80
|
-
const dst_offset = bs.offset + lane_index;
|
|
81
|
-
const src_offset = srcStart + lane_index;
|
|
82
|
-
const code = load<u16>(src_offset) << 2;
|
|
83
|
-
const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + code);
|
|
84
|
-
mask &= mask - 1;
|
|
85
|
-
|
|
86
|
-
if ((escaped & 0xffff) != BACK_SLASH) {
|
|
87
|
-
bs.growSize(10);
|
|
88
|
-
store<u64>(dst_offset, 13511005048209500);
|
|
89
|
-
store<u32>(dst_offset, escaped, 8);
|
|
90
|
-
while (lane_index < 6) {
|
|
91
|
-
store<u8>(bs.offset + lane_index, load<u8>(srcStart + lane_index, 2), 12);
|
|
92
|
-
lane_index += 2;
|
|
93
|
-
}
|
|
94
|
-
bs.offset += 10;
|
|
95
|
-
} else {
|
|
96
|
-
bs.growSize(2);
|
|
97
|
-
store<u32>(dst_offset, escaped);
|
|
98
|
-
|
|
99
|
-
while (lane_index < 6) {
|
|
100
|
-
store<u8>(bs.offset + lane_index, load<u8>(srcStart + lane_index, 2), 4);
|
|
101
|
-
lane_index += 2;
|
|
102
|
-
}
|
|
103
|
-
bs.offset += 2;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
bs.offset += 8;
|
|
108
|
-
srcStart += 8;
|
|
109
|
-
}
|
|
110
|
-
if (rem & 4) {
|
|
111
|
-
const block = load<u32>(srcStart);
|
|
112
|
-
const codeA = block & 0xffff;
|
|
113
|
-
const codeB = (block >> 16) & 0xffff;
|
|
114
|
-
|
|
115
|
-
if (codeA == 92 || codeA == 34 || codeA < 32) {
|
|
116
|
-
const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + (codeA << 2));
|
|
117
|
-
|
|
118
|
-
if ((escaped & 0xffff) != BACK_SLASH) {
|
|
119
|
-
bs.growSize(10);
|
|
120
|
-
store<u64>(bs.offset, 13511005048209500);
|
|
121
|
-
store<u32>(bs.offset, escaped, 8);
|
|
122
|
-
bs.offset += 12;
|
|
123
|
-
} else {
|
|
124
|
-
bs.growSize(2);
|
|
125
|
-
store<u32>(bs.offset, escaped);
|
|
126
|
-
bs.offset += 4;
|
|
127
|
-
}
|
|
128
|
-
} else {
|
|
129
|
-
store<u16>(bs.offset, codeA);
|
|
130
|
-
bs.offset += 2;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (codeB == 92 || codeB == 34 || codeB < 32) {
|
|
134
|
-
const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + (codeB << 2));
|
|
135
|
-
|
|
136
|
-
if ((escaped & 0xffff) != BACK_SLASH) {
|
|
137
|
-
bs.growSize(10);
|
|
138
|
-
store<u64>(bs.offset, 13511005048209500);
|
|
139
|
-
store<u32>(bs.offset, escaped, 8);
|
|
140
|
-
bs.offset += 12;
|
|
141
|
-
} else {
|
|
142
|
-
bs.growSize(2);
|
|
143
|
-
store<u32>(bs.offset, escaped);
|
|
144
|
-
bs.offset += 4;
|
|
145
|
-
}
|
|
146
|
-
} else {
|
|
147
|
-
store<u16>(bs.offset, codeB);
|
|
148
|
-
bs.offset += 2;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
srcStart += 4;
|
|
152
|
-
}
|
|
153
|
-
if (rem & 2) {
|
|
67
|
+
while (srcStart <= srcEnd - 2) {
|
|
154
68
|
const code = load<u16>(srcStart);
|
|
155
69
|
if (code == 92 || code == 34 || code < 32) {
|
|
156
70
|
const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + (code << 2));
|
|
157
|
-
|
|
158
71
|
if ((escaped & 0xffff) != BACK_SLASH) {
|
|
159
72
|
bs.growSize(10);
|
|
160
|
-
store<u64>(bs.offset,
|
|
73
|
+
store<u64>(bs.offset, U00_MARKER);
|
|
161
74
|
store<u32>(bs.offset, escaped, 8);
|
|
162
75
|
bs.offset += 12;
|
|
163
76
|
} else {
|
|
@@ -169,6 +82,7 @@ export function serializeString_SIMD(src: string): void {
|
|
|
169
82
|
store<u16>(bs.offset, code);
|
|
170
83
|
bs.offset += 2;
|
|
171
84
|
}
|
|
85
|
+
srcStart += 2;
|
|
172
86
|
}
|
|
173
87
|
|
|
174
88
|
store<u8>(bs.offset, 34); /* " */
|
package/assembly/test.tmp.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { JSON } from "../assembly/index";
|
|
2
2
|
import { bs } from "../lib/as-bs";
|
|
3
3
|
|
|
4
|
+
|
|
4
5
|
@json
|
|
5
6
|
class Vec3 {
|
|
6
7
|
x: f32 = 0;
|
|
@@ -24,6 +25,7 @@ class Vec3 {
|
|
|
24
25
|
bs.offset += 2;
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
|
|
27
29
|
@inline
|
|
28
30
|
__INITIALIZE(): this {
|
|
29
31
|
return this;
|
|
@@ -204,6 +206,7 @@ class Vec3 {
|
|
|
204
206
|
}
|
|
205
207
|
}
|
|
206
208
|
|
|
209
|
+
|
|
207
210
|
@json
|
|
208
211
|
class Player {
|
|
209
212
|
|
|
@@ -212,6 +215,7 @@ class Player {
|
|
|
212
215
|
lastName!: string;
|
|
213
216
|
lastActive!: Array<i32>;
|
|
214
217
|
|
|
218
|
+
|
|
215
219
|
@omitif((self: this): boolean => self.age < 18)
|
|
216
220
|
age!: i32;
|
|
217
221
|
pos!: Vec3 | null;
|
|
@@ -260,6 +264,7 @@ class Player {
|
|
|
260
264
|
bs.offset += 2;
|
|
261
265
|
}
|
|
262
266
|
|
|
267
|
+
|
|
263
268
|
@inline
|
|
264
269
|
__INITIALIZE(): this {
|
|
265
270
|
store<string>(changetype<usize>(this), "", offsetof<this>("lastName"));
|
package/bench/abc.bench.ts
CHANGED
|
@@ -6,15 +6,22 @@ const v2 = '"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"';
|
|
|
6
6
|
bench(
|
|
7
7
|
"Serialize Alphabet",
|
|
8
8
|
() => {
|
|
9
|
-
JSON.stringify(v1);
|
|
9
|
+
blackbox(JSON.stringify(blackbox(v1)));
|
|
10
10
|
},
|
|
11
11
|
64_000_00,
|
|
12
|
+
v1.length << 1,
|
|
12
13
|
);
|
|
13
14
|
|
|
14
15
|
bench(
|
|
15
16
|
"Deserialize Alphabet",
|
|
16
17
|
() => {
|
|
17
|
-
JSON.parse(v2);
|
|
18
|
+
blackbox(JSON.parse(blackbox(v2)));
|
|
18
19
|
},
|
|
19
20
|
64_000_00,
|
|
21
|
+
v2.length << 1,
|
|
20
22
|
);
|
|
23
|
+
|
|
24
|
+
function blackbox<T>(value: T): T {
|
|
25
|
+
(globalThis as any).__blackhole = value;
|
|
26
|
+
return globalThis.__blackhole;
|
|
27
|
+
}
|
package/bench/lib/bench.ts
CHANGED
|
@@ -1,27 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
console = {
|
|
3
|
-
log: print,
|
|
4
|
-
error: print,
|
|
5
|
-
warn: print,
|
|
6
|
-
};
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function bench(description: string, routine: () => void, ops: number = 1_000_000): void {
|
|
1
|
+
export function bench(description: string, routine: () => void, ops: number = 1_000_000, bytesPerOp: number = 0): void {
|
|
10
2
|
console.log(" - Benchmarking " + description);
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
|
|
4
|
+
let warmup = Math.floor(ops / 10);
|
|
5
|
+
while (warmup-- > 0) {
|
|
13
6
|
routine();
|
|
14
7
|
}
|
|
15
|
-
|
|
8
|
+
|
|
9
|
+
const start = performance.now();
|
|
10
|
+
|
|
16
11
|
let count = ops;
|
|
17
|
-
while (count
|
|
12
|
+
while (count-- > 0) {
|
|
18
13
|
routine();
|
|
19
|
-
count--;
|
|
20
14
|
}
|
|
21
|
-
const elapsed = Date.now() - start;
|
|
22
15
|
|
|
23
|
-
const
|
|
24
|
-
const
|
|
16
|
+
const end = performance.now();
|
|
17
|
+
const elapsed = Math.max(1, end - start);
|
|
18
|
+
|
|
19
|
+
const opsPerSecond = (ops * 1000) / elapsed;
|
|
20
|
+
|
|
21
|
+
let log = ` Completed benchmark in ${formatNumber(Math.round(elapsed))}ms at ${formatNumber(Math.round(opsPerSecond))} ops/s`;
|
|
22
|
+
|
|
23
|
+
if (bytesPerOp > 0) {
|
|
24
|
+
const totalBytes = bytesPerOp * ops;
|
|
25
|
+
const mbPerSec = totalBytes / (elapsed / 1000) / (1000 * 1000);
|
|
26
|
+
log += ` @ ${formatNumber(Math.round(mbPerSec))}MB/s`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
console.log(log + "\n");
|
|
30
|
+
}
|
|
25
31
|
|
|
26
|
-
|
|
32
|
+
function formatNumber(n: number): string {
|
|
33
|
+
let str = n.toString();
|
|
34
|
+
let len = str.length;
|
|
35
|
+
let result = "";
|
|
36
|
+
let commaOffset = len % 3;
|
|
37
|
+
for (let i = 0; i < len; i++) {
|
|
38
|
+
if (i > 0 && (i - commaOffset) % 3 === 0) result += ",";
|
|
39
|
+
result += str.charAt(i);
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
27
42
|
}
|
package/bench.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Bench } from "tinybench";
|
|
2
|
+
|
|
3
|
+
const bench = new Bench({ time: 1000 });
|
|
4
|
+
|
|
5
|
+
const v1 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
6
|
+
const v2 = '"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"';
|
|
7
|
+
|
|
8
|
+
bench.add("Serialize Alphabet", () => {
|
|
9
|
+
JSON.stringify("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
bench.add("Deserialize Alphabet", () => {
|
|
13
|
+
JSON.parse('"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
bench.run().then(() => {
|
|
17
|
+
console.table(bench.table());
|
|
18
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "json-as",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.20",
|
|
4
4
|
"author": "Jairus Tanaka",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
"main": "transform/lib/index.js",
|
|
10
10
|
"devDependencies": {
|
|
11
11
|
"@assemblyscript/wasi-shim": "^0.1.0",
|
|
12
|
-
"@types/node": "^
|
|
12
|
+
"@types/node": "^24.0.8",
|
|
13
13
|
"assemblyscript": "^0.28.2",
|
|
14
14
|
"assemblyscript-prettier": "^3.0.1",
|
|
15
15
|
"prettier": "^3.6.2",
|
|
16
|
+
"tinybench": "^4.0.1",
|
|
16
17
|
"tsx": "^4.20.3",
|
|
17
18
|
"typescript": "^5.8.3"
|
|
18
19
|
},
|
package/run-bench.as.sh
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
RUNTIMES=${RUNTIMES:-"minimal stub"}
|
|
3
3
|
ENGINES=${ENGINES:-"liftoff ignition sparkplug turbofan llvm"}
|
|
4
|
-
for file in ./assembly/__benches__/
|
|
4
|
+
for file in ./assembly/__benches__/abc.bench.ts; do
|
|
5
5
|
filename=$(basename -- "$file")
|
|
6
|
-
output_wasi=
|
|
7
6
|
for runtime in $RUNTIMES; do
|
|
8
|
-
output="./build/${filename%.ts}.${runtime}
|
|
7
|
+
output="./build/${filename%.ts}.${runtime}"
|
|
9
8
|
|
|
10
|
-
npx asc "$file" --transform ./transform -o "${output}.1" -O3 --converge --noAssert --uncheckedBehavior always --runtime $runtime --enable
|
|
9
|
+
npx asc "$file" --transform ./transform -o "${output}.1" -O3 --converge --noAssert --uncheckedBehavior always --runtime $runtime --enable bulk-memory --exportStart start || {
|
|
11
10
|
echo "Build failed"
|
|
12
11
|
exit 1
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
wasm-opt -
|
|
14
|
+
wasm-opt --enable-bulk-memory --enable-simd --enable-nontrapping-float-to-int --enable-tail-call -tnh -iit -ifwl -s 0 -O4 "${output}.1" -o "${output}.wasm"
|
|
16
15
|
rm "${output}.1"
|
|
17
16
|
|
|
18
17
|
npx asc "$file" --transform ./transform -o "${output}.2" -O3 --converge --noAssert --uncheckedBehavior always --runtime $runtime --enable simd --enable bulk-memory --config ./node_modules/@assemblyscript/wasi-shim/asconfig.json || {
|
|
@@ -20,7 +19,7 @@ for file in ./assembly/__benches__/medium.bench.ts; do
|
|
|
20
19
|
exit 1
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
wasm-opt -
|
|
22
|
+
wasm-opt --enable-bulk-memory --enable-simd --enable-nontrapping-float-to-int --enable-tail-call -tnh -iit -ifwl -s 0 -O4 "${output}.2" -o "${output%.wasm}.wasi.wasm"
|
|
24
23
|
rm "${output}.2"
|
|
25
24
|
|
|
26
25
|
for engine in $ENGINES; do
|
|
@@ -44,7 +43,7 @@ for file in ./assembly/__benches__/medium.bench.ts; do
|
|
|
44
43
|
fi
|
|
45
44
|
|
|
46
45
|
if [[ "$engine" == "llvm" ]]; then
|
|
47
|
-
wasmer run
|
|
46
|
+
wasmer run --llvm --enable-simd --enable-bulk-memory "${output}.wasi.wasm"
|
|
48
47
|
fi
|
|
49
48
|
done
|
|
50
49
|
done
|
package/run-bench.js.sh
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
RUNTIMES=${RUNTIMES:-"
|
|
2
|
+
RUNTIMES=${RUNTIMES:-"liftoff ignition sparkplug turbofan"}
|
|
3
3
|
npx tsc -p ./bench > /dev/null 2>&1
|
|
4
|
-
for file in ./bench
|
|
4
|
+
for file in ./bench/abc.bench.ts; do
|
|
5
5
|
filename=$(basename -- "$file")
|
|
6
6
|
file_js="${filename%.ts}.js"
|
|
7
7
|
|
|
@@ -28,10 +28,6 @@ for file in ./bench/*.bench.ts; do
|
|
|
28
28
|
if [[ "$engine" == "turbofan" ]]; then
|
|
29
29
|
v8 --no-liftoff --no-wasm-tier-up --module ./build/$file_js
|
|
30
30
|
fi
|
|
31
|
-
|
|
32
|
-
if [[ "$engine" == "default" ]]; then
|
|
33
|
-
jsc -m ./build/$file_js
|
|
34
|
-
fi
|
|
35
31
|
done
|
|
36
32
|
done
|
|
37
33
|
|
package/bench.js
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { Bench } from "tinybench";
|
|
2
|
-
// Trying a new benchmarking lib.
|
|
3
|
-
|
|
4
|
-
// JavaScript Results
|
|
5
|
-
// ┌─────────┬───────────────────────────┬─────────────┬────────────────────┬──────────┬─────────┐
|
|
6
|
-
// │ (index) │ Task Name │ ops / sec │ Average Time(ns) │ Margin │ Samples │
|
|
7
|
-
// ├─────────┼───────────────────────────┼─────────────┼────────────────────┼──────────┼─────────┤
|
|
8
|
-
// │ 0 │ 'Stringify Object (Vec3)' │ '817,816' │ 1222.76 │ '±3.55%' │ 81782 │
|
|
9
|
-
// │ 1 │ 'Parse Object (Vec3)' │ '726,115' │ 1377.19 │ '±3.21%' │ 72612 │
|
|
10
|
-
// │ 2 │ 'Stringify Number Array' │ '1,104,036' │ 905.77 │ '±6.48%' │ 110404 │
|
|
11
|
-
// │ 3 │ 'Parse Number Array' │ '1,114,053' │ 897.62 │ '±2.58%' │ 111406 │
|
|
12
|
-
// │ 4 │ 'Stringify String' │ '1,565,716' │ 638.69 │ '±2.04%' │ 156572 │
|
|
13
|
-
// │ 5 │ 'Parse String' │ '69,568' │ 14374.22 │ '±2.55%' │ 6957 │
|
|
14
|
-
// └─────────┴───────────────────────────┴─────────────┴────────────────────┴──────────┴─────────┘
|
|
15
|
-
|
|
16
|
-
// AssemblyScript Results (Runtime Minimal)
|
|
17
|
-
// ┌─────────┬───────────────────────────┬─────────────┬────────────────────┬──────────┬─────────┐
|
|
18
|
-
// │ (index) │ Task Name │ ops / sec │ Average Time(ns) │ Diff │ Samples │
|
|
19
|
-
// ├─────────┼───────────────────────────┼─────────────┼────────────────────┼──────────┼─────────┤
|
|
20
|
-
// │ 0 │ 'Stringify Object (Vec3)' │ '2,091,000' │ 417.22 │ -805ns │ ------- │
|
|
21
|
-
// │ 1 │ 'Parse Object (Vec3)' │ '1,780,000' │ 539.02 │ -838ns │ ------- |
|
|
22
|
-
// │ 2 │ 'Stringify Number Array' │ '1,920,000' │ 445.43 │ -460ns │ ------- │
|
|
23
|
-
// │ 3 │ 'Parse Number Array' │ '1,660,000' │ 597.17 │ -300ns │ ------- │
|
|
24
|
-
// │ 4 │ 'Stringify String' │ '1,280,000' │ 736.27 │ +97ns │ ------- │
|
|
25
|
-
// │ 5 │ 'Parse String' │ '4,230,000' │ 239.21 │ -14135ns │ ------- │
|
|
26
|
-
// └─────────┴───────────────────────────┴─────────────┴────────────────────┴──────────┴─────────┘
|
|
27
|
-
|
|
28
|
-
const vec = {
|
|
29
|
-
x: 3,
|
|
30
|
-
y: 1,
|
|
31
|
-
z: 8,
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
let data;
|
|
35
|
-
|
|
36
|
-
const bench = new Bench({ time: 1000 })
|
|
37
|
-
.add("serialize vec3", () => (data = JSON.stringify(vec)))
|
|
38
|
-
.add("deserialize vec3", () => {
|
|
39
|
-
data = JSON.parse('{"x":3,"y":1,"z":8}');
|
|
40
|
-
})
|
|
41
|
-
.add("serialize alphabet string", () => {
|
|
42
|
-
data = JSON.stringify("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~`!@#$%^&*()-_=+{[}]|\\:;\"'?/>.<,'\"}");
|
|
43
|
-
})
|
|
44
|
-
.add("deserialize alphabet string", () => {
|
|
45
|
-
data = JSON.parse('"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~`!@#$%^&*()-_=+{[}]|\\\\:;\\"\'?/>.<,\'\\"}"');
|
|
46
|
-
}) /*
|
|
47
|
-
.add("parse float", () => {
|
|
48
|
-
data = JSON.parse("1.2345")
|
|
49
|
-
})
|
|
50
|
-
.add("stringify iny", () => {
|
|
51
|
-
data = JSON.stringify(12345)
|
|
52
|
-
})
|
|
53
|
-
.add("parse int", () => {
|
|
54
|
-
data = JSON.parse("12345")
|
|
55
|
-
})
|
|
56
|
-
.add("Stringify Object (Vec3)", () => {
|
|
57
|
-
data = JSON.stringify(vec);
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
.add("Parse Object (Vec3)", () => {
|
|
61
|
-
data = JSON.parse('{"x":0,"y":0,"z":0}');
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
.add("Stringify Number Array", () => {
|
|
65
|
-
data = JSON.stringify([1, 2, 3]);
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
.add("Parse Number Array", () => {
|
|
69
|
-
data = JSON.parse("[1,2,3]");
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
.add("Stringify String", () => {
|
|
73
|
-
data = JSON.stringify('Hello "World!');
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
.add("Parse String", () => {
|
|
77
|
-
data = JSON.stringify('hello "world abc');
|
|
78
|
-
})*/
|
|
79
|
-
.todo("unimplemented .add");
|
|
80
|
-
|
|
81
|
-
await bench.run();
|
|
82
|
-
|
|
83
|
-
console.table(bench.table());
|