quickwin 3.0.1 → 3.0.3

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.
Files changed (163) hide show
  1. package/README.en.md +3 -0
  2. package/README.md +2 -0
  3. package/package.json +2 -3
  4. package/qwin-nowasm-x86.exe +0 -0
  5. package/qwin-nowasm.exe +0 -0
  6. package/qwin-x86.exe +0 -0
  7. package/qwin.exe +0 -0
  8. package/examples/PdfCanvas.js +0 -38
  9. package/examples/PdfCanvas.tsx +0 -54
  10. package/examples/execd/main.js +0 -23379
  11. package/examples/execd/tray.js +0 -192
  12. package/examples/execd/worker.js +0 -122
  13. package/examples/pdf_preview2.js +0 -398
  14. package/examples/pdf_preview2.ts +0 -353
  15. package/examples/pdf_viewer.js +0 -22515
  16. package/examples/pdf_viewer.tsx +0 -228
  17. package/examples/test_gallery.js +0 -18692
  18. package/examples/test_gallery.tsx +0 -291
  19. package/examples/test_react_lazy.js +0 -18560
  20. package/examples/test_react_lazy.tsx +0 -65
  21. package/examples/test_react_listbox.js +0 -18562
  22. package/examples/test_react_listbox.tsx +0 -73
  23. package/examples/test_root_window.js +0 -18532
  24. package/examples/test_root_window.tsx +0 -41
  25. package/examples/test_tab.js +0 -18563
  26. package/examples/test_tab.tsx +0 -77
  27. package/examples/tray_demo.js +0 -86
  28. package/examples/tray_demo.tsx +0 -89
  29. package/test/add.wasm +0 -0
  30. package/test/bench_ffi_callraw.js +0 -235
  31. package/test/bench_ffi_callraw.ts +0 -235
  32. package/test/bench_ffi_overhead.js +0 -139
  33. package/test/bench_ffi_overhead.ts +0 -141
  34. package/test/bench_sendmessage.js +0 -104
  35. package/test/bench_sendmessage.ts +0 -100
  36. package/test/bench_sendmessage_nonnull.js +0 -187
  37. package/test/bench_sendmessage_nonnull.ts +0 -188
  38. package/test/complex.wasm +0 -0
  39. package/test/complex_imports.wasm +0 -0
  40. package/test/debug/http_server_e2e.js +0 -31
  41. package/test/debug/minwasm.js +0 -52
  42. package/test/debug/mupdf_debug.js +0 -86
  43. package/test/debug/wasm.js +0 -122
  44. package/test/debug/wasm_export_debug.js +0 -54
  45. package/test/debug/wasm_names.js +0 -36
  46. package/test/example.pdf +0 -0
  47. package/test/global_imports.wasm +0 -0
  48. package/test/import_func.wasm +0 -0
  49. package/test/imports.wasm +0 -0
  50. package/test/mupdf-cdn.js +0 -40
  51. package/test/mupdf-cdn.ts +0 -43
  52. package/test/run.js +0 -111
  53. package/test/run.ts +0 -116
  54. package/test/sjlj.wasm +0 -0
  55. package/test/test_basic.js +0 -7
  56. package/test/test_basic.ts +0 -9
  57. package/test/test_basic_fetch.js +0 -63
  58. package/test/test_basic_fetch.ts +0 -59
  59. package/test/test_brotli.js +0 -48
  60. package/test/test_brotli.ts +0 -52
  61. package/test/test_fetch_cache.js +0 -169
  62. package/test/test_fetch_cache.ts +0 -187
  63. package/test/test_ffi.js +0 -157
  64. package/test/test_ffi.ts +0 -170
  65. package/test/test_ffi_abi.js +0 -290
  66. package/test/test_ffi_abi.ts +0 -274
  67. package/test/test_ffi_array.js +0 -116
  68. package/test/test_ffi_array.ts +0 -141
  69. package/test/test_ffi_bind.js +0 -656
  70. package/test/test_ffi_bind.ts +0 -670
  71. package/test/test_ffi_struct.js +0 -779
  72. package/test/test_ffi_struct.ts +0 -828
  73. package/test/test_ffi_struct_layout.js +0 -495
  74. package/test/test_ffi_struct_layout.ts +0 -543
  75. package/test/test_ffi_user32.js +0 -103
  76. package/test/test_ffi_user32.ts +0 -113
  77. package/test/test_ffi_windows.js +0 -172
  78. package/test/test_ffi_windows.ts +0 -187
  79. package/test/test_files.js +0 -102
  80. package/test/test_files.ts +0 -121
  81. package/test/test_frame_encoding.js +0 -128
  82. package/test/test_frame_encoding.ts +0 -132
  83. package/test/test_gdi_draw.js +0 -681
  84. package/test/test_gdi_draw.ts +0 -725
  85. package/test/test_helper.js +0 -91
  86. package/test/test_helper.ts +0 -88
  87. package/test/test_http_import.js +0 -96
  88. package/test/test_http_import.ts +0 -93
  89. package/test/test_http_server.js +0 -214
  90. package/test/test_http_server.ts +0 -211
  91. package/test/test_import_dir.js +0 -4
  92. package/test/test_import_dir.ts +0 -5
  93. package/test/test_ipv6.js +0 -134
  94. package/test/test_ipv6.ts +0 -130
  95. package/test/test_jsx.js +0 -1314
  96. package/test/test_meta_url.js +0 -3
  97. package/test/test_meta_url.ts +0 -4
  98. package/test/test_mupdf_render.js +0 -51
  99. package/test/test_mupdf_render.ts +0 -55
  100. package/test/test_mupdf_twice.js +0 -62
  101. package/test/test_mupdf_twice.ts +0 -65
  102. package/test/test_mupdf_wasm.js +0 -39
  103. package/test/test_mupdf_wasm.ts +0 -32
  104. package/test/test_net_event.js +0 -57
  105. package/test/test_net_event.ts +0 -56
  106. package/test/test_net_fetch.js +0 -384
  107. package/test/test_net_fetch.ts +0 -373
  108. package/test/test_net_websocket.js +0 -180
  109. package/test/test_net_websocket.ts +0 -164
  110. package/test/test_polyfill.js +0 -144
  111. package/test/test_polyfill.ts +0 -159
  112. package/test/test_printer.js +0 -137
  113. package/test/test_printer.ts +0 -148
  114. package/test/test_proc.js +0 -141
  115. package/test/test_proc.ts +0 -163
  116. package/test/test_react_complex.js +0 -18678
  117. package/test/test_react_counter.js +0 -18555
  118. package/test/test_react_flex.js +0 -18871
  119. package/test/test_react_hidden.js +0 -18560
  120. package/test/test_react_listview_link.js +0 -18590
  121. package/test/test_react_notify.js +0 -18797
  122. package/test/test_react_render.js +0 -18550
  123. package/test/test_registry.js +0 -74
  124. package/test/test_registry.ts +0 -95
  125. package/test/test_sysinfo.js +0 -122
  126. package/test/test_sysinfo.ts +0 -141
  127. package/test/test_url.js +0 -173
  128. package/test/test_url.ts +0 -183
  129. package/test/test_wasm_basic.js +0 -85
  130. package/test/test_wasm_basic.ts +0 -73
  131. package/test/test_wasm_import_global.js +0 -41
  132. package/test/test_wasm_import_global.ts +0 -45
  133. package/test/test_wasm_sjlj.js +0 -145
  134. package/test/test_wasm_sjlj.ts +0 -125
  135. package/test/test_wasm_types.js +0 -97
  136. package/test/test_wasm_types.ts +0 -124
  137. package/test/test_wolfssl_read.js +0 -203
  138. package/test/test_wolfssl_read.ts +0 -184
  139. package/test/test_worker.js +0 -21
  140. package/test/test_worker.ts +0 -24
  141. package/test/test_worker_concurrent.js +0 -45
  142. package/test/test_worker_concurrent.ts +0 -56
  143. package/test/test_worker_data_url.js +0 -46
  144. package/test/test_worker_data_url.ts +0 -51
  145. package/test/test_worker_fetch.js +0 -26
  146. package/test/test_worker_fetch.ts +0 -27
  147. package/test/test_worker_net.js +0 -69
  148. package/test/test_worker_net.ts +0 -66
  149. package/test/test_worker_wasm_concurrent.js +0 -44
  150. package/test/test_worker_wasm_concurrent.ts +0 -45
  151. package/test/types.wasm +0 -0
  152. package/test/worker_fetch.js +0 -13
  153. package/test/worker_fetch.ts +0 -13
  154. package/test/worker_helper.js +0 -1
  155. package/test/worker_helper.ts +0 -1
  156. package/test/worker_http.js +0 -18
  157. package/test/worker_http.ts +0 -20
  158. package/test/worker_import_lib.js +0 -12
  159. package/test/worker_import_lib.ts +0 -13
  160. package/test/worker_module.js +0 -16
  161. package/test/worker_module.ts +0 -17
  162. package/test/worker_wasm_concurrent.js +0 -22
  163. package/test/worker_wasm_concurrent.ts +0 -22
@@ -1,100 +0,0 @@
1
- // 一次性性能 bench:gui.SendMessage(C 模块直调) vs FFI bind SendMessageW(libffi trampoline)
2
- // 不入 CI。用法:make js 后 POST /exec {"cmd":"qwin.exe test/bench_sendmessage.js"}
3
- // 每场景预热 1 轮 + 5 轮计数(奇偶交叉起跑顺序),取各自最小值降抖动。
4
- // 注意:全部消息发给自有窗口(直调 wndproc)——跨进程消息走消息经纪,几十 μs/次且
5
- // IPC 开销掩盖入口链差异,不作测点。结束必须 DestroyWindow + std.exit:留活窗会让
6
- // qwin 主流程驻留消息循环,exec 永不返回。
7
- import * as std from 'std'
8
- import * as gui from 'gui'
9
- import { SendMessage } from '../lib/windows/user32.js'
10
- import { WCHAR } from '../lib/ffi/bind.js'
11
- import { PtrArrayBuffer } from '../lib/ffi/ctype.js'
12
-
13
- const N = 500_000
14
- // S2 含 WCHAR.encode 每次分配:500k 时 GC 抖动把单轮拖到 190s+(实测 ~380μs/迭代),
15
- // 降到 50k——统计上仍远超噪声需求
16
- const N_S2 = 50_000
17
- const ROUNDS = 5
18
- const ROUNDS_S2 = 3
19
- const WM_SETTEXT = 0x000c
20
- const WM_GETTEXT = 0x000d
21
-
22
- // 文件日志(cwd=Z:\quickwin,宿主机 _build/bench_log.txt 可实时读)——exec 只在进程
23
- // 退出后回包,挂了靠它定位卡点
24
- const log = std.open('bench_log.txt', 'w')
25
- function step(msg: string): void {
26
- std.printf('%s\n', msg)
27
- if (log) { log.puts(msg + '\n'); log.flush() }
28
- }
29
-
30
- step('stage: imports ok')
31
-
32
- const winRaw = gui.CreateWindow('STATIC', 'bench-title', 0, 0, 0, 20, 20, null, null)
33
- if (!winRaw) throw new Error('CreateWindow(STATIC) failed')
34
- const win: gui.HWND = winRaw
35
- step('stage: window created')
36
-
37
- // 两侧入口各自提为常量(循环内属性查找开销对齐)
38
- const gSM = gui.SendMessage
39
- const fSM = SendMessage
40
- const TEXT = 'bench'
41
- const buf = new PtrArrayBuffer<'WCHAR'>(64)
42
-
43
- // —— S1(WM_NULL 纯数字,最纯入口链开销):实现已随首轮跑数移除,数据见 .agents/SENDMESSAGE_BENCH.md ——
44
-
45
- // —— S2:WM_SETTEXT 字符串 lParam(任务 3 多态点的真实成本形态):
46
- // gui 侧 C 内编码;FFI 侧调用点 WCHAR.encode 在循环内(迁移后的真实写法)——
47
- function s2g(): number {
48
- const t0 = Date.now()
49
- for (let i = 0; i < N_S2; i++) gSM(win, WM_SETTEXT, 0, TEXT)
50
- return Date.now() - t0
51
- }
52
- function s2f(): number {
53
- const t0 = Date.now()
54
- for (let i = 0; i < N_S2; i++) {
55
- const w = WCHAR.encode(TEXT)
56
- fSM(win, WM_SETTEXT, 0, w.ptr)
57
- }
58
- return Date.now() - t0
59
- }
60
-
61
- // —— S3:WM_GETTEXT 缓冲出参往返,两侧 JS 实参完全相同(纯入口差 + C 侧拷贝共模)——
62
- function s3g(): number {
63
- const t0 = Date.now()
64
- for (let i = 0; i < N; i++) gSM(win, WM_GETTEXT, 64, buf.ptr)
65
- return Date.now() - t0
66
- }
67
- function s3f(): number {
68
- const t0 = Date.now()
69
- for (let i = 0; i < N; i++) fSM(win, WM_GETTEXT, 64, buf.ptr)
70
- return Date.now() - t0
71
- }
72
-
73
- function suite(name: string, n: number, rounds: number, rg: () => number, rf: () => number): void {
74
- step('suite: ' + name + ' warmup')
75
- rg(); rf() // 预热各 1 轮
76
- const gs: number[] = []
77
- const fs: number[] = []
78
- for (let r = 0; r < rounds; r++) {
79
- // 奇偶交叉起跑顺序,抵消顺序性偏置(温度/调度漂移)
80
- if (r % 2 === 0) { gs.push(rg()); fs.push(rf()) }
81
- else { fs.push(rf()); gs.push(rg()) }
82
- step(' round ' + String(r) + ': gui=' + String(gs[gs.length - 1]) +
83
- 'ms ffi=' + String(fs[fs.length - 1]) + 'ms')
84
- }
85
- const gmin = Math.min(...gs), fmin = Math.min(...fs)
86
- const ns = (ms: number) => (ms * 1e6 / n).toFixed(0)
87
- step(' ==> gui min=' + String(gmin) + 'ms (' + ns(gmin) + ' ns/call), ffi min=' +
88
- String(fmin) + 'ms (' + ns(fmin) + ' ns/call), ffi/gui=' + (fmin / gmin).toFixed(2) +
89
- 'x, delta=' + ns(fmin - gmin) + 'ns')
90
- }
91
-
92
- step('bench_sendmessage: round2 (S1 done in round1), S2 n=' + String(N_S2) +
93
- '/' + String(ROUNDS_S2) + 'r, S3 n=' + String(N) + '/' + String(ROUNDS) + 'r (min)')
94
- suite('S2 WM_SETTEXT 字符串 lParam (FFI 侧含 WCHAR.encode)', N_S2, ROUNDS_S2, s2g, s2f)
95
- suite('S3 WM_GETTEXT 缓冲出参往返 (两侧实参相同)', N, ROUNDS, s3g, s3f)
96
-
97
- gui.DestroyWindow(win)
98
- step('DONE')
99
- if (log) log.close()
100
- std.exit(0)
@@ -1,187 +0,0 @@
1
- // 一次性性能 bench:FFI 签名 '!' 标注的调用时 0 检验成本(同函数多臂对照 + A/A 噪声底)
2
- // 不入 CI。用法:make js 后 POST /exec {"cmd":"qwin.exe test/bench_sendmessage_nonnull.js"}
3
- // 臂(同一 proc 各自 bind,唯一变量是 '!'):
4
- // A1 base '<HWND>ptr u32 <>ptr <>ptr -> <>ptr' 基线
5
- // A2 ctrl '<HWND>ptr u32 <>ptr <>ptr -> <>ptr' 与 A1 完全同签名——两臂差值 = 机器噪声底
6
- // B arg! '<HWND>ptr! u32 <>ptr <>ptr -> <>ptr' + 参数位 arg#1 0 检查
7
- // C ret! '<HWND>ptr! u32 <>ptr <>ptr -> <>ptr!' + 返回位 0 检查(仅非零返回场景)
8
- // 方法学(前两轮教训):批量缩到 50k/样本 → 臂序每 ~650ms 轮转一次,抵消 QEMU 调度抖动;
9
- // S1/S3 各臂 30 样本取 median(对 GC/调度离群稳健);A2-A1 是效应检验的参照带——
10
- // B/A 落在 A2/A 带内即「低于噪声底,不可测」。'!' 边际 = 每标注位 1 次整数 === 比较
11
- // (endsWith 无条件跑、所有臂共模),理论 ≪ 轮噪声。
12
- // C 臂标了返回 '!',撞真实 0 即 throw —— 先用 A1 探各场景原始返回值,非零才让 C 进。
13
- // 全部消息发自有窗口(直调 wndproc)。结束必须 DestroyWindow + std.exit。
14
- import * as std from 'std';
15
- import * as gui from 'gui';
16
- import { bind, WCHAR } from '../lib/ffi/bind.js';
17
- import { PtrArrayBuffer } from '../lib/ffi/ctype.js';
18
- const BATCH = 50_000; // 每样本批量(~650ms)——小批量快轮转,去相关调度抖动
19
- const ROUNDS = 30; // S1/S3 每臂样本数(30 × 3-4 臂 ≈ 60-80s/suite)
20
- const N_S2 = 50_000;
21
- const ROUNDS_S2 = 7;
22
- const WM_NULL = 0x0000;
23
- const WM_SETTEXT = 0x000c;
24
- const WM_GETTEXT = 0x000d;
25
- // median(离群稳健)供 micro 与 suite 共用
26
- const med = (t) => {
27
- const s = [...t].sort((x, y) => x - y);
28
- const m = s.length >> 1;
29
- return s.length % 2 ? s[m] : (s[m - 1] + s[m]) / 2;
30
- };
31
- // S0:'!' 边际操作本身(1 次整数 === 比较 + 分支)的解释器成本——FFI 层噪声
32
- // ±400~900ns/call 盖住了 12.5μs 调用上的 ns 级效应,这里绕开 ffiCall 直接量单检查。
33
- // flag 必须是模块级可变量:QuickJS 编译期会常量折叠字面量,折叠掉就测不到了。
34
- let microFlag = true;
35
- const MICRO_N = 100_000_000;
36
- function microLoop() {
37
- const t0 = Date.now();
38
- let hits = 0;
39
- for (let i = 0; i < MICRO_N; i++)
40
- if (microFlag && i === 0)
41
- hits++;
42
- const dt = Date.now() - t0;
43
- if (hits !== (microFlag ? 1 : 0))
44
- throw new Error('micro sanity failed: hits=' + hits);
45
- return dt;
46
- }
47
- function microSuite() {
48
- step('suite: S0 micro 0-check interpreter cost (n=' + String(MICRO_N) + ')');
49
- microFlag = true;
50
- microLoop();
51
- microFlag = false;
52
- microLoop();
53
- const ts = [];
54
- const fs = [];
55
- for (let r = 0; r < 5; r++) {
56
- // 奇偶交叉起跑顺序(同原 bench 方法)
57
- if (r % 2 === 0) {
58
- microFlag = true;
59
- ts.push(microLoop());
60
- microFlag = false;
61
- fs.push(microLoop());
62
- }
63
- else {
64
- microFlag = false;
65
- fs.push(microLoop());
66
- microFlag = true;
67
- ts.push(microLoop());
68
- }
69
- step(' round ' + String(r) + ': check=' + String(ts[ts.length - 1]) +
70
- 'ms base=' + String(fs[fs.length - 1]) + 'ms');
71
- }
72
- const ns = (ms) => (ms * 1e6 / MICRO_N).toFixed(2);
73
- const tmed = med(ts), fmed = med(fs);
74
- step(' ** median: check=' + ns(tmed) + 'ns/op base=' + ns(fmed) +
75
- 'ns/op ==> per-check cost=' + ((tmed - fmed) * 1e6 / MICRO_N).toFixed(2) + 'ns');
76
- }
77
- // 文件日志(cwd=Z:\quickwin,宿主机 _build/bench_nonnull_log.txt 可实时读)——exec 只在
78
- // 进程退出后回包,挂了靠它定位卡点
79
- const log = std.open('bench_nonnull_log.txt', 'w');
80
- function step(msg) {
81
- std.printf('%s\n', msg);
82
- if (log) {
83
- log.puts(msg + '\n');
84
- log.flush();
85
- }
86
- }
87
- step('stage: imports ok');
88
- const winRaw = gui.CreateWindow('STATIC', 'bench-title', 0, 0, 0, 20, 20, null, null);
89
- if (!winRaw)
90
- throw new Error('CreateWindow(STATIC) failed');
91
- const win = winRaw;
92
- step('stage: window created');
93
- const SIG = '<HWND>ptr u32 <>ptr <>ptr -> <>ptr';
94
- const fA1 = bind('user32.dll', 'SendMessageW', SIG);
95
- const fA2 = bind('user32.dll', 'SendMessageW', SIG); // A/A 对照:同签名二绑 = 噪声底
96
- const fB = bind('user32.dll', 'SendMessageW', '<HWND>ptr! u32 <>ptr <>ptr -> <>ptr');
97
- const fC = bind('user32.dll', 'SendMessageW', '<HWND>ptr! u32 <>ptr <>ptr -> <>ptr!');
98
- const TEXT = 'bench';
99
- const buf = new PtrArrayBuffer(64);
100
- // C 臂可行性探针:A1 读各场景原始返回值,撞 0 的场景不进 C('!' 会 throw)。
101
- // S1 的 WM_NULL 走 DefWindowProc 预期返 0 → C 不进 S1 是预期而非缺测。
102
- const retS1 = fA1(win, WM_NULL, 0, 0x1234);
103
- const retS2 = fA1(win, WM_SETTEXT, 0, WCHAR.encode(TEXT).ptr);
104
- const retS3 = fA1(win, WM_GETTEXT, 64, buf.ptr);
105
- step('probe ret: S1=' + String(retS1) + ' S2=' + String(retS2) + ' S3=' + String(retS3) +
106
- ' (C arm only enters nonzero scenes)');
107
- function run1(f) {
108
- const t0 = Date.now();
109
- for (let i = 0; i < BATCH; i++)
110
- f(win, WM_NULL, 0, 0x1234);
111
- return Date.now() - t0;
112
- }
113
- function run2(f) {
114
- const t0 = Date.now();
115
- for (let i = 0; i < N_S2; i++) {
116
- const w = WCHAR.encode(TEXT);
117
- f(win, WM_SETTEXT, 0, w.ptr);
118
- }
119
- return Date.now() - t0;
120
- }
121
- function run3(f) {
122
- const t0 = Date.now();
123
- for (let i = 0; i < BATCH; i++)
124
- f(win, WM_GETTEXT, 64, buf.ptr);
125
- return Date.now() - t0;
126
- }
127
- function suite(name, n, rounds, arms) {
128
- step('suite: ' + name + ' warmup');
129
- for (const a of arms)
130
- a.fn(); // 预热各 1 轮
131
- const times = arms.map(() => []);
132
- for (let r = 0; r < rounds; r++) {
133
- // 臂序按 r 轮转起跑,抵消顺序性偏置(温度/调度漂移)
134
- for (let k = 0; k < arms.length; k++) {
135
- const i = (r + k) % arms.length;
136
- times[i].push(arms[i].fn());
137
- }
138
- }
139
- // 每轮原始数据只 dump 前 3 轮(30 轮全打没意义,log 只留诊断线索)
140
- for (let r = 0; r < Math.min(rounds, 3); r++) {
141
- step(' round ' + String(r) + ': ' +
142
- arms.map((a, i) => a.label + '=' + String(times[i][r]) + 'ms').join(' '));
143
- }
144
- const ns = (ms) => (ms * 1e6 / n).toFixed(1);
145
- // median 是主指标(离群稳健);min 留作与前两轮/原 bench 的可比项
146
- const meds = times.map(med);
147
- const mins = times.map(t => Math.min(...t));
148
- step(' ** median: ' + arms.map((a, i) => a.label + '=' + ns(meds[i]) + 'ns').join(' | '));
149
- step(' min: ' + arms.map((a, i) => a.label + '=' + ns(mins[i]) + 'ns').join(' | '));
150
- for (let i = 1; i < arms.length; i++)
151
- step(' vs A1: ' + arms[i].label + ' delta=' +
152
- ((meds[i] - meds[0]) * 1e6 / n).toFixed(1) + 'ns/call (median), ' +
153
- ((mins[i] - mins[0]) * 1e6 / n).toFixed(1) + 'ns/call (min)' +
154
- (i === 1 ? ' <-- A2-A1 = NOISE FLOOR' : ''));
155
- }
156
- const s1Arms = [
157
- { label: 'A1', fn: () => run1(fA1) },
158
- { label: 'A2', fn: () => run1(fA2) },
159
- { label: 'B ', fn: () => run1(fB) },
160
- ];
161
- if (retS1 !== 0)
162
- s1Arms.push({ label: 'C ', fn: () => run1(fC) });
163
- const s2Arms = [
164
- { label: 'A1', fn: () => run2(fA1) },
165
- { label: 'A2', fn: () => run2(fA2) },
166
- { label: 'B ', fn: () => run2(fB) },
167
- ];
168
- if (retS2 !== 0)
169
- s2Arms.push({ label: 'C ', fn: () => run2(fC) });
170
- const s3Arms = [
171
- { label: 'A1', fn: () => run3(fA1) },
172
- { label: 'A2', fn: () => run3(fA2) },
173
- { label: 'B ', fn: () => run3(fB) },
174
- ];
175
- if (retS3 !== 0)
176
- s3Arms.push({ label: 'C ', fn: () => run3(fC) });
177
- step('bench_sendmessage_nonnull: S1/S3 batch=' + String(BATCH) + '×' + String(ROUNDS) +
178
- 'r, S2 n=' + String(N_S2) + '×' + String(ROUNDS_S2) + 'r (median 主指标)');
179
- microSuite();
180
- suite('S1 WM_NULL 纯数字参数 (最灵敏测点)', BATCH, ROUNDS, s1Arms);
181
- suite('S2 WM_SETTEXT 字符串 lParam (FFI 侧含 WCHAR.encode)', N_S2, ROUNDS_S2, s2Arms);
182
- suite('S3 WM_GETTEXT 缓冲出参往返', BATCH, ROUNDS, s3Arms);
183
- gui.DestroyWindow(win);
184
- step('DONE');
185
- if (log)
186
- log.close();
187
- std.exit(0);
@@ -1,188 +0,0 @@
1
- // 一次性性能 bench:FFI 签名 '!' 标注的调用时 0 检验成本(同函数多臂对照 + A/A 噪声底)
2
- // 不入 CI。用法:make js 后 POST /exec {"cmd":"qwin.exe test/bench_sendmessage_nonnull.js"}
3
- // 臂(同一 proc 各自 bind,唯一变量是 '!'):
4
- // A1 base '<HWND>ptr u32 <>ptr <>ptr -> <>ptr' 基线
5
- // A2 ctrl '<HWND>ptr u32 <>ptr <>ptr -> <>ptr' 与 A1 完全同签名——两臂差值 = 机器噪声底
6
- // B arg! '<HWND>ptr! u32 <>ptr <>ptr -> <>ptr' + 参数位 arg#1 0 检查
7
- // C ret! '<HWND>ptr! u32 <>ptr <>ptr -> <>ptr!' + 返回位 0 检查(仅非零返回场景)
8
- // 方法学(前两轮教训):批量缩到 50k/样本 → 臂序每 ~650ms 轮转一次,抵消 QEMU 调度抖动;
9
- // S1/S3 各臂 30 样本取 median(对 GC/调度离群稳健);A2-A1 是效应检验的参照带——
10
- // B/A 落在 A2/A 带内即「低于噪声底,不可测」。'!' 边际 = 每标注位 1 次整数 === 比较
11
- // (endsWith 无条件跑、所有臂共模),理论 ≪ 轮噪声。
12
- // C 臂标了返回 '!',撞真实 0 即 throw —— 先用 A1 探各场景原始返回值,非零才让 C 进。
13
- // 全部消息发自有窗口(直调 wndproc)。结束必须 DestroyWindow + std.exit。
14
- import * as std from 'std'
15
- import * as gui from 'gui'
16
- import { bind, WCHAR } from '../lib/ffi/bind.js'
17
- import { PtrArrayBuffer } from '../lib/ffi/ctype.js'
18
-
19
- const BATCH = 50_000 // 每样本批量(~650ms)——小批量快轮转,去相关调度抖动
20
- const ROUNDS = 30 // S1/S3 每臂样本数(30 × 3-4 臂 ≈ 60-80s/suite)
21
- const N_S2 = 50_000
22
- const ROUNDS_S2 = 7
23
- const WM_NULL = 0x0000
24
- const WM_SETTEXT = 0x000c
25
- const WM_GETTEXT = 0x000d
26
-
27
- // median(离群稳健)供 micro 与 suite 共用
28
- const med = (t: number[]): number => {
29
- const s = [...t].sort((x, y) => x - y)
30
- const m = s.length >> 1
31
- return s.length % 2 ? s[m]! : (s[m - 1]! + s[m]!) / 2
32
- }
33
-
34
- // S0:'!' 边际操作本身(1 次整数 === 比较 + 分支)的解释器成本——FFI 层噪声
35
- // ±400~900ns/call 盖住了 12.5μs 调用上的 ns 级效应,这里绕开 ffiCall 直接量单检查。
36
- // flag 必须是模块级可变量:QuickJS 编译期会常量折叠字面量,折叠掉就测不到了。
37
- let microFlag = true
38
- const MICRO_N = 100_000_000
39
- function microLoop(): number {
40
- const t0 = Date.now()
41
- let hits = 0
42
- for (let i = 0; i < MICRO_N; i++)
43
- if (microFlag && i === 0) hits++
44
- const dt = Date.now() - t0
45
- if (hits !== (microFlag ? 1 : 0)) throw new Error('micro sanity failed: hits=' + hits)
46
- return dt
47
- }
48
- function microSuite(): void {
49
- step('suite: S0 micro 0-check interpreter cost (n=' + String(MICRO_N) + ')')
50
- microFlag = true; microLoop()
51
- microFlag = false; microLoop()
52
- const ts: number[] = []
53
- const fs: number[] = []
54
- for (let r = 0; r < 5; r++) {
55
- // 奇偶交叉起跑顺序(同原 bench 方法)
56
- if (r % 2 === 0) {
57
- microFlag = true; ts.push(microLoop())
58
- microFlag = false; fs.push(microLoop())
59
- } else {
60
- microFlag = false; fs.push(microLoop())
61
- microFlag = true; ts.push(microLoop())
62
- }
63
- step(' round ' + String(r) + ': check=' + String(ts[ts.length - 1]) +
64
- 'ms base=' + String(fs[fs.length - 1]) + 'ms')
65
- }
66
- const ns = (ms: number) => (ms * 1e6 / MICRO_N).toFixed(2)
67
- const tmed = med(ts), fmed = med(fs)
68
- step(' ** median: check=' + ns(tmed) + 'ns/op base=' + ns(fmed) +
69
- 'ns/op ==> per-check cost=' + ((tmed - fmed) * 1e6 / MICRO_N).toFixed(2) + 'ns')
70
- }
71
-
72
- // 文件日志(cwd=Z:\quickwin,宿主机 _build/bench_nonnull_log.txt 可实时读)——exec 只在
73
- // 进程退出后回包,挂了靠它定位卡点
74
- const log = std.open('bench_nonnull_log.txt', 'w')
75
- function step(msg: string): void {
76
- std.printf('%s\n', msg)
77
- if (log) { log.puts(msg + '\n'); log.flush() }
78
- }
79
-
80
- step('stage: imports ok')
81
-
82
- const winRaw = gui.CreateWindow('STATIC', 'bench-title', 0, 0, 0, 20, 20, null, null)
83
- if (!winRaw) throw new Error('CreateWindow(STATIC) failed')
84
- const win: gui.HWND = winRaw
85
- step('stage: window created')
86
-
87
- const SIG = '<HWND>ptr u32 <>ptr <>ptr -> <>ptr'
88
- const fA1 = bind('user32.dll', 'SendMessageW', SIG)
89
- const fA2 = bind('user32.dll', 'SendMessageW', SIG) // A/A 对照:同签名二绑 = 噪声底
90
- const fB = bind('user32.dll', 'SendMessageW', '<HWND>ptr! u32 <>ptr <>ptr -> <>ptr')
91
- const fC = bind('user32.dll', 'SendMessageW', '<HWND>ptr! u32 <>ptr <>ptr -> <>ptr!')
92
-
93
- // 各 BindFn 结构等价 → 收敛到一个可传参的函数型(循环内统一走局部参数,开销对齐)
94
- type SM = (h: gui.HWND, msg: number, w: number, l: number) => number
95
-
96
- const TEXT = 'bench'
97
- const buf = new PtrArrayBuffer<'WCHAR'>(64)
98
-
99
- // C 臂可行性探针:A1 读各场景原始返回值,撞 0 的场景不进 C('!' 会 throw)。
100
- // S1 的 WM_NULL 走 DefWindowProc 预期返 0 → C 不进 S1 是预期而非缺测。
101
- const retS1 = fA1(win, WM_NULL, 0, 0x1234)
102
- const retS2 = fA1(win, WM_SETTEXT, 0, WCHAR.encode(TEXT).ptr)
103
- const retS3 = fA1(win, WM_GETTEXT, 64, buf.ptr)
104
- step('probe ret: S1=' + String(retS1) + ' S2=' + String(retS2) + ' S3=' + String(retS3) +
105
- ' (C arm only enters nonzero scenes)')
106
-
107
- function run1(f: SM): number {
108
- const t0 = Date.now()
109
- for (let i = 0; i < BATCH; i++) f(win, WM_NULL, 0, 0x1234)
110
- return Date.now() - t0
111
- }
112
- function run2(f: SM): number {
113
- const t0 = Date.now()
114
- for (let i = 0; i < N_S2; i++) {
115
- const w = WCHAR.encode(TEXT)
116
- f(win, WM_SETTEXT, 0, w.ptr)
117
- }
118
- return Date.now() - t0
119
- }
120
- function run3(f: SM): number {
121
- const t0 = Date.now()
122
- for (let i = 0; i < BATCH; i++) f(win, WM_GETTEXT, 64, buf.ptr)
123
- return Date.now() - t0
124
- }
125
-
126
- type Arm = { label: string; fn: () => number }
127
-
128
- function suite(name: string, n: number, rounds: number, arms: Arm[]): void {
129
- step('suite: ' + name + ' warmup')
130
- for (const a of arms) a.fn() // 预热各 1 轮
131
- const times: number[][] = arms.map(() => [])
132
- for (let r = 0; r < rounds; r++) {
133
- // 臂序按 r 轮转起跑,抵消顺序性偏置(温度/调度漂移)
134
- for (let k = 0; k < arms.length; k++) {
135
- const i = (r + k) % arms.length
136
- times[i]!.push(arms[i]!.fn())
137
- }
138
- }
139
- // 每轮原始数据只 dump 前 3 轮(30 轮全打没意义,log 只留诊断线索)
140
- for (let r = 0; r < Math.min(rounds, 3); r++) {
141
- step(' round ' + String(r) + ': ' +
142
- arms.map((a, i) => a.label + '=' + String(times[i]![r]) + 'ms').join(' '))
143
- }
144
- const ns = (ms: number) => (ms * 1e6 / n).toFixed(1)
145
- // median 是主指标(离群稳健);min 留作与前两轮/原 bench 的可比项
146
- const meds = times.map(med)
147
- const mins = times.map(t => Math.min(...t))
148
- step(' ** median: ' + arms.map((a, i) =>
149
- a.label + '=' + ns(meds[i]!) + 'ns').join(' | '))
150
- step(' min: ' + arms.map((a, i) =>
151
- a.label + '=' + ns(mins[i]!) + 'ns').join(' | '))
152
- for (let i = 1; i < arms.length; i++)
153
- step(' vs A1: ' + arms[i]!.label + ' delta=' +
154
- ((meds[i]! - meds[0]!) * 1e6 / n).toFixed(1) + 'ns/call (median), ' +
155
- ((mins[i]! - mins[0]!) * 1e6 / n).toFixed(1) + 'ns/call (min)' +
156
- (i === 1 ? ' <-- A2-A1 = NOISE FLOOR' : ''))
157
- }
158
-
159
- const s1Arms: Arm[] = [
160
- { label: 'A1', fn: () => run1(fA1) },
161
- { label: 'A2', fn: () => run1(fA2) },
162
- { label: 'B ', fn: () => run1(fB) },
163
- ]
164
- if (retS1 !== 0) s1Arms.push({ label: 'C ', fn: () => run1(fC) })
165
- const s2Arms: Arm[] = [
166
- { label: 'A1', fn: () => run2(fA1) },
167
- { label: 'A2', fn: () => run2(fA2) },
168
- { label: 'B ', fn: () => run2(fB) },
169
- ]
170
- if (retS2 !== 0) s2Arms.push({ label: 'C ', fn: () => run2(fC) })
171
- const s3Arms: Arm[] = [
172
- { label: 'A1', fn: () => run3(fA1) },
173
- { label: 'A2', fn: () => run3(fA2) },
174
- { label: 'B ', fn: () => run3(fB) },
175
- ]
176
- if (retS3 !== 0) s3Arms.push({ label: 'C ', fn: () => run3(fC) })
177
-
178
- step('bench_sendmessage_nonnull: S1/S3 batch=' + String(BATCH) + '×' + String(ROUNDS) +
179
- 'r, S2 n=' + String(N_S2) + '×' + String(ROUNDS_S2) + 'r (median 主指标)')
180
- microSuite()
181
- suite('S1 WM_NULL 纯数字参数 (最灵敏测点)', BATCH, ROUNDS, s1Arms)
182
- suite('S2 WM_SETTEXT 字符串 lParam (FFI 侧含 WCHAR.encode)', N_S2, ROUNDS_S2, s2Arms)
183
- suite('S3 WM_GETTEXT 缓冲出参往返', BATCH, ROUNDS, s3Arms)
184
-
185
- gui.DestroyWindow(win)
186
- step('DONE')
187
- if (log) log.close()
188
- std.exit(0)
package/test/complex.wasm DELETED
Binary file
Binary file
@@ -1,31 +0,0 @@
1
- import '../../lib/fetch.js';
2
- import { createServer } from '../../lib/http-server.js';
3
- import * as std from 'std';
4
- const server = createServer(async (req) => {
5
- const url = new URL(req.url);
6
- if (url.pathname === '/echo') {
7
- return new Response(JSON.stringify({ method: req.method, path: url.pathname, body: await req.text(), q: Object.fromEntries(url.searchParams) }), { headers: { 'Content-Type': 'application/json' } });
8
- }
9
- return new Response('hello from quickwin http-server', { headers: { 'Content-Type': 'text/plain' } });
10
- });
11
- const rc = server.listen(0, '127.0.0.1');
12
- if (rc !== 0) {
13
- console.log('listen failed');
14
- std.exit(1);
15
- }
16
- const addr = server.address();
17
- const port = addr.port;
18
- console.log('server on port', port);
19
- async function main() {
20
- const r1 = await fetch(`http://127.0.0.1:${port}/`);
21
- console.log('GET / status', r1.status, 'body:', await r1.text());
22
- const r2 = await fetch(`http://127.0.0.1:${port}/echo?x=1`, {
23
- method: 'POST',
24
- headers: { 'Content-Type': 'text/plain' },
25
- body: 'hello-body',
26
- });
27
- console.log('POST /echo status', r2.status, 'body:', await r2.text());
28
- server.close();
29
- std.exit(0);
30
- }
31
- main();
@@ -1,52 +0,0 @@
1
- import * as std from 'std';
2
- std.printf("Hello\n");
3
- function readWasmFile(path) {
4
- const fp = std.open(path, 'rb');
5
- if (!fp) {
6
- return null;
7
- }
8
- fp.seek(0, 2); // SEEK_END
9
- const size = fp.tell();
10
- fp.seek(0, 0); // SEEK_SET
11
- const buffer = new ArrayBuffer(size);
12
- fp.read(buffer, 0, size);
13
- fp.close();
14
- return buffer;
15
- }
16
- const wasmBuf = readWasmFile('./complex_imports.wasm');
17
- try {
18
- if (!wasmBuf)
19
- throw new Error('failed to load wasm');
20
- const m = new WebAssembly.Module(wasmBuf);
21
- console.log('imports:', WebAssembly.Module.imports(m));
22
- console.log('exports:', WebAssembly.Module.exports(m));
23
- const inst = new WebAssembly.Instance(m, {
24
- env: {
25
- log_i32: (x) => { console.log(' log_i32:', x); },
26
- log_i64: (x) => { console.log(' log_i64:', x); },
27
- log_f32: (x) => { console.log(' log_f32:', x); },
28
- log_f64: (x) => { console.log(' log_f64:', x); },
29
- op_i32: (a, b, c) => a * b + c,
30
- }
31
- });
32
- const exp = inst.exports;
33
- console.log('--- i32 ---');
34
- let r = exp.run_i32(7);
35
- console.log(' run_i32(7) =>', r, '(expected 75)');
36
- console.log('--- i64 ---');
37
- r = exp.run_i64(100000, 200000);
38
- console.log(' run_i64(100000, 200000) =>', r, '(expected 300000)');
39
- r = exp.run_i64(10000000000, 20000000000);
40
- console.log(' run_i64(10000000000, 20000000000) =>', r, '(expected 30000000000)');
41
- console.log('--- f32 ---');
42
- r = exp.run_f32(3.5);
43
- console.log(' run_f32(3.5) =>', r, '(expected 7.0)');
44
- console.log('--- f64 ---');
45
- r = exp.run_f64(2.5, 4.0);
46
- console.log(' run_f64(2.5, 4.0) =>', r, '(expected 10.0)');
47
- console.log('--- all done ---');
48
- }
49
- catch (e) {
50
- console.log('ERROR:', e);
51
- }
52
- ;
@@ -1,86 +0,0 @@
1
- import * as std from 'std';
2
- import { MUPDF_JS_URL, preloadMupdfWasm } from '../mupdf-cdn.js';
3
- await preloadMupdfWasm();
4
- await import(MUPDF_JS_URL);
5
- // Debug the instance exports
6
- const inst = globalThis["$wasm_inst"];
7
- const mod = globalThis["$wasm_mod"];
8
- console.log('=== WASM Module exports (first 30) ===');
9
- const modExports = WebAssembly.Module.exports(mod);
10
- for (let i = 0; i < 30 && i < modExports.length; i++)
11
- console.log(` ${i}: ${modExports[i].name} (${modExports[i].kind})`);
12
- // Find Sf and Tf indices
13
- let sf_idx = -1, tf_idx = -1;
14
- for (let i = 0; i < modExports.length; i++) {
15
- if (modExports[i].name === 'Sf')
16
- sf_idx = i;
17
- if (modExports[i].name === 'Tf')
18
- tf_idx = i;
19
- }
20
- console.log('Sf index:', sf_idx, 'Tf index:', tf_idx);
21
- console.log('instance exports Sf:', typeof inst.exports['Sf']);
22
- console.log('instance exports Tf:', typeof inst.exports['Tf']);
23
- // Load a simple WASM to verify bridge works
24
- const fp = std.open('./test/example.pdf', 'rb');
25
- if (!fp) {
26
- console.log('cannot open example.pdf');
27
- std.exit(1);
28
- }
29
- fp.seek(0, 2);
30
- const size = fp.tell();
31
- fp.seek(0, 0);
32
- const buf = new ArrayBuffer(size);
33
- fp.read(buf, 0, size);
34
- fp.close();
35
- console.log('\npdf size:', size);
36
- // Try calling lower-level functions directly
37
- const lib = globalThis["$libmupdf_wasm_Module"];
38
- // First open the document
39
- const wasmMemory = inst.exports['cb'];
40
- console.log('WASM memory:', typeof wasmMemory, wasmMemory?.buffer?.byteLength);
41
- // Create a buffer and try
42
- const bufPtr = lib._wasm_malloc(size);
43
- console.log('malloc buffer pointer:', bufPtr);
44
- // Write PDF data into WASM memory at bufPtr...
45
- const mem8 = new Uint8Array(wasmMemory.buffer);
46
- for (let i = 0; i < size; i++)
47
- mem8[bufPtr + i] = (new Uint8Array(buf))[i];
48
- console.log('wrote pdf data to WASM memory at', bufPtr);
49
- // Try opening document directly with _wasm_open_document_with_buffer
50
- // Need a string for "application/pdf"
51
- const magicStr = "application/pdf\x00";
52
- const magicPtr = lib._wasm_malloc(magicStr.length);
53
- for (let i = 0; i < magicStr.length; i++)
54
- mem8[magicPtr + i] = magicStr.charCodeAt(i);
55
- console.log('magic string at', magicPtr);
56
- // Create a Buffer wrapper object - need to call Buffer constructor
57
- // Actually let's just call the low-level function directly
58
- const fz_ptr = inst.exports['Mf'](magicPtr, bufPtr); // _wasm_open_document_with_buffer
59
- console.log('_wasm_open_document_with_buffer returned:', fz_ptr, typeof fz_ptr);
60
- if (fz_ptr && typeof fz_ptr === 'number') {
61
- // Try countPages
62
- const count = inst.exports['Sf'](fz_ptr);
63
- console.log('_wasm_count_pages returned:', count, typeof count);
64
- // Try loadPage
65
- if (typeof fz_ptr !== 'number') {
66
- console.log('fz_ptr is not a number!');
67
- }
68
- const pagePtr = inst.exports['Tf'](fz_ptr, 0);
69
- console.log('_wasm_load_page returned:', pagePtr, typeof pagePtr);
70
- // Get bounds
71
- // _wasm_bound_page(Sf?) - need to find this export
72
- for (let i = 0; i < modExports.length; i++) {
73
- if (modExports[i].name.startsWith('Uf')) {
74
- console.log('Uf at index', i, '- likely _wasm_pdf_page_from_fz_page');
75
- }
76
- if (modExports[i].name.startsWith('Vf')) {
77
- console.log('Vf at index', i);
78
- }
79
- }
80
- // Try calling _wasm_pdf_page_from_fz_page (Uf)
81
- const pdfPtr = inst.exports['Uf'](pagePtr);
82
- console.log('_wasm_pdf_page_from_fz_page returned:', pdfPtr, typeof pdfPtr);
83
- }
84
- lib._wasm_free(magicPtr);
85
- lib._wasm_free(bufPtr);
86
- console.log('\ndone');