jspurefix 5.9.1 → 5.9.2
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/BACKPORT_PLAN.md +20 -7
- package/WORKLOG.md +119 -0
- package/dist/benchmark/bench-report.d.ts +5 -0
- package/dist/benchmark/bench-report.js +91 -0
- package/dist/benchmark/bench-report.js.map +1 -0
- package/dist/benchmark/bench-runner.d.ts +12 -0
- package/dist/benchmark/bench-runner.js +193 -0
- package/dist/benchmark/bench-runner.js.map +1 -0
- package/dist/benchmark/bench-stats.d.ts +10 -0
- package/dist/benchmark/bench-stats.js +56 -0
- package/dist/benchmark/bench-stats.js.map +1 -0
- package/dist/benchmark/bench-types.d.ts +74 -0
- package/dist/benchmark/bench-types.js +3 -0
- package/dist/benchmark/bench-types.js.map +1 -0
- package/dist/benchmark/gc-probe.d.ts +18 -0
- package/dist/benchmark/gc-probe.js +103 -0
- package/dist/benchmark/gc-probe.js.map +1 -0
- package/dist/benchmark/index.d.ts +6 -0
- package/dist/benchmark/index.js +23 -0
- package/dist/benchmark/index.js.map +1 -0
- package/dist/benchmark/parse-bench.d.ts +28 -0
- package/dist/benchmark/parse-bench.js +85 -0
- package/dist/benchmark/parse-bench.js.map +1 -0
- package/dist/buffer/ascii/ascii-parser.d.ts +1 -0
- package/dist/buffer/ascii/ascii-parser.js +4 -1
- package/dist/buffer/ascii/ascii-parser.js.map +1 -1
- package/dist/buffer/ascii/ascii-segment-parser.js +3 -2
- package/dist/buffer/ascii/ascii-segment-parser.js.map +1 -1
- package/dist/buffer/ascii/tag-index.js.map +1 -1
- package/dist/buffer/msg-view.d.ts +1 -0
- package/dist/buffer/msg-view.js +25 -19
- package/dist/buffer/msg-view.js.map +1 -1
- package/dist/dictionary/parser/quickfix/dictionary-validator.d.ts +1 -1
- package/dist/dictionary/parser/quickfix/dictionary-validator.js.map +1 -1
- package/dist/dictionary/parser/quickfix/quick-fix-graph-parser.d.ts +1 -1
- package/dist/dictionary/parser/quickfix/quick-fix-graph-parser.js +1 -2
- package/dist/dictionary/parser/quickfix/quick-fix-graph-parser.js.map +1 -1
- package/dist/dictionary/parser/quickfix/validation-error.d.ts +2 -2
- package/dist/dictionary/parser/quickfix/validation-error.js.map +1 -1
- package/dist/jsfix-bench.d.ts +1 -0
- package/dist/jsfix-bench.js +213 -0
- package/dist/jsfix-bench.js.map +1 -0
- package/dist/store/file-session-store.js +1 -1
- package/dist/store/file-session-store.js.map +1 -1
- package/dist/store/file-session-stream-provider.d.ts +1 -0
- package/dist/store/file-session-stream-provider.js +13 -13
- package/dist/store/file-session-stream-provider.js.map +1 -1
- package/dist/store/fix-session-store-factory.d.ts +1 -1
- package/dist/store/fix-session-store-factory.js.map +1 -1
- package/dist/store/fix-session-store.d.ts +11 -11
- package/dist/store/fix-session-store.js.map +1 -1
- package/dist/store/memory-session-store.js.map +1 -1
- package/dist/store/session-stream-provider.d.ts +13 -13
- package/dist/store/session-stream-provider.js.map +1 -1
- package/dist/transport/session/fix-clock.d.ts +1 -1
- package/dist/transport/session/fix-clock.js.map +1 -1
- package/dist/transport/session/resend-request-manager.d.ts +2 -2
- package/dist/transport/session/resend-request-manager.js.map +1 -1
- package/dist/transport/session/session-sequence-coordinator.d.ts +1 -1
- package/dist/transport/session/session-sequence-coordinator.js.map +1 -1
- package/dist/transport/session/session-sequence-store.d.ts +3 -3
- package/dist/transport/session/session-sequence-store.js.map +1 -1
- package/dist/transport/tcp/tcp-initiator.js.map +1 -1
- package/docs/benchmarking.md +117 -0
- package/eslint.config.mjs +15 -1
- package/jsfix.test_client.txt +62 -62
- package/jsfix.test_server.txt +72 -72
- package/package.json +9 -1
- package/src/benchmark/bench-report.ts +101 -0
- package/src/benchmark/bench-runner.ts +196 -0
- package/src/benchmark/bench-stats.ts +86 -0
- package/src/benchmark/bench-types.ts +110 -0
- package/src/benchmark/gc-probe.ts +145 -0
- package/src/benchmark/index.ts +6 -0
- package/src/benchmark/parse-bench.ts +144 -0
- package/src/buffer/ascii/ascii-parser.ts +16 -1
- package/src/buffer/ascii/ascii-segment-parser.ts +9 -6
- package/src/buffer/ascii/tag-index.ts +3 -3
- package/src/buffer/msg-view.ts +35 -18
- package/src/dictionary/parser/quickfix/dictionary-validator.ts +1 -1
- package/src/dictionary/parser/quickfix/quick-fix-graph-parser.ts +2 -3
- package/src/dictionary/parser/quickfix/validation-error.ts +1 -1
- package/src/jsfix-bench.ts +204 -0
- package/src/store/file-session-store.ts +2 -2
- package/src/store/file-session-stream-provider.ts +17 -11
- package/src/store/fix-session-store-factory.ts +1 -1
- package/src/store/fix-session-store.ts +11 -11
- package/src/store/memory-session-store.ts +1 -1
- package/src/store/session-stream-provider.ts +13 -13
- package/src/transport/session/fix-clock.ts +1 -1
- package/src/transport/session/resend-request-manager.ts +4 -4
- package/src/transport/session/session-sequence-coordinator.ts +1 -1
- package/src/transport/session/session-sequence-store.ts +3 -3
- package/src/transport/tcp/tcp-initiator.ts +1 -1
package/jsfix.test_server.txt
CHANGED
|
@@ -1,75 +1,75 @@
|
|
|
1
|
-
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=
|
|
2
|
-
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=
|
|
3
|
-
8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=
|
|
4
|
-
8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=
|
|
5
|
-
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=
|
|
6
|
-
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=
|
|
7
|
-
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=
|
|
8
|
-
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=
|
|
9
|
-
8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=
|
|
10
|
-
8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=
|
|
11
|
-
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=
|
|
12
|
-
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=
|
|
13
|
-
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=
|
|
14
|
-
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=
|
|
15
|
-
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=2|57=fix|52=
|
|
16
|
-
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=
|
|
17
|
-
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=
|
|
18
|
-
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=
|
|
19
|
-
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=
|
|
20
|
-
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=
|
|
21
|
-
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=
|
|
22
|
-
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=
|
|
23
|
-
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=
|
|
24
|
-
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=
|
|
25
|
-
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=
|
|
26
|
-
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=0|57=fix|52=
|
|
27
|
-
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=
|
|
28
|
-
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=5|57=fix|52=
|
|
29
|
-
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=
|
|
30
|
-
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=
|
|
31
|
-
8=FIX.4.4|9=0000110|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=
|
|
32
|
-
8=FIX.4.4|9=0000125|35=3|49=accept-comp|56=init-comp|34=1|57=fix|52=
|
|
33
|
-
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=
|
|
34
|
-
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=
|
|
1
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:47:29.192|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=108|
|
|
2
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:47:29.199|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=163|
|
|
3
|
+
8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260802-16:47:29.200|7=1|16=1|10=233|
|
|
4
|
+
8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260802-16:47:29.205|122=20260802-16:47:29.204|123=Y|36=2|10=106|
|
|
5
|
+
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260802-16:47:31.202|58=5|10=205|
|
|
6
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:47:31.203|58=5|10=205|
|
|
7
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:47:34.035|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=100|
|
|
8
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:47:34.037|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=150|
|
|
9
|
+
8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260802-16:47:34.038|7=1|16=0|10=237|
|
|
10
|
+
8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260802-16:47:34.041|122=20260802-16:47:34.041|123=Y|36=2|10=095|
|
|
11
|
+
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260802-16:47:36.040|58=5|10=210|
|
|
12
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:47:36.041|58=5|10=210|
|
|
13
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:47:38.525|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=108|
|
|
14
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:47:38.527|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=158|
|
|
15
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=2|57=fix|52=20260802-16:47:38.527|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=111|
|
|
16
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:47:39.070|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=104|
|
|
17
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:47:39.071|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=153|
|
|
18
|
+
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=20260802-16:47:40.074|58=5|10=211|
|
|
19
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:47:40.075|58=5|10=212|
|
|
20
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:47:42.868|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=113|
|
|
21
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:47:42.870|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=154|
|
|
22
|
+
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=20260802-16:47:43.871|58=5|10=219|
|
|
23
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:47:43.872|58=5|10=220|
|
|
24
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:47:46.667|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=114|
|
|
25
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:47:46.668|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=163|
|
|
26
|
+
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=0|57=fix|52=20260802-16:47:47.670|58=5|10=218|
|
|
27
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:47:50.181|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=100|
|
|
28
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=5|57=fix|52=20260802-16:47:50.182|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=153|
|
|
29
|
+
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=20260802-16:47:51.183|58=5|10=214|
|
|
30
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:47:51.184|58=5|10=215|
|
|
31
|
+
8=FIX.4.4|9=0000110|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:47:53.673|98=0|141=Y|553=js-client|554=pwd-client|10=177|
|
|
32
|
+
8=FIX.4.4|9=0000125|35=3|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:47:53.674|45=1|372=A|373=1|58=msgType A missing required tag 108|10=011|
|
|
33
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:47:58.294|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=113|
|
|
34
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:47:58.296|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=163|
|
|
35
35
|
8=FIX4.4|9=0000136|35=ZZ|49=init-comp|56=accept-comp|34=2|57=fix|52=20180902-12:25:28.980|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=177|
|
|
36
|
-
8=FIX.4.4|9=0000111|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=
|
|
37
|
-
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=
|
|
38
|
-
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=
|
|
39
|
-
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=
|
|
40
|
-
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=
|
|
36
|
+
8=FIX.4.4|9=0000111|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:47:58.298|45=2|372=ZZ|373=11|58=msgType ZZ unknown|10=166|
|
|
37
|
+
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260802-16:48:00.297|58=5|10=216|
|
|
38
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260802-16:48:00.298|58=5|10=217|
|
|
39
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:48:03.059|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=103|
|
|
40
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:48:03.061|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=144|
|
|
41
41
|
8=FIX4.4|9=0000123|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20180902-12:25:59.161|999=Sun, 02 Sep 2018 12:25:59 GMT|10=95|
|
|
42
|
-
8=FIX.4.4|9=0000146|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=
|
|
43
|
-
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=
|
|
44
|
-
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=
|
|
45
|
-
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=
|
|
46
|
-
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=
|
|
42
|
+
8=FIX.4.4|9=0000146|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:48:03.063|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 143|10=068|
|
|
43
|
+
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260802-16:48:05.062|58=5|10=211|
|
|
44
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260802-16:48:05.063|58=5|10=212|
|
|
45
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:48:11.555|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=103|
|
|
46
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:48:11.556|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=152|
|
|
47
47
|
8=FIX4.4|9=0000123|35=0|49=init-not!|56=accept-comp|34=2|57=fix|52=20180902-12:25:59.161|112=Sun, 02 Sep 2018 12:25:59 GMT|10=95|
|
|
48
|
-
8=FIX.4.4|9=0000145|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=
|
|
49
|
-
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=
|
|
50
|
-
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=
|
|
51
|
-
8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=
|
|
52
|
-
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=
|
|
53
|
-
8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=
|
|
54
|
-
8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=
|
|
55
|
-
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=4|57=fix|52=
|
|
56
|
-
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=
|
|
57
|
-
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=
|
|
58
|
-
8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=
|
|
59
|
-
8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=
|
|
60
|
-
8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=3|57=fix|52=
|
|
61
|
-
8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=4|57=fix|52=
|
|
62
|
-
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=
|
|
63
|
-
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=5|57=fix|52=
|
|
64
|
-
8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=
|
|
65
|
-
8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=
|
|
66
|
-
8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=
|
|
67
|
-
8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=
|
|
68
|
-
8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=
|
|
69
|
-
8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=4|57=fix|52=
|
|
70
|
-
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=5|57=fix|52=
|
|
71
|
-
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=
|
|
72
|
-
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=
|
|
73
|
-
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=
|
|
74
|
-
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=
|
|
75
|
-
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=
|
|
48
|
+
8=FIX.4.4|9=0000145|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:48:11.558|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 59|10=033|
|
|
49
|
+
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260802-16:48:13.558|58=5|10=220|
|
|
50
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260802-16:48:13.559|58=5|10=221|
|
|
51
|
+
8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:48:16.035|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=051|
|
|
52
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:48:16.036|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=150|
|
|
53
|
+
8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260802-16:48:18.042|112=Sun, 02 Aug 2026 16:48:18 GMT|10=157|
|
|
54
|
+
8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260802-16:48:20.242|112=Sun, 02 Aug 2026 16:48:20 GMT|10=146|
|
|
55
|
+
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=4|57=fix|52=20260802-16:48:22.038|58=5|10=214|
|
|
56
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:48:22.039|58=5|10=213|
|
|
57
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:48:24.515|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=103|
|
|
58
|
+
8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:48:24.516|98=0|108=2|141=Y|553=js-server|554=pwd-server|10=102|
|
|
59
|
+
8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:48:26.523|112=Sun, 02 Aug 2026 16:48:26 GMT|10=159|
|
|
60
|
+
8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=3|57=fix|52=20260802-16:48:28.525|112=Sun, 02 Aug 2026 16:48:28 GMT|10=166|
|
|
61
|
+
8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=4|57=fix|52=20260802-16:48:30.531|112=Sun, 02 Aug 2026 16:48:30 GMT|10=150|
|
|
62
|
+
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=20260802-16:48:32.517|58=5|10=215|
|
|
63
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=5|57=fix|52=20260802-16:48:32.518|58=5|10=219|
|
|
64
|
+
8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:48:35.238|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=057|
|
|
65
|
+
8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:48:35.239|98=0|108=5|141=Y|553=js-server|554=pwd-server|10=109|
|
|
66
|
+
8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260802-16:48:37.244|112=Sun, 02 Aug 2026 16:48:37 GMT|10=163|
|
|
67
|
+
8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260802-16:48:39.248|112=Sun, 02 Aug 2026 16:48:39 GMT|10=172|
|
|
68
|
+
8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:48:40.250|112=Sun, 02 Aug 2026 16:48:40 GMT|10=148|
|
|
69
|
+
8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=4|57=fix|52=20260802-16:48:41.251|112=Sun, 02 Aug 2026 16:48:41 GMT|10=153|
|
|
70
|
+
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=5|57=fix|52=20260802-16:48:43.241|58=5|10=214|
|
|
71
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260802-16:48:43.242|58=5|10=213|
|
|
72
|
+
8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:50:41.579|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=105|
|
|
73
|
+
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:50:41.589|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=154|
|
|
74
|
+
8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=20260802-16:50:42.592|58=5|10=212|
|
|
75
|
+
8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:50:42.593|58=5|10=213|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jspurefix",
|
|
3
|
-
"version": "5.9.
|
|
3
|
+
"version": "5.9.2",
|
|
4
4
|
"description": "pure node js fix engine",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -35,6 +35,14 @@
|
|
|
35
35
|
"repo44-script": "node dist/jsfix-cmd --dict=repo44 --generate --density=0.8 --script --delimiter=\"|\" --repeats=50 --session=\"data/session/test-initiator.json\"",
|
|
36
36
|
"repo44-replay": "node dist/jsfix-cmd --dict=repo44 --fix=data/examples/FIX.4.4/fix.txt --delimiter=\"|\" --stats--delimiter=\"|\" --objects",
|
|
37
37
|
"repo44-repscr": "node dist/jsfix-cmd --dict=repo44 --fix=data/examples/FIX.4.4/fix.txt --delimiter=\"|\" --stats--delimiter=\"|\" --stats",
|
|
38
|
+
"bench": "node --expose-gc dist/jsfix-bench",
|
|
39
|
+
"bench:er": "node --expose-gc dist/jsfix-bench --dict=qf44 --fix=data/examples/FIX.4.4/quickfix/execution-report/fix.txt",
|
|
40
|
+
"bench:er2": "node --expose-gc dist/jsfix-bench --dict=qf44 --fix=data/examples/FIX.4.4/quickfix/execution-report2/fix.txt",
|
|
41
|
+
"bench:nos": "node --expose-gc dist/jsfix-bench --dict=qf43 --fix=data/examples/FIX.4.3/quickfix/new-order-single/fix.txt",
|
|
42
|
+
"bench:md": "node --expose-gc dist/jsfix-bench --dict=qf50sp2 --fix=data/examples/FIX.5.0SP2/quickfix/md-data-snapshot/fix.txt",
|
|
43
|
+
"bench:tc": "node --expose-gc dist/jsfix-bench --dict=repo44 --fix=data/examples/FIX.4.4/repo/trade-capture/fix.txt",
|
|
44
|
+
"bench:save": "node --expose-gc dist/jsfix-bench --save=benchmarks/baseline.json",
|
|
45
|
+
"bench:compare": "node --expose-gc dist/jsfix-bench --baseline=benchmarks/baseline.json",
|
|
38
46
|
"repo44-bench": "node dist/jsfix-cmd --dict=repo44 --benchmark --delimiter=\"|\"",
|
|
39
47
|
"qf43-bench-nos": "node dist/jsfix-cmd --dict=qf43 --fix=data/examples/FIX.4.3/quickfix/new-order-single/fix.txt --benchmark --delimiter=\"|\" --repeats=80000",
|
|
40
48
|
"qf44-bench-er": "node dist/jsfix-cmd --dict=qf44 --fix=data/examples/FIX.4.4/quickfix/execution-report/fix.txt --benchmark --delimiter=\"|\" --repeats=80000",
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { compare } from './bench-stats'
|
|
2
|
+
import { IBenchResult, IBenchSuiteResult } from './bench-types'
|
|
3
|
+
|
|
4
|
+
const bytesPerKb = 1024
|
|
5
|
+
const bytesPerMb = bytesPerKb * 1024
|
|
6
|
+
const nanosPerMicro = 1e3
|
|
7
|
+
|
|
8
|
+
function micros (nanos: number): string {
|
|
9
|
+
return (nanos / nanosPerMicro).toFixed(3)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function mb (bytes: number): string {
|
|
13
|
+
return (bytes / bytesPerMb).toFixed(2)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function thousands (n: number): string {
|
|
17
|
+
return Math.round(n).toLocaleString('en-US')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function signed (n: number, digits: number = 2): string {
|
|
21
|
+
return `${n >= 0 ? '+' : ''}${n.toFixed(digits)}`
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function formatEnvironment (suite: IBenchSuiteResult): string {
|
|
25
|
+
const env = suite.environment
|
|
26
|
+
const lines = [
|
|
27
|
+
`${suite.label} ${suite.capturedAt}`,
|
|
28
|
+
`node ${env.node} v8 ${env.v8} ${env.platform} ${env.arch}`,
|
|
29
|
+
`${env.cpu} x${env.cpuCount}`
|
|
30
|
+
]
|
|
31
|
+
if (!env.forcedGcAvailable) {
|
|
32
|
+
lines.push('WARNING: no --expose-gc, rounds are not isolated and retained heap is indicative only')
|
|
33
|
+
}
|
|
34
|
+
return lines.join('\n')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function formatResult (result: IBenchResult): string {
|
|
38
|
+
const t = result.timing
|
|
39
|
+
const m = result.memory
|
|
40
|
+
const noise = t.coefficientOfVariation * 100
|
|
41
|
+
const lines = [
|
|
42
|
+
`${result.name}`,
|
|
43
|
+
result.description ? ` ${result.description}` : '',
|
|
44
|
+
` rounds ${t.samples} x ${result.options.operationsPerRound} msgs` +
|
|
45
|
+
` (warmup ${result.options.warmupOperations})`,
|
|
46
|
+
` per msg median ${micros(t.median)}us min ${micros(t.min)}us` +
|
|
47
|
+
` p95 ${micros(t.p95)}us max ${micros(t.max)}us`,
|
|
48
|
+
` throughput ${thousands(result.operationsPerSecond)} msg/sec`,
|
|
49
|
+
` noise ${noise.toFixed(2)}% of median (a change smaller than this means nothing)`,
|
|
50
|
+
` gc pass ${m.operations} msgs uninterrupted: ${m.gc.minor} minor ${m.gc.major} major ` +
|
|
51
|
+
`${m.gc.totalPauseMs.toFixed(1)}ms paused longest ${m.gc.longestPauseMs.toFixed(2)}ms`,
|
|
52
|
+
` gc rate ${m.minorGcPerMillionOperations.toFixed(1)} minor/M msgs ` +
|
|
53
|
+
`${m.majorGcPerMillionOperations.toFixed(1)} major/M msgs ` +
|
|
54
|
+
`${m.gcPauseMsPerMillionOperations.toFixed(0)}ms paused/M msgs`,
|
|
55
|
+
` retained ${mb(m.retainedHeapBytes)}MB ` +
|
|
56
|
+
`${m.retainedBytesPerOperation.toFixed(1)} bytes/msg heap at end of pass ${mb(m.heapAtEndOfPassBytes)}MB`
|
|
57
|
+
]
|
|
58
|
+
return lines.filter(l => l.length > 0).join('\n')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function formatSuite (suite: IBenchSuiteResult): string {
|
|
62
|
+
const blocks = suite.results.map(formatResult)
|
|
63
|
+
return [formatEnvironment(suite), '', ...blocks].join('\n\n')
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Compare a suite against a stored baseline, matching results by name.
|
|
68
|
+
*
|
|
69
|
+
* A difference is only called out as real when it exceeds the run to run
|
|
70
|
+
* spread both suites displayed. Everything else is reported as noise, because
|
|
71
|
+
* that is what it is.
|
|
72
|
+
*/
|
|
73
|
+
export function formatComparison (baseline: IBenchSuiteResult, candidate: IBenchSuiteResult): string {
|
|
74
|
+
const lines: string[] = [`baseline ${baseline.label} (${baseline.capturedAt})`, '']
|
|
75
|
+
if (baseline.environment.cpu !== candidate.environment.cpu ||
|
|
76
|
+
baseline.environment.node !== candidate.environment.node) {
|
|
77
|
+
lines.push('WARNING: baseline was taken on a different machine or node build, timings are not comparable')
|
|
78
|
+
lines.push('')
|
|
79
|
+
}
|
|
80
|
+
for (const result of candidate.results) {
|
|
81
|
+
const before = baseline.results.find(r => r.name === result.name)
|
|
82
|
+
if (!before) {
|
|
83
|
+
lines.push(`${result.name} no baseline`)
|
|
84
|
+
continue
|
|
85
|
+
}
|
|
86
|
+
const timing = compare(before.timing, result.timing)
|
|
87
|
+
const verdict = timing.significant
|
|
88
|
+
? (timing.deltaPercent < 0 ? 'FASTER' : 'SLOWER')
|
|
89
|
+
: 'noise'
|
|
90
|
+
lines.push(`${result.name}`)
|
|
91
|
+
lines.push(` per msg ${micros(before.timing.median)}us -> ${micros(result.timing.median)}us` +
|
|
92
|
+
` ${signed(timing.deltaPercent)}% ${verdict}` +
|
|
93
|
+
` (noise floor +/-${timing.noiseThresholdPercent.toFixed(2)}%)`)
|
|
94
|
+
const gcBefore = before.memory.minorGcPerMillionOperations
|
|
95
|
+
const gcAfter = result.memory.minorGcPerMillionOperations
|
|
96
|
+
const gcDelta = gcBefore === 0 ? 0 : ((gcAfter - gcBefore) / gcBefore) * 100
|
|
97
|
+
lines.push(` minor gc ${gcBefore.toFixed(1)} -> ${gcAfter.toFixed(1)} per M msgs ${signed(gcDelta)}%`)
|
|
98
|
+
lines.push('')
|
|
99
|
+
}
|
|
100
|
+
return lines.join('\n')
|
|
101
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import * as os from 'os'
|
|
2
|
+
import { GcProbe } from './gc-probe'
|
|
3
|
+
import { summarise } from './bench-stats'
|
|
4
|
+
import {
|
|
5
|
+
IBenchCase,
|
|
6
|
+
IBenchOptions,
|
|
7
|
+
IBenchResult,
|
|
8
|
+
IBenchSuiteResult,
|
|
9
|
+
IEnvironment,
|
|
10
|
+
IMemoryResult,
|
|
11
|
+
IRoundResult
|
|
12
|
+
} from './bench-types'
|
|
13
|
+
|
|
14
|
+
const nanosPerSecond = 1e9
|
|
15
|
+
const operationsPerMillion = 1e6
|
|
16
|
+
|
|
17
|
+
export const defaultBenchOptions: IBenchOptions = {
|
|
18
|
+
// v8 needs to see a function run a few thousand times before it stops
|
|
19
|
+
// interpreting it, so an unwarmed first round measures the wrong code
|
|
20
|
+
warmupOperations: 20000,
|
|
21
|
+
rounds: 10,
|
|
22
|
+
operationsPerRound: 20000,
|
|
23
|
+
gcBetweenRounds: true
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function environment (): IEnvironment {
|
|
27
|
+
const cpus = os.cpus()
|
|
28
|
+
return {
|
|
29
|
+
node: process.version,
|
|
30
|
+
v8: process.versions.v8,
|
|
31
|
+
platform: `${os.type()} ${os.release()}`,
|
|
32
|
+
arch: process.arch,
|
|
33
|
+
cpu: cpus.length > 0 ? cpus[0].model.trim() : 'unknown',
|
|
34
|
+
cpuCount: cpus.length,
|
|
35
|
+
forcedGcAvailable: GcProbe.forcedGcAvailable()
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** hand the event loop one turn, so queued observer callbacks can run */
|
|
40
|
+
async function tick (): Promise<void> {
|
|
41
|
+
await new Promise<void>(resolve => { setImmediate(resolve) })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Yield until the probe stops seeing new entries. Node dispatches a gc entry on
|
|
46
|
+
* the turn after the collection, and a burst takes several turns to come
|
|
47
|
+
* through, so a single yield loses most of them. Bounded so a pathological case
|
|
48
|
+
* cannot spin here.
|
|
49
|
+
*/
|
|
50
|
+
async function drain (probe: GcProbe, maxTurns: number = 50): Promise<void> {
|
|
51
|
+
let quietTurns = 0
|
|
52
|
+
for (let turn = 0; turn < maxTurns && quietTurns < 3; ++turn) {
|
|
53
|
+
const before = probe.seen()
|
|
54
|
+
await tick()
|
|
55
|
+
quietTurns = probe.seen() === before ? quietTurns + 1 : 0
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Drives a case through a warmup, a series of timed rounds and a separate
|
|
61
|
+
* diagnostics pass.
|
|
62
|
+
*
|
|
63
|
+
* One round is one sample. A single long run cannot tell you whether a number
|
|
64
|
+
* is repeatable, and a difference smaller than the spread between rounds is not
|
|
65
|
+
* a difference at all.
|
|
66
|
+
*
|
|
67
|
+
* Timing and memory are deliberately not measured together. Isolating rounds
|
|
68
|
+
* means forcing a collection between them, and a forced collection is itself a
|
|
69
|
+
* gc event - counting those would report the harness rather than the work. So
|
|
70
|
+
* the timed rounds collect between them and report no gc figures, and a final
|
|
71
|
+
* pass runs uninterrupted with the probe attached and reports no timings.
|
|
72
|
+
*/
|
|
73
|
+
export class BenchRunner {
|
|
74
|
+
constructor (private readonly options: IBenchOptions = defaultBenchOptions) {
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public async run (benchCase: IBenchCase): Promise<IBenchResult> {
|
|
78
|
+
const options = this.options
|
|
79
|
+
benchCase.setup?.()
|
|
80
|
+
try {
|
|
81
|
+
this.warmup(benchCase)
|
|
82
|
+
const rounds: IRoundResult[] = []
|
|
83
|
+
for (let round = 0; round < options.rounds; ++round) {
|
|
84
|
+
if (options.gcBetweenRounds) {
|
|
85
|
+
// a collection provoked by the previous round must not be charged
|
|
86
|
+
// to the next one
|
|
87
|
+
GcProbe.forceGc()
|
|
88
|
+
}
|
|
89
|
+
rounds.push(this.measureRound(benchCase, round))
|
|
90
|
+
}
|
|
91
|
+
const timing = summarise(rounds.map(r => r.nanosPerOperation))
|
|
92
|
+
return {
|
|
93
|
+
name: benchCase.name,
|
|
94
|
+
description: benchCase.description,
|
|
95
|
+
options,
|
|
96
|
+
rounds,
|
|
97
|
+
timing,
|
|
98
|
+
operationsPerSecond: timing.median === 0 ? 0 : nanosPerSecond / timing.median,
|
|
99
|
+
memory: await this.measureMemory(benchCase)
|
|
100
|
+
}
|
|
101
|
+
} finally {
|
|
102
|
+
benchCase.teardown?.()
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public async runAll (label: string, cases: IBenchCase[]): Promise<IBenchSuiteResult> {
|
|
107
|
+
const results: IBenchResult[] = []
|
|
108
|
+
for (const benchCase of cases) {
|
|
109
|
+
results.push(await this.run(benchCase))
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
label,
|
|
113
|
+
// stamped from the clock rather than passed in, a suite is a record of
|
|
114
|
+
// when it was taken
|
|
115
|
+
capturedAt: new Date().toISOString(),
|
|
116
|
+
environment: environment(),
|
|
117
|
+
results
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* One uninterrupted pass with the gc probe attached.
|
|
123
|
+
*
|
|
124
|
+
* Nothing is forced while it runs, so every collection counted is one the
|
|
125
|
+
* work itself provoked. The heap readings either side are taken after a
|
|
126
|
+
* forced collection, making them what the pass retained rather than what it
|
|
127
|
+
* churned - churn is what the collection counts are for.
|
|
128
|
+
*/
|
|
129
|
+
private async measureMemory (benchCase: IBenchCase): Promise<IMemoryResult> {
|
|
130
|
+
// settle, then let any delivery from that settling drain before the probe
|
|
131
|
+
// exists - entries are dispatched a turn late and would otherwise land in
|
|
132
|
+
// the middle of the measured pass
|
|
133
|
+
GcProbe.forceGc()
|
|
134
|
+
await tick()
|
|
135
|
+
|
|
136
|
+
const heapBefore = process.memoryUsage().heapUsed
|
|
137
|
+
const probe = new GcProbe()
|
|
138
|
+
probe.start()
|
|
139
|
+
const operations = benchCase.run(this.options.operationsPerRound)
|
|
140
|
+
const heapAtEndOfPassBytes = process.memoryUsage().heapUsed
|
|
141
|
+
await drain(probe)
|
|
142
|
+
const gc = probe.stop()
|
|
143
|
+
|
|
144
|
+
GcProbe.forceGc()
|
|
145
|
+
const heapAfter = process.memoryUsage().heapUsed
|
|
146
|
+
|
|
147
|
+
const retainedHeapBytes = heapAfter - heapBefore
|
|
148
|
+
const perMillion = operations === 0 ? 0 : operationsPerMillion / operations
|
|
149
|
+
return {
|
|
150
|
+
operations,
|
|
151
|
+
retainedHeapBytes,
|
|
152
|
+
retainedBytesPerOperation: operations === 0 ? 0 : retainedHeapBytes / operations,
|
|
153
|
+
heapAtEndOfPassBytes,
|
|
154
|
+
gc,
|
|
155
|
+
minorGcPerMillionOperations: gc.minor * perMillion,
|
|
156
|
+
majorGcPerMillionOperations: gc.major * perMillion,
|
|
157
|
+
gcPauseMsPerMillionOperations: gc.totalPauseMs * perMillion,
|
|
158
|
+
forcedGcAvailable: GcProbe.forcedGcAvailable()
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private warmup (benchCase: IBenchCase): void {
|
|
163
|
+
const target = this.options.warmupOperations
|
|
164
|
+
if (target <= 0) return
|
|
165
|
+
let done = 0
|
|
166
|
+
// in chunks the size of a real round, so warmup exercises the same shape of
|
|
167
|
+
// work rather than one long unbroken call
|
|
168
|
+
const chunk = Math.max(1, Math.min(target, this.options.operationsPerRound))
|
|
169
|
+
while (done < target) {
|
|
170
|
+
const requested = Math.min(chunk, target - done)
|
|
171
|
+
const completed = benchCase.run(requested)
|
|
172
|
+
// a case that reports less than it was asked for would never let this
|
|
173
|
+
// loop reach its target, so refuse it here rather than spin
|
|
174
|
+
if (completed !== requested) {
|
|
175
|
+
throw new Error(`${benchCase.name}: asked for ${requested} operations during warmup but completed ${completed}`)
|
|
176
|
+
}
|
|
177
|
+
done += completed
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private measureRound (benchCase: IBenchCase, round: number): IRoundResult {
|
|
182
|
+
const requested = this.options.operationsPerRound
|
|
183
|
+
const startedAt = process.hrtime.bigint()
|
|
184
|
+
const operations = benchCase.run(requested)
|
|
185
|
+
const elapsedNanos = Number(process.hrtime.bigint() - startedAt)
|
|
186
|
+
if (operations !== requested) {
|
|
187
|
+
throw new Error(`${benchCase.name}: asked for ${requested} operations in round ${round} but completed ${operations}`)
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
round,
|
|
191
|
+
operations,
|
|
192
|
+
elapsedNanos,
|
|
193
|
+
nanosPerOperation: operations === 0 ? 0 : elapsedNanos / operations
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { ISampleStats } from './bench-types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Descriptive statistics over a set of measured rounds.
|
|
5
|
+
*
|
|
6
|
+
* min matters as much as median here. A round can only ever be made slower by
|
|
7
|
+
* interference - a collection landing mid round, the scheduler taking the core
|
|
8
|
+
* away - so the fastest round is the closest reading to the true cost of the
|
|
9
|
+
* work, while the median tells you what to expect in practice.
|
|
10
|
+
*/
|
|
11
|
+
export function summarise (values: number[]): ISampleStats {
|
|
12
|
+
if (values.length === 0) {
|
|
13
|
+
throw new Error('cannot summarise an empty sample set')
|
|
14
|
+
}
|
|
15
|
+
const sorted = [...values].sort((a, b) => a - b)
|
|
16
|
+
const n = sorted.length
|
|
17
|
+
const mean = sorted.reduce((a, v) => a + v, 0) / n
|
|
18
|
+
const variance = sorted.reduce((a, v) => a + (v - mean) * (v - mean), 0) / n
|
|
19
|
+
const stdDev = Math.sqrt(variance)
|
|
20
|
+
return {
|
|
21
|
+
samples: n,
|
|
22
|
+
min: sorted[0],
|
|
23
|
+
max: sorted[n - 1],
|
|
24
|
+
mean,
|
|
25
|
+
median: median(sorted),
|
|
26
|
+
p95: percentile(sorted, 95),
|
|
27
|
+
p99: percentile(sorted, 99),
|
|
28
|
+
stdDev,
|
|
29
|
+
coefficientOfVariation: mean === 0 ? 0 : stdDev / mean
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param sorted values in ascending order
|
|
35
|
+
*/
|
|
36
|
+
export function median (sorted: number[]): number {
|
|
37
|
+
const n = sorted.length
|
|
38
|
+
if (n === 0) return 0
|
|
39
|
+
const mid = n >> 1
|
|
40
|
+
return n % 2 === 1
|
|
41
|
+
? sorted[mid]
|
|
42
|
+
: (sorted[mid - 1] + sorted[mid]) / 2
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Nearest rank percentile - no interpolation, so every value reported is a
|
|
47
|
+
* reading that actually occurred.
|
|
48
|
+
*
|
|
49
|
+
* @param sorted values in ascending order
|
|
50
|
+
* @param p percentile in the range 0 to 100
|
|
51
|
+
*/
|
|
52
|
+
export function percentile (sorted: number[], p: number): number {
|
|
53
|
+
const n = sorted.length
|
|
54
|
+
if (n === 0) return 0
|
|
55
|
+
const rank = Math.ceil((p / 100) * n)
|
|
56
|
+
const index = Math.min(Math.max(rank - 1, 0), n - 1)
|
|
57
|
+
return sorted[index]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface IComparison {
|
|
61
|
+
/** positive means the candidate took longer than the baseline */
|
|
62
|
+
readonly deltaPercent: number
|
|
63
|
+
/**
|
|
64
|
+
* true when the change is larger than the noise both runs displayed. Below
|
|
65
|
+
* this threshold a difference is not evidence of anything.
|
|
66
|
+
*/
|
|
67
|
+
readonly significant: boolean
|
|
68
|
+
readonly noiseThresholdPercent: number
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Compare a candidate timing against a baseline, using the run to run spread
|
|
73
|
+
* each of them showed to decide whether the difference is real.
|
|
74
|
+
*/
|
|
75
|
+
export function compare (baseline: ISampleStats, candidate: ISampleStats): IComparison {
|
|
76
|
+
const deltaPercent = baseline.median === 0
|
|
77
|
+
? 0
|
|
78
|
+
: ((candidate.median - baseline.median) / baseline.median) * 100
|
|
79
|
+
// both runs contribute their own noise to any difference between them
|
|
80
|
+
const noiseThresholdPercent = (baseline.coefficientOfVariation + candidate.coefficientOfVariation) * 100
|
|
81
|
+
return {
|
|
82
|
+
deltaPercent,
|
|
83
|
+
significant: Math.abs(deltaPercent) > noiseThresholdPercent,
|
|
84
|
+
noiseThresholdPercent
|
|
85
|
+
}
|
|
86
|
+
}
|