quickwin 3.0.0 → 3.0.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/README.en.md +3 -0
- package/README.md +2 -0
- package/lib/abort.d.ts +21 -0
- package/lib/assert.d.ts +2 -0
- package/lib/bitmap.d.ts +7 -0
- package/lib/cache-utils.d.ts +6 -0
- package/lib/certs.d.ts +1 -0
- package/lib/dpi.d.ts +9 -0
- package/lib/dumpRects.d.ts +2 -0
- package/lib/exec-log.d.ts +23 -0
- package/lib/fetch.d.ts +4 -0
- package/lib/ffi/bind.d.ts +70 -0
- package/lib/ffi/ctype.d.ts +117 -0
- package/lib/ffi/index.d.ts +2 -0
- package/lib/ffi/struct.d.ts +174 -0
- package/lib/http-common.d.ts +143 -0
- package/lib/http-server.d.ts +59 -0
- package/lib/polyfill.d.ts +22 -0
- package/lib/react-qw/components/Button.d.ts +9 -0
- package/lib/react-qw/components/CheckBox.d.ts +11 -0
- package/lib/react-qw/components/ComboBox.d.ts +11 -0
- package/lib/react-qw/components/DateTimePicker.d.ts +15 -0
- package/lib/react-qw/components/Input.d.ts +15 -0
- package/lib/react-qw/components/Link.d.ts +12 -0
- package/lib/react-qw/components/ListBox.d.ts +13 -0
- package/lib/react-qw/components/ListView.d.ts +37 -0
- package/lib/react-qw/components/PathPicker.d.ts +23 -0
- package/lib/react-qw/components/ProgressBar.d.ts +9 -0
- package/lib/react-qw/components/RadioButton.d.ts +11 -0
- package/lib/react-qw/components/ScrollView.d.ts +9 -0
- package/lib/react-qw/components/Slider.d.ts +12 -0
- package/lib/react-qw/components/Tab.d.ts +14 -0
- package/lib/react-qw/components/Tooltip.d.ts +7 -0
- package/lib/react-qw/components/TreeView.d.ts +18 -0
- package/lib/react-qw/components/index.d.ts +16 -0
- package/lib/react-qw/index.d.ts +24 -0
- package/lib/react-qw/layout.d.ts +29 -0
- package/lib/react-qw/nmhdr.d.ts +1 -0
- package/lib/react-qw/props.d.ts +2 -0
- package/lib/react-qw/reconciler.d.ts +21 -0
- package/lib/stream.d.ts +20 -0
- package/lib/text-codec.d.ts +39 -0
- package/lib/text-measure.d.ts +14 -0
- package/lib/url.d.ts +54 -0
- package/lib/websocket.d.ts +73 -0
- package/lib/windows/{advapi32.ts → advapi32.d.ts} +6 -21
- package/lib/windows/comctl32.d.ts +14 -0
- package/lib/windows/comdlg32.d.ts +4 -0
- package/lib/windows/{gdi32.ts → gdi32.d.ts} +161 -98
- package/lib/windows/{kernel32.ts → kernel32.d.ts} +138 -115
- package/lib/windows/{msimg32.ts → msimg32.d.ts} +11 -14
- package/lib/windows/ntdll.d.ts +5 -0
- package/lib/windows/ole32.d.ts +3 -0
- package/lib/windows/{psapi.ts → psapi.d.ts} +9 -18
- package/lib/windows/{shell32.ts → shell32.d.ts} +25 -24
- package/lib/windows/structs.d.ts +3141 -0
- package/lib/windows/{user32.ts → user32.d.ts} +196 -193
- package/lib/windows/version.d.ts +27 -0
- package/lib/windows/{winspool.ts → winspool.d.ts} +18 -32
- package/package.json +15 -8
- package/qwin-nowasm-x86.exe +0 -0
- package/qwin-nowasm.exe +0 -0
- package/qwin-x86.exe +0 -0
- package/qwin.exe +0 -0
- package/examples/PdfCanvas.js +0 -38
- package/examples/PdfCanvas.tsx +0 -54
- package/examples/execd/main.js +0 -23379
- package/examples/execd/tray.js +0 -192
- package/examples/execd/worker.js +0 -122
- package/examples/pdf_preview2.js +0 -398
- package/examples/pdf_preview2.ts +0 -353
- package/examples/pdf_viewer.js +0 -22515
- package/examples/pdf_viewer.tsx +0 -228
- package/examples/test_gallery.js +0 -18692
- package/examples/test_gallery.tsx +0 -291
- package/examples/test_react_lazy.js +0 -18560
- package/examples/test_react_lazy.tsx +0 -65
- package/examples/test_react_listbox.js +0 -18562
- package/examples/test_react_listbox.tsx +0 -73
- package/examples/test_root_window.js +0 -18532
- package/examples/test_root_window.tsx +0 -41
- package/examples/test_tab.js +0 -18563
- package/examples/test_tab.tsx +0 -77
- package/examples/tray_demo.js +0 -86
- package/examples/tray_demo.tsx +0 -89
- package/lib/abort.ts +0 -69
- package/lib/assert.ts +0 -7
- package/lib/bitmap.ts +0 -39
- package/lib/cache-utils.ts +0 -40
- package/lib/certs.ts +0 -258
- package/lib/dpi.ts +0 -31
- package/lib/dumpRects.ts +0 -34
- package/lib/exec-log.ts +0 -30
- package/lib/fetch.ts +0 -589
- package/lib/ffi/bind.ts +0 -540
- package/lib/ffi/ctype.ts +0 -371
- package/lib/ffi/index.ts +0 -6
- package/lib/ffi/struct.ts +0 -780
- package/lib/http-common.ts +0 -441
- package/lib/http-server.ts +0 -604
- package/lib/polyfill.ts +0 -171
- package/lib/react-qw/components/index.ts +0 -17
- package/lib/react-qw/index.ts +0 -96
- package/lib/react-qw/layout.ts +0 -111
- package/lib/react-qw/nmhdr.ts +0 -7
- package/lib/react-qw/props.ts +0 -41
- package/lib/react-qw/reconciler.ts +0 -379
- package/lib/stream.ts +0 -22
- package/lib/text-codec.ts +0 -171
- package/lib/text-measure.ts +0 -44
- package/lib/url.ts +0 -201
- package/lib/websocket.ts +0 -600
- package/lib/windows/comctl32.ts +0 -20
- package/lib/windows/comdlg32.ts +0 -10
- package/lib/windows/ntdll.ts +0 -11
- package/lib/windows/ole32.ts +0 -9
- package/lib/windows/structs.ts +0 -807
- package/lib/windows/version.ts +0 -35
- package/test/add.wasm +0 -0
- package/test/bench_ffi_callraw.js +0 -235
- package/test/bench_ffi_callraw.ts +0 -235
- package/test/bench_ffi_overhead.js +0 -139
- package/test/bench_ffi_overhead.ts +0 -141
- package/test/bench_sendmessage.js +0 -104
- package/test/bench_sendmessage.ts +0 -100
- package/test/bench_sendmessage_nonnull.js +0 -187
- package/test/bench_sendmessage_nonnull.ts +0 -188
- package/test/complex.wasm +0 -0
- package/test/complex_imports.wasm +0 -0
- package/test/debug/http_server_e2e.js +0 -31
- package/test/debug/minwasm.js +0 -52
- package/test/debug/mupdf_debug.js +0 -86
- package/test/debug/wasm.js +0 -122
- package/test/debug/wasm_export_debug.js +0 -54
- package/test/debug/wasm_names.js +0 -36
- package/test/example.pdf +0 -0
- package/test/global_imports.wasm +0 -0
- package/test/import_func.wasm +0 -0
- package/test/imports.wasm +0 -0
- package/test/mupdf-cdn.js +0 -40
- package/test/mupdf-cdn.ts +0 -43
- package/test/run.js +0 -111
- package/test/run.ts +0 -116
- package/test/sjlj.wasm +0 -0
- package/test/test_basic.js +0 -7
- package/test/test_basic.ts +0 -9
- package/test/test_basic_fetch.js +0 -63
- package/test/test_basic_fetch.ts +0 -59
- package/test/test_brotli.js +0 -48
- package/test/test_brotli.ts +0 -52
- package/test/test_fetch_cache.js +0 -169
- package/test/test_fetch_cache.ts +0 -187
- package/test/test_ffi.js +0 -157
- package/test/test_ffi.ts +0 -170
- package/test/test_ffi_abi.js +0 -290
- package/test/test_ffi_abi.ts +0 -274
- package/test/test_ffi_array.js +0 -116
- package/test/test_ffi_array.ts +0 -141
- package/test/test_ffi_bind.js +0 -656
- package/test/test_ffi_bind.ts +0 -670
- package/test/test_ffi_struct.js +0 -779
- package/test/test_ffi_struct.ts +0 -828
- package/test/test_ffi_struct_layout.js +0 -495
- package/test/test_ffi_struct_layout.ts +0 -543
- package/test/test_ffi_user32.js +0 -103
- package/test/test_ffi_user32.ts +0 -113
- package/test/test_ffi_windows.js +0 -172
- package/test/test_ffi_windows.ts +0 -187
- package/test/test_files.js +0 -102
- package/test/test_files.ts +0 -121
- package/test/test_frame_encoding.js +0 -128
- package/test/test_frame_encoding.ts +0 -132
- package/test/test_gdi_draw.js +0 -681
- package/test/test_gdi_draw.ts +0 -725
- package/test/test_helper.js +0 -91
- package/test/test_helper.ts +0 -88
- package/test/test_http_import.js +0 -96
- package/test/test_http_import.ts +0 -93
- package/test/test_http_server.js +0 -214
- package/test/test_http_server.ts +0 -211
- package/test/test_import_dir.js +0 -4
- package/test/test_import_dir.ts +0 -5
- package/test/test_ipv6.js +0 -134
- package/test/test_ipv6.ts +0 -130
- package/test/test_jsx.js +0 -1314
- package/test/test_meta_url.js +0 -3
- package/test/test_meta_url.ts +0 -4
- package/test/test_mupdf_render.js +0 -51
- package/test/test_mupdf_render.ts +0 -55
- package/test/test_mupdf_twice.js +0 -62
- package/test/test_mupdf_twice.ts +0 -65
- package/test/test_mupdf_wasm.js +0 -39
- package/test/test_mupdf_wasm.ts +0 -32
- package/test/test_net_event.js +0 -57
- package/test/test_net_event.ts +0 -56
- package/test/test_net_fetch.js +0 -384
- package/test/test_net_fetch.ts +0 -373
- package/test/test_net_websocket.js +0 -180
- package/test/test_net_websocket.ts +0 -164
- package/test/test_polyfill.js +0 -144
- package/test/test_polyfill.ts +0 -159
- package/test/test_printer.js +0 -137
- package/test/test_printer.ts +0 -148
- package/test/test_proc.js +0 -141
- package/test/test_proc.ts +0 -163
- package/test/test_react_complex.js +0 -18678
- package/test/test_react_counter.js +0 -18555
- package/test/test_react_flex.js +0 -18871
- package/test/test_react_hidden.js +0 -18560
- package/test/test_react_listview_link.js +0 -18590
- package/test/test_react_notify.js +0 -18797
- package/test/test_react_render.js +0 -18550
- package/test/test_registry.js +0 -74
- package/test/test_registry.ts +0 -95
- package/test/test_sysinfo.js +0 -122
- package/test/test_sysinfo.ts +0 -141
- package/test/test_url.js +0 -173
- package/test/test_url.ts +0 -183
- package/test/test_wasm_basic.js +0 -85
- package/test/test_wasm_basic.ts +0 -73
- package/test/test_wasm_import_global.js +0 -41
- package/test/test_wasm_import_global.ts +0 -45
- package/test/test_wasm_sjlj.js +0 -145
- package/test/test_wasm_sjlj.ts +0 -125
- package/test/test_wasm_types.js +0 -97
- package/test/test_wasm_types.ts +0 -124
- package/test/test_wolfssl_read.js +0 -203
- package/test/test_wolfssl_read.ts +0 -184
- package/test/test_worker.js +0 -21
- package/test/test_worker.ts +0 -24
- package/test/test_worker_concurrent.js +0 -45
- package/test/test_worker_concurrent.ts +0 -56
- package/test/test_worker_data_url.js +0 -46
- package/test/test_worker_data_url.ts +0 -51
- package/test/test_worker_fetch.js +0 -26
- package/test/test_worker_fetch.ts +0 -27
- package/test/test_worker_net.js +0 -69
- package/test/test_worker_net.ts +0 -66
- package/test/test_worker_wasm_concurrent.js +0 -44
- package/test/test_worker_wasm_concurrent.ts +0 -45
- package/test/types.wasm +0 -0
- package/test/worker_fetch.js +0 -13
- package/test/worker_fetch.ts +0 -13
- package/test/worker_helper.js +0 -1
- package/test/worker_helper.ts +0 -1
- package/test/worker_http.js +0 -18
- package/test/worker_http.ts +0 -20
- package/test/worker_import_lib.js +0 -12
- package/test/worker_import_lib.ts +0 -13
- package/test/worker_module.js +0 -16
- package/test/worker_module.ts +0 -17
- package/test/worker_wasm_concurrent.js +0 -22
- package/test/worker_wasm_concurrent.ts +0 -22
package/lib/windows/version.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
// version.dll 绑定(文件版本资源——exe/dll「属性 → 详细信息」的数据;品牌约定见
|
|
2
|
-
// lib/windows/user32.ts 头注释):
|
|
3
|
-
// - 读取三段式:Size 拿缓冲大小与句柄 → Info 读进缓冲 → VerQueryValue 在缓冲内
|
|
4
|
-
// 查询子块(lplpBuffer 回写的指针指向缓冲内部,跟进读用 ffi.readByte 拼值)
|
|
5
|
-
// - 位宽无关:句柄/指针出参槽一律 <BYTE>ptr 收 PAB(4/8) + DataView / os.arch 分支读回
|
|
6
|
-
import { bind, type CodecMap } from '../ffi/bind.js'
|
|
7
|
-
|
|
8
|
-
// dll 名部分应用(工厂转调保泛型 infer,见 lib/windows/user32.ts 头注释)
|
|
9
|
-
const b = <const S extends string, const LE extends CodecMap = {}>(name: string, sig: S, encoders?: LE) =>
|
|
10
|
-
bind('version.dll', name, sig, encoders)
|
|
11
|
-
|
|
12
|
-
/** 取文件版本资源大小
|
|
13
|
-
* @returns 字节数(0 = 文件无版本资源)
|
|
14
|
-
* @param args_0 lptstrFilename 文件路径(禁空)
|
|
15
|
-
* @param args_1 lpdwHandle 出参槽(Info 步的入参句柄,MSDN 恒 0 但仍须传),
|
|
16
|
-
* PtrArrayBuffer(4) + Uint32Array 读回 */
|
|
17
|
-
export const GetFileVersionInfoSize = /*@__PURE__*/ b('GetFileVersionInfoSizeW',
|
|
18
|
-
'<WCHAR>ptr! <BYTE>ptr -> u32')
|
|
19
|
-
/** 读版本资源进缓冲
|
|
20
|
-
* @returns 成功 → 非 0
|
|
21
|
-
* @param args_0 lptstrFilename 文件路径(禁空)
|
|
22
|
-
* @param args_1 dwHandle Size 步读回的句柄(恒 0)
|
|
23
|
-
* @param args_2 dwLen 缓冲字节数(= Size 返回值)
|
|
24
|
-
* @param args_3 lpData 接收缓冲(PtrArrayBuffer(dwLen)) */
|
|
25
|
-
export const GetFileVersionInfo = /*@__PURE__*/ b('GetFileVersionInfoW',
|
|
26
|
-
'<WCHAR>ptr! u32 u32 <BYTE>ptr -> i32')
|
|
27
|
-
/** 在版本资源缓冲内查询子块
|
|
28
|
-
* @returns 成功 → 非 0
|
|
29
|
-
* @param args_0 pBlock Info 步的资源缓冲(PtrArrayBuffer 直传)
|
|
30
|
-
* @param args_1 lpszSubBlock 子块路径(禁空):'\\' = 根块 VS_FIXEDFILEINFO、
|
|
31
|
-
* '\\StringFileInfo\\040904B0\\FileVersion' 等字符串表项
|
|
32
|
-
* @param args_2 lplpBuffer 出参槽(块内偏移指针写回),PtrArrayBuffer(8)
|
|
33
|
-
* @param args_3 puLen 出参槽(块字节数,VS_FIXEDFILEINFO = 52),PtrArrayBuffer(4) */
|
|
34
|
-
export const VerQueryValue = /*@__PURE__*/ b('VerQueryValueW',
|
|
35
|
-
'<BYTE>ptr <WCHAR>ptr! <BYTE>ptr <BYTE>ptr -> i32')
|
package/test/add.wasm
DELETED
|
Binary file
|
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
// 一次性性能 bench(归因场):绕过 callPacked 直调 ffi.ffiCall,把 bind() 调用链拆层。
|
|
2
|
-
// 不入 CI。A 臂 = bind() 同场对照(对齐 bench_ffi_overhead.ts 的 S0-S3 量级)。
|
|
3
|
-
// 用法:make js 后 win11 POST /exec {"cmd":"qwin.exe test\\bench_ffi_callraw.js","timeout":600000}
|
|
4
|
-
// 臂(同 proc、同帧布局,唯一变量是「每次调用经过哪些层」):
|
|
5
|
-
// A bind rest 参数 + callPacked 全链(每调 argFrame/DataView/retBuf/readRet-dv
|
|
6
|
-
// 分配、getWidth 闭包、writeSlot 对象字面量、ffiCall、readRet)
|
|
7
|
-
// B raw 帧复用 + 每调最小写槽/读返 + ffiCall → native 桩 + QuickJS C 边界
|
|
8
|
-
// C raw-alloc 每调 new ArrayBuffer(32)+DataView+ArrayBuffer(8)+读返 DataView
|
|
9
|
-
// (复刻 callPacked 分配面)+ 写槽 + ffiCall(仅 S0/S1)
|
|
10
|
-
// 差值解读:B = trampoline 地板;C−B = 每调分配;A−C = callPacked JS 层其余。
|
|
11
|
-
// x64 帧约定:argFrame 恒 32B(4×8 槽,桩无条件读 4 槽),retBuf 8B(ffiCall ≥8 校验)。
|
|
12
|
-
// S1/S2/S3 各臂消费返回值到 sink(A/B/C 同口径,防 DCE 疑虑 + 收尾打印证活)。
|
|
13
|
-
// 结束必须 DestroyWindow + std.exit。
|
|
14
|
-
import * as std from 'std';
|
|
15
|
-
import * as gui from 'gui';
|
|
16
|
-
import * as ffi from 'ffi';
|
|
17
|
-
import { bind } from '../lib/ffi/bind.js';
|
|
18
|
-
const WM_NULL = 0x0000;
|
|
19
|
-
const FRAME_BYTES = 32; // x64:4 槽 × 8B,桩无条件读 slots[0..3]
|
|
20
|
-
const ROUNDS = 15;
|
|
21
|
-
const TARGET_MS = 400;
|
|
22
|
-
const med = (t) => {
|
|
23
|
-
const s = [...t].sort((x, y) => x - y);
|
|
24
|
-
const m = s.length >> 1;
|
|
25
|
-
return s.length % 2 ? s[m] : (s[m - 1] + s[m]) / 2;
|
|
26
|
-
};
|
|
27
|
-
// 文件日志(cwd=Z:\quickwin → 宿主 _build/bench_ffi_callraw_log.txt)
|
|
28
|
-
const log = std.open('bench_ffi_callraw_log.txt', 'w');
|
|
29
|
-
function step(msg) {
|
|
30
|
-
std.printf('%s\n', msg);
|
|
31
|
-
if (log) {
|
|
32
|
-
log.puts(msg + '\n');
|
|
33
|
-
log.flush();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
step('stage: imports ok');
|
|
37
|
-
// ---- proc 地址(B/C 臂绕过 bind 直驱 ffiCall)----
|
|
38
|
-
function procOf(dll, name) {
|
|
39
|
-
const h = ffi.LoadLibrary(dll);
|
|
40
|
-
if (!h)
|
|
41
|
-
throw new Error('LoadLibrary(' + dll + ') failed');
|
|
42
|
-
const p = ffi.GetProcAddress(h, name);
|
|
43
|
-
if (!p)
|
|
44
|
-
throw new Error('GetProcAddress(' + dll + '!' + name + ') failed');
|
|
45
|
-
return p;
|
|
46
|
-
}
|
|
47
|
-
const pNop0 = procOf('test/nop.dll', 'nop0');
|
|
48
|
-
const pNop1 = procOf('test/nop.dll', 'nop1');
|
|
49
|
-
const pPid = procOf('kernel32.dll', 'GetCurrentProcessId');
|
|
50
|
-
const pSm = procOf('user32.dll', 'SendMessageW');
|
|
51
|
-
// B 臂复用帧 + 视图(一次分配,跨轮复用)
|
|
52
|
-
const frame = new ArrayBuffer(FRAME_BYTES);
|
|
53
|
-
const retBuf = new ArrayBuffer(8);
|
|
54
|
-
const fv = new DataView(frame);
|
|
55
|
-
const rv = new DataView(retBuf);
|
|
56
|
-
let sink = 0;
|
|
57
|
-
const fNop0 = bind('test/nop.dll', 'nop0', ' -> void');
|
|
58
|
-
const fNop1 = bind('test/nop.dll', 'nop1', 'i32 -> i32');
|
|
59
|
-
const fPid = bind('kernel32.dll', 'GetCurrentProcessId', ' -> u32');
|
|
60
|
-
const winRaw = gui.CreateWindow('STATIC', 'bench-title', 0, 0, 0, 20, 20, null, null);
|
|
61
|
-
if (!winRaw)
|
|
62
|
-
throw new Error('CreateWindow(STATIC) failed');
|
|
63
|
-
const hwnd = winRaw;
|
|
64
|
-
const fSm = bind('user32.dll', 'SendMessageW', '<HWND>ptr u32 <>ptr <>ptr -> <>ptr');
|
|
65
|
-
step('stage: window created');
|
|
66
|
-
// ---- 可行性探针:任一臂签名/帧布局错位立刻炸 ----
|
|
67
|
-
fNop0();
|
|
68
|
-
if (fNop1(1) !== 2)
|
|
69
|
-
throw new Error('sanity: bind nop1(1) !== 2');
|
|
70
|
-
if (fPid() <= 0)
|
|
71
|
-
throw new Error('sanity: bind pid <= 0');
|
|
72
|
-
ffi.ffiCall(pNop0, frame, retBuf, 0);
|
|
73
|
-
fv.setInt32(0, 1, true);
|
|
74
|
-
ffi.ffiCall(pNop1, frame, retBuf, 0);
|
|
75
|
-
if (rv.getInt32(0, true) !== 2)
|
|
76
|
-
throw new Error('sanity: raw nop1 !== 2, got ' + rv.getInt32(0, true));
|
|
77
|
-
ffi.ffiCall(pPid, frame, retBuf, 0);
|
|
78
|
-
if (rv.getUint32(0, true) <= 0)
|
|
79
|
-
throw new Error('sanity: raw pid <= 0');
|
|
80
|
-
// S3 帧布局预演(hwnd 槽 + msg + wParam + lParam)
|
|
81
|
-
fv.setBigUint64(0, BigInt(Number(hwnd)), true);
|
|
82
|
-
fv.setUint32(8, WM_NULL, true);
|
|
83
|
-
fv.setBigUint64(16, 0n, true);
|
|
84
|
-
fv.setBigUint64(24, 0x1234n, true);
|
|
85
|
-
ffi.ffiCall(pSm, frame, retBuf, 0);
|
|
86
|
-
const smRet = rv.getBigUint64(0, true);
|
|
87
|
-
step('stage: probes ok, raw SendMessage ret=' + String(smRet) + ' (expect 0n)');
|
|
88
|
-
// ---- 臂循环:函数取到局部 const,两侧/各臂同形态 ----
|
|
89
|
-
const runA0 = (n) => {
|
|
90
|
-
const f = fNop0;
|
|
91
|
-
const t0 = Date.now();
|
|
92
|
-
for (let i = 0; i < n; i++)
|
|
93
|
-
f();
|
|
94
|
-
return Date.now() - t0;
|
|
95
|
-
};
|
|
96
|
-
const runB0 = (n) => {
|
|
97
|
-
const p = pNop0, fr = frame, rb = retBuf;
|
|
98
|
-
const t0 = Date.now();
|
|
99
|
-
for (let i = 0; i < n; i++)
|
|
100
|
-
ffi.ffiCall(p, fr, rb, 0);
|
|
101
|
-
return Date.now() - t0;
|
|
102
|
-
};
|
|
103
|
-
const runC0 = (n) => {
|
|
104
|
-
const p = pNop0;
|
|
105
|
-
const t0 = Date.now();
|
|
106
|
-
for (let i = 0; i < n; i++) {
|
|
107
|
-
const f = new ArrayBuffer(FRAME_BYTES);
|
|
108
|
-
const d = new DataView(f); // callPacked 无条件建 dv,照抄
|
|
109
|
-
void d;
|
|
110
|
-
ffi.ffiCall(p, f, new ArrayBuffer(8), 0);
|
|
111
|
-
}
|
|
112
|
-
return Date.now() - t0;
|
|
113
|
-
};
|
|
114
|
-
const runA1 = (n) => {
|
|
115
|
-
const f = fNop1;
|
|
116
|
-
const t0 = Date.now();
|
|
117
|
-
for (let i = 0; i < n; i++)
|
|
118
|
-
sink += f(1);
|
|
119
|
-
return Date.now() - t0;
|
|
120
|
-
};
|
|
121
|
-
const runB1 = (n) => {
|
|
122
|
-
const p = pNop1, fr = frame, rb = retBuf, d = fv, rd = rv;
|
|
123
|
-
const t0 = Date.now();
|
|
124
|
-
for (let i = 0; i < n; i++) {
|
|
125
|
-
d.setInt32(0, 1, true);
|
|
126
|
-
ffi.ffiCall(p, fr, rb, 0);
|
|
127
|
-
sink += rd.getInt32(0, true);
|
|
128
|
-
}
|
|
129
|
-
return Date.now() - t0;
|
|
130
|
-
};
|
|
131
|
-
const runC1 = (n) => {
|
|
132
|
-
const p = pNop1;
|
|
133
|
-
const t0 = Date.now();
|
|
134
|
-
for (let i = 0; i < n; i++) {
|
|
135
|
-
const f = new ArrayBuffer(FRAME_BYTES);
|
|
136
|
-
const d = new DataView(f);
|
|
137
|
-
d.setInt32(0, 1, true);
|
|
138
|
-
const r = new ArrayBuffer(8);
|
|
139
|
-
ffi.ffiCall(p, f, r, 0);
|
|
140
|
-
sink += new DataView(r).getInt32(0, true); // readRet 同构:每次 new DataView
|
|
141
|
-
}
|
|
142
|
-
return Date.now() - t0;
|
|
143
|
-
};
|
|
144
|
-
const runA2 = (n) => {
|
|
145
|
-
const f = fPid;
|
|
146
|
-
const t0 = Date.now();
|
|
147
|
-
for (let i = 0; i < n; i++)
|
|
148
|
-
sink += f();
|
|
149
|
-
return Date.now() - t0;
|
|
150
|
-
};
|
|
151
|
-
const runB2 = (n) => {
|
|
152
|
-
const p = pPid, fr = frame, rb = retBuf, rd = rv;
|
|
153
|
-
const t0 = Date.now();
|
|
154
|
-
for (let i = 0; i < n; i++) {
|
|
155
|
-
ffi.ffiCall(p, fr, rb, 0);
|
|
156
|
-
sink += rd.getUint32(0, true);
|
|
157
|
-
}
|
|
158
|
-
return Date.now() - t0;
|
|
159
|
-
};
|
|
160
|
-
const runA3 = (n) => {
|
|
161
|
-
const f = fSm;
|
|
162
|
-
const t0 = Date.now();
|
|
163
|
-
for (let i = 0; i < n; i++)
|
|
164
|
-
sink += f(hwnd, WM_NULL, 0, 0x1234);
|
|
165
|
-
return Date.now() - t0;
|
|
166
|
-
};
|
|
167
|
-
const runB3 = (n) => {
|
|
168
|
-
const p = pSm, fr = frame, rb = retBuf, d = fv, rd = rv;
|
|
169
|
-
const t0 = Date.now();
|
|
170
|
-
for (let i = 0; i < n; i++) {
|
|
171
|
-
d.setBigUint64(0, BigInt(Number(hwnd)), true);
|
|
172
|
-
d.setUint32(8, WM_NULL, true);
|
|
173
|
-
d.setBigUint64(16, 0n, true);
|
|
174
|
-
d.setBigUint64(24, 0x1234n, true);
|
|
175
|
-
ffi.ffiCall(p, fr, rb, 0);
|
|
176
|
-
sink += Number(rd.getBigUint64(0, true));
|
|
177
|
-
}
|
|
178
|
-
return Date.now() - t0;
|
|
179
|
-
};
|
|
180
|
-
// 自校准:探针逐级放大 n0 至 dt≥20ms(1ms 量化步长 ≤5%),按 TARGET_MS 定批量。
|
|
181
|
-
// B 臂可能比 A 快两个量级,固定批量必有一侧撞量化墙——与 overhead 场同法。
|
|
182
|
-
function calibrate(label, fn) {
|
|
183
|
-
let n0 = 10_000;
|
|
184
|
-
let dt = 0;
|
|
185
|
-
for (;;) {
|
|
186
|
-
dt = fn(n0);
|
|
187
|
-
if (dt >= 20 || n0 >= 10_000_000)
|
|
188
|
-
break;
|
|
189
|
-
n0 *= 10;
|
|
190
|
-
}
|
|
191
|
-
const per = dt / n0;
|
|
192
|
-
const n = Math.max(10_000, Math.min(10_000_000, Math.round(TARGET_MS / per)));
|
|
193
|
-
step(' calib ' + label + ': probe n=' + String(n0) + ' dt=' + String(dt) +
|
|
194
|
-
'ms → batch=' + String(n));
|
|
195
|
-
return n;
|
|
196
|
-
}
|
|
197
|
-
const mkArm = (label, fn) => ({ label, n: calibrate(label, fn), fn });
|
|
198
|
-
function suite(name, arms) {
|
|
199
|
-
step('suite: ' + name + ' warmup');
|
|
200
|
-
for (const a of arms)
|
|
201
|
-
a.fn(a.n);
|
|
202
|
-
const times = arms.map(() => []);
|
|
203
|
-
for (let r = 0; r < ROUNDS; r++) {
|
|
204
|
-
for (let k = 0; k < arms.length; k++) {
|
|
205
|
-
const i = (r + k) % arms.length;
|
|
206
|
-
times[i].push(arms[i].fn(arms[i].n));
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
for (let r = 0; r < Math.min(ROUNDS, 3); r++) {
|
|
210
|
-
step(' round ' + String(r) + ': ' +
|
|
211
|
-
arms.map((a, i) => a.label + '=' + String(times[i][r]) + 'ms').join(' '));
|
|
212
|
-
}
|
|
213
|
-
const ns = (i, ms) => (ms * 1e6 / arms[i].n).toFixed(1);
|
|
214
|
-
const meds = times.map(med);
|
|
215
|
-
const mins = times.map(t => Math.min(...t));
|
|
216
|
-
step(' ** median: ' + arms.map((a, i) => a.label + '=' + ns(i, meds[i]) + 'ns').join(' | '));
|
|
217
|
-
step(' min: ' + arms.map((a, i) => a.label + '=' + ns(i, mins[i]) + 'ns').join(' | '));
|
|
218
|
-
if (arms.length >= 3) {
|
|
219
|
-
const a = Number(ns(0, meds[0])), b = Number(ns(1, meds[1])), c = Number(ns(2, meds[2]));
|
|
220
|
-
step(' split: C-B=' + (c - b).toFixed(1) + 'ns (每调分配) A-C=' +
|
|
221
|
-
(a - c).toFixed(1) + 'ns (callPacked JS 层其余)');
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
step('bench_ffi_callraw (attribution): A=bind / B=raw ffiCall / C=raw+per-call alloc; ' +
|
|
225
|
-
'calib ~' + String(TARGET_MS) + 'ms, ' + String(ROUNDS) + 'r (median)');
|
|
226
|
-
suite('S0 nop0 0参 void', [mkArm('A bind', runA0), mkArm('B raw ', runB0), mkArm('C allc', runC0)]);
|
|
227
|
-
suite('S1 nop1 1×i32', [mkArm('A bind', runA1), mkArm('B raw ', runB1), mkArm('C allc', runC1)]);
|
|
228
|
-
suite('S2 GetCurrentProcessId', [mkArm('A bind', runA2), mkArm('B raw ', runB2)]);
|
|
229
|
-
suite('S3 SendMessageW WM_NULL', [mkArm('A bind', runA3), mkArm('B raw ', runB3)]);
|
|
230
|
-
step('sink=' + String(sink) + ' (liveness: >0 证明各臂 native 调用真实发生)');
|
|
231
|
-
gui.DestroyWindow(hwnd);
|
|
232
|
-
step('DONE');
|
|
233
|
-
if (log)
|
|
234
|
-
log.close();
|
|
235
|
-
std.exit(0);
|
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
// 一次性性能 bench(归因场):绕过 callPacked 直调 ffi.ffiCall,把 bind() 调用链拆层。
|
|
2
|
-
// 不入 CI。A 臂 = bind() 同场对照(对齐 bench_ffi_overhead.ts 的 S0-S3 量级)。
|
|
3
|
-
// 用法:make js 后 win11 POST /exec {"cmd":"qwin.exe test\\bench_ffi_callraw.js","timeout":600000}
|
|
4
|
-
// 臂(同 proc、同帧布局,唯一变量是「每次调用经过哪些层」):
|
|
5
|
-
// A bind rest 参数 + callPacked 全链(每调 argFrame/DataView/retBuf/readRet-dv
|
|
6
|
-
// 分配、getWidth 闭包、writeSlot 对象字面量、ffiCall、readRet)
|
|
7
|
-
// B raw 帧复用 + 每调最小写槽/读返 + ffiCall → native 桩 + QuickJS C 边界
|
|
8
|
-
// C raw-alloc 每调 new ArrayBuffer(32)+DataView+ArrayBuffer(8)+读返 DataView
|
|
9
|
-
// (复刻 callPacked 分配面)+ 写槽 + ffiCall(仅 S0/S1)
|
|
10
|
-
// 差值解读:B = trampoline 地板;C−B = 每调分配;A−C = callPacked JS 层其余。
|
|
11
|
-
// x64 帧约定:argFrame 恒 32B(4×8 槽,桩无条件读 4 槽),retBuf 8B(ffiCall ≥8 校验)。
|
|
12
|
-
// S1/S2/S3 各臂消费返回值到 sink(A/B/C 同口径,防 DCE 疑虑 + 收尾打印证活)。
|
|
13
|
-
// 结束必须 DestroyWindow + std.exit。
|
|
14
|
-
import * as std from 'std'
|
|
15
|
-
import * as gui from 'gui'
|
|
16
|
-
import * as ffi from 'ffi'
|
|
17
|
-
import { bind } from '../lib/ffi/bind.js'
|
|
18
|
-
|
|
19
|
-
const WM_NULL = 0x0000
|
|
20
|
-
const FRAME_BYTES = 32 // x64:4 槽 × 8B,桩无条件读 slots[0..3]
|
|
21
|
-
const ROUNDS = 15
|
|
22
|
-
const TARGET_MS = 400
|
|
23
|
-
|
|
24
|
-
const med = (t: number[]): number => {
|
|
25
|
-
const s = [...t].sort((x, y) => x - y)
|
|
26
|
-
const m = s.length >> 1
|
|
27
|
-
return s.length % 2 ? s[m]! : (s[m - 1]! + s[m]!) / 2
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// 文件日志(cwd=Z:\quickwin → 宿主 _build/bench_ffi_callraw_log.txt)
|
|
31
|
-
const log = std.open('bench_ffi_callraw_log.txt', 'w')
|
|
32
|
-
function step(msg: string): void {
|
|
33
|
-
std.printf('%s\n', msg)
|
|
34
|
-
if (log) { log.puts(msg + '\n'); log.flush() }
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
step('stage: imports ok')
|
|
38
|
-
|
|
39
|
-
// ---- proc 地址(B/C 臂绕过 bind 直驱 ffiCall)----
|
|
40
|
-
function procOf(dll: string, name: string): number {
|
|
41
|
-
const h = ffi.LoadLibrary(dll)
|
|
42
|
-
if (!h) throw new Error('LoadLibrary(' + dll + ') failed')
|
|
43
|
-
const p = ffi.GetProcAddress(h, name)
|
|
44
|
-
if (!p) throw new Error('GetProcAddress(' + dll + '!' + name + ') failed')
|
|
45
|
-
return p
|
|
46
|
-
}
|
|
47
|
-
const pNop0 = procOf('test/nop.dll', 'nop0')
|
|
48
|
-
const pNop1 = procOf('test/nop.dll', 'nop1')
|
|
49
|
-
const pPid = procOf('kernel32.dll', 'GetCurrentProcessId')
|
|
50
|
-
const pSm = procOf('user32.dll', 'SendMessageW')
|
|
51
|
-
|
|
52
|
-
// B 臂复用帧 + 视图(一次分配,跨轮复用)
|
|
53
|
-
const frame = new ArrayBuffer(FRAME_BYTES)
|
|
54
|
-
const retBuf = new ArrayBuffer(8)
|
|
55
|
-
const fv = new DataView(frame)
|
|
56
|
-
const rv = new DataView(retBuf)
|
|
57
|
-
let sink = 0
|
|
58
|
-
|
|
59
|
-
const fNop0 = bind('test/nop.dll', 'nop0', ' -> void')
|
|
60
|
-
const fNop1 = bind('test/nop.dll', 'nop1', 'i32 -> i32')
|
|
61
|
-
const fPid = bind('kernel32.dll', 'GetCurrentProcessId', ' -> u32')
|
|
62
|
-
|
|
63
|
-
const winRaw = gui.CreateWindow('STATIC', 'bench-title', 0, 0, 0, 20, 20, null, null)
|
|
64
|
-
if (!winRaw) throw new Error('CreateWindow(STATIC) failed')
|
|
65
|
-
const hwnd: gui.HWND = winRaw
|
|
66
|
-
const fSm = bind('user32.dll', 'SendMessageW', '<HWND>ptr u32 <>ptr <>ptr -> <>ptr')
|
|
67
|
-
step('stage: window created')
|
|
68
|
-
|
|
69
|
-
// ---- 可行性探针:任一臂签名/帧布局错位立刻炸 ----
|
|
70
|
-
fNop0()
|
|
71
|
-
if (fNop1(1) !== 2) throw new Error('sanity: bind nop1(1) !== 2')
|
|
72
|
-
if (fPid() <= 0) throw new Error('sanity: bind pid <= 0')
|
|
73
|
-
ffi.ffiCall(pNop0, frame, retBuf, 0)
|
|
74
|
-
fv.setInt32(0, 1, true)
|
|
75
|
-
ffi.ffiCall(pNop1, frame, retBuf, 0)
|
|
76
|
-
if (rv.getInt32(0, true) !== 2) throw new Error('sanity: raw nop1 !== 2, got ' + rv.getInt32(0, true))
|
|
77
|
-
ffi.ffiCall(pPid, frame, retBuf, 0)
|
|
78
|
-
if (rv.getUint32(0, true) <= 0) throw new Error('sanity: raw pid <= 0')
|
|
79
|
-
// S3 帧布局预演(hwnd 槽 + msg + wParam + lParam)
|
|
80
|
-
fv.setBigUint64(0, BigInt(Number(hwnd)), true)
|
|
81
|
-
fv.setUint32(8, WM_NULL, true)
|
|
82
|
-
fv.setBigUint64(16, 0n, true)
|
|
83
|
-
fv.setBigUint64(24, 0x1234n, true)
|
|
84
|
-
ffi.ffiCall(pSm, frame, retBuf, 0)
|
|
85
|
-
const smRet = rv.getBigUint64(0, true)
|
|
86
|
-
step('stage: probes ok, raw SendMessage ret=' + String(smRet) + ' (expect 0n)')
|
|
87
|
-
|
|
88
|
-
// ---- 臂循环:函数取到局部 const,两侧/各臂同形态 ----
|
|
89
|
-
const runA0 = (n: number): number => {
|
|
90
|
-
const f = fNop0
|
|
91
|
-
const t0 = Date.now()
|
|
92
|
-
for (let i = 0; i < n; i++) f()
|
|
93
|
-
return Date.now() - t0
|
|
94
|
-
}
|
|
95
|
-
const runB0 = (n: number): number => {
|
|
96
|
-
const p = pNop0, fr = frame, rb = retBuf
|
|
97
|
-
const t0 = Date.now()
|
|
98
|
-
for (let i = 0; i < n; i++) ffi.ffiCall(p, fr, rb, 0)
|
|
99
|
-
return Date.now() - t0
|
|
100
|
-
}
|
|
101
|
-
const runC0 = (n: number): number => {
|
|
102
|
-
const p = pNop0
|
|
103
|
-
const t0 = Date.now()
|
|
104
|
-
for (let i = 0; i < n; i++) {
|
|
105
|
-
const f = new ArrayBuffer(FRAME_BYTES)
|
|
106
|
-
const d = new DataView(f) // callPacked 无条件建 dv,照抄
|
|
107
|
-
void d
|
|
108
|
-
ffi.ffiCall(p, f, new ArrayBuffer(8), 0)
|
|
109
|
-
}
|
|
110
|
-
return Date.now() - t0
|
|
111
|
-
}
|
|
112
|
-
const runA1 = (n: number): number => {
|
|
113
|
-
const f = fNop1
|
|
114
|
-
const t0 = Date.now()
|
|
115
|
-
for (let i = 0; i < n; i++) sink += f(1)
|
|
116
|
-
return Date.now() - t0
|
|
117
|
-
}
|
|
118
|
-
const runB1 = (n: number): number => {
|
|
119
|
-
const p = pNop1, fr = frame, rb = retBuf, d = fv, rd = rv
|
|
120
|
-
const t0 = Date.now()
|
|
121
|
-
for (let i = 0; i < n; i++) {
|
|
122
|
-
d.setInt32(0, 1, true)
|
|
123
|
-
ffi.ffiCall(p, fr, rb, 0)
|
|
124
|
-
sink += rd.getInt32(0, true)
|
|
125
|
-
}
|
|
126
|
-
return Date.now() - t0
|
|
127
|
-
}
|
|
128
|
-
const runC1 = (n: number): number => {
|
|
129
|
-
const p = pNop1
|
|
130
|
-
const t0 = Date.now()
|
|
131
|
-
for (let i = 0; i < n; i++) {
|
|
132
|
-
const f = new ArrayBuffer(FRAME_BYTES)
|
|
133
|
-
const d = new DataView(f)
|
|
134
|
-
d.setInt32(0, 1, true)
|
|
135
|
-
const r = new ArrayBuffer(8)
|
|
136
|
-
ffi.ffiCall(p, f, r, 0)
|
|
137
|
-
sink += new DataView(r).getInt32(0, true) // readRet 同构:每次 new DataView
|
|
138
|
-
}
|
|
139
|
-
return Date.now() - t0
|
|
140
|
-
}
|
|
141
|
-
const runA2 = (n: number): number => {
|
|
142
|
-
const f = fPid
|
|
143
|
-
const t0 = Date.now()
|
|
144
|
-
for (let i = 0; i < n; i++) sink += f()
|
|
145
|
-
return Date.now() - t0
|
|
146
|
-
}
|
|
147
|
-
const runB2 = (n: number): number => {
|
|
148
|
-
const p = pPid, fr = frame, rb = retBuf, rd = rv
|
|
149
|
-
const t0 = Date.now()
|
|
150
|
-
for (let i = 0; i < n; i++) {
|
|
151
|
-
ffi.ffiCall(p, fr, rb, 0)
|
|
152
|
-
sink += rd.getUint32(0, true)
|
|
153
|
-
}
|
|
154
|
-
return Date.now() - t0
|
|
155
|
-
}
|
|
156
|
-
const runA3 = (n: number): number => {
|
|
157
|
-
const f = fSm
|
|
158
|
-
const t0 = Date.now()
|
|
159
|
-
for (let i = 0; i < n; i++) sink += f(hwnd, WM_NULL, 0, 0x1234)
|
|
160
|
-
return Date.now() - t0
|
|
161
|
-
}
|
|
162
|
-
const runB3 = (n: number): number => {
|
|
163
|
-
const p = pSm, fr = frame, rb = retBuf, d = fv, rd = rv
|
|
164
|
-
const t0 = Date.now()
|
|
165
|
-
for (let i = 0; i < n; i++) {
|
|
166
|
-
d.setBigUint64(0, BigInt(Number(hwnd)), true)
|
|
167
|
-
d.setUint32(8, WM_NULL, true)
|
|
168
|
-
d.setBigUint64(16, 0n, true)
|
|
169
|
-
d.setBigUint64(24, 0x1234n, true)
|
|
170
|
-
ffi.ffiCall(p, fr, rb, 0)
|
|
171
|
-
sink += Number(rd.getBigUint64(0, true))
|
|
172
|
-
}
|
|
173
|
-
return Date.now() - t0
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
type Arm = { label: string; n: number; fn: (n: number) => number }
|
|
177
|
-
|
|
178
|
-
// 自校准:探针逐级放大 n0 至 dt≥20ms(1ms 量化步长 ≤5%),按 TARGET_MS 定批量。
|
|
179
|
-
// B 臂可能比 A 快两个量级,固定批量必有一侧撞量化墙——与 overhead 场同法。
|
|
180
|
-
function calibrate(label: string, fn: (n: number) => number): number {
|
|
181
|
-
let n0 = 10_000
|
|
182
|
-
let dt = 0
|
|
183
|
-
for (;;) {
|
|
184
|
-
dt = fn(n0)
|
|
185
|
-
if (dt >= 20 || n0 >= 10_000_000) break
|
|
186
|
-
n0 *= 10
|
|
187
|
-
}
|
|
188
|
-
const per = dt / n0
|
|
189
|
-
const n = Math.max(10_000, Math.min(10_000_000, Math.round(TARGET_MS / per)))
|
|
190
|
-
step(' calib ' + label + ': probe n=' + String(n0) + ' dt=' + String(dt) +
|
|
191
|
-
'ms → batch=' + String(n))
|
|
192
|
-
return n
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const mkArm = (label: string, fn: (n: number) => number): Arm =>
|
|
196
|
-
({ label, n: calibrate(label, fn), fn })
|
|
197
|
-
|
|
198
|
-
function suite(name: string, arms: Arm[]): void {
|
|
199
|
-
step('suite: ' + name + ' warmup')
|
|
200
|
-
for (const a of arms) a.fn(a.n)
|
|
201
|
-
const times: number[][] = arms.map(() => [])
|
|
202
|
-
for (let r = 0; r < ROUNDS; r++) {
|
|
203
|
-
for (let k = 0; k < arms.length; k++) {
|
|
204
|
-
const i = (r + k) % arms.length
|
|
205
|
-
times[i]!.push(arms[i]!.fn(arms[i]!.n))
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
for (let r = 0; r < Math.min(ROUNDS, 3); r++) {
|
|
209
|
-
step(' round ' + String(r) + ': ' +
|
|
210
|
-
arms.map((a, i) => a.label + '=' + String(times[i]![r]) + 'ms').join(' '))
|
|
211
|
-
}
|
|
212
|
-
const ns = (i: number, ms: number) => (ms * 1e6 / arms[i]!.n).toFixed(1)
|
|
213
|
-
const meds = times.map(med)
|
|
214
|
-
const mins = times.map(t => Math.min(...t))
|
|
215
|
-
step(' ** median: ' + arms.map((a, i) => a.label + '=' + ns(i, meds[i]!) + 'ns').join(' | '))
|
|
216
|
-
step(' min: ' + arms.map((a, i) => a.label + '=' + ns(i, mins[i]!) + 'ns').join(' | '))
|
|
217
|
-
if (arms.length >= 3) {
|
|
218
|
-
const a = Number(ns(0, meds[0]!)), b = Number(ns(1, meds[1]!)), c = Number(ns(2, meds[2]!))
|
|
219
|
-
step(' split: C-B=' + (c - b).toFixed(1) + 'ns (每调分配) A-C=' +
|
|
220
|
-
(a - c).toFixed(1) + 'ns (callPacked JS 层其余)')
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
step('bench_ffi_callraw (attribution): A=bind / B=raw ffiCall / C=raw+per-call alloc; ' +
|
|
225
|
-
'calib ~' + String(TARGET_MS) + 'ms, ' + String(ROUNDS) + 'r (median)')
|
|
226
|
-
suite('S0 nop0 0参 void', [mkArm('A bind', runA0), mkArm('B raw ', runB0), mkArm('C allc', runC0)])
|
|
227
|
-
suite('S1 nop1 1×i32', [mkArm('A bind', runA1), mkArm('B raw ', runB1), mkArm('C allc', runC1)])
|
|
228
|
-
suite('S2 GetCurrentProcessId', [mkArm('A bind', runA2), mkArm('B raw ', runB2)])
|
|
229
|
-
suite('S3 SendMessageW WM_NULL', [mkArm('A bind', runA3), mkArm('B raw ', runB3)])
|
|
230
|
-
|
|
231
|
-
step('sink=' + String(sink) + ' (liveness: >0 证明各臂 native 调用真实发生)')
|
|
232
|
-
gui.DestroyWindow(hwnd)
|
|
233
|
-
step('DONE')
|
|
234
|
-
if (log) log.close()
|
|
235
|
-
std.exit(0)
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
// 一次性性能 bench:我方 bind() FFI vs node:ffi(Node v26.10) 同机同场景对比(win11 VM)
|
|
2
|
-
// 不入 CI。对照脚本 test/bench_nodeffi.mjs(node 侧同场景)。结论见对话,未落文档。
|
|
3
|
-
// 用法:make js 后 POST /exec {"cmd":"qwin.exe test/bench_ffi_overhead.js"}
|
|
4
|
-
// 场景(跨运行时按 ns/call 归一,每臂独立自校准到 ~400ms/样本):
|
|
5
|
-
// S0 nop0 test/nop.dll 0 参 void —— 纯 JS→C 往返 trampoline(无封送无返回读取)
|
|
6
|
-
// S1 nop1 test/nop.dll 1×i32 —— 单标量封送往返
|
|
7
|
-
// S2 pid kernel32 GetCurrentProcessId 0 参 —— 真实系统 DLL
|
|
8
|
-
// S3 sm user32 SendMessageW WM_NULL 自有窗口 —— 完整链路 + gui C 直调地板参照
|
|
9
|
-
// 方法学沿用 bench_sendmessage_nonnull:预热 1 + 臂序轮转 + median 主指标/min。
|
|
10
|
-
// Date.now() 1ms 量化 → 批量自校准(探针 ≥20ms 才有 ≤5% 步长,目标 ~400ms/样本)。
|
|
11
|
-
// 全部消息发自有窗口(直调 wndproc)。结束必须 DestroyWindow + std.exit。
|
|
12
|
-
import * as std from 'std';
|
|
13
|
-
import * as gui from 'gui';
|
|
14
|
-
import { bind } from '../lib/ffi/bind.js';
|
|
15
|
-
const WM_NULL = 0x0000;
|
|
16
|
-
const ROUNDS = 15;
|
|
17
|
-
const TARGET_MS = 400;
|
|
18
|
-
// median(离群稳健)供 calibrate 与 suite 共用
|
|
19
|
-
const med = (t) => {
|
|
20
|
-
const s = [...t].sort((x, y) => x - y);
|
|
21
|
-
const m = s.length >> 1;
|
|
22
|
-
return s.length % 2 ? s[m] : (s[m - 1] + s[m]) / 2;
|
|
23
|
-
};
|
|
24
|
-
// 文件日志(cwd=Z:\quickwin,宿主机 _build/bench_ffi_overhead_log.txt 可实时读)——
|
|
25
|
-
// exec 只在进程退出后回包,挂了靠它定位卡点
|
|
26
|
-
const log = std.open('bench_ffi_overhead_log.txt', 'w');
|
|
27
|
-
function step(msg) {
|
|
28
|
-
std.printf('%s\n', msg);
|
|
29
|
-
if (log) {
|
|
30
|
-
log.puts(msg + '\n');
|
|
31
|
-
log.flush();
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
step('stage: imports ok');
|
|
35
|
-
const winRaw = gui.CreateWindow('STATIC', 'bench-title', 0, 0, 0, 20, 20, null, null);
|
|
36
|
-
if (!winRaw)
|
|
37
|
-
throw new Error('CreateWindow(STATIC) failed');
|
|
38
|
-
const win = winRaw;
|
|
39
|
-
step('stage: window created');
|
|
40
|
-
const fNop0 = bind('test/nop.dll', 'nop0', ' -> void');
|
|
41
|
-
const fNop1 = bind('test/nop.dll', 'nop1', 'i32 -> i32');
|
|
42
|
-
const fPid = bind('kernel32.dll', 'GetCurrentProcessId', ' -> u32');
|
|
43
|
-
const fSm = bind('user32.dll', 'SendMessageW', '<HWND>ptr u32 <>ptr <>ptr -> <>ptr');
|
|
44
|
-
// 每臂循环体:函数先取到局部 const(与 node 侧同形态——模块槽访问 vs 局部槽访问的
|
|
45
|
-
// 每迭代差在 ns 级,两边都取局部才对齐);调用结果不使用(QuickJS 解释器不消除
|
|
46
|
-
// 副作用调用,与原 bench 同口径)
|
|
47
|
-
const runNop0 = (n) => {
|
|
48
|
-
const f = fNop0;
|
|
49
|
-
const t0 = Date.now();
|
|
50
|
-
for (let i = 0; i < n; i++)
|
|
51
|
-
f();
|
|
52
|
-
return Date.now() - t0;
|
|
53
|
-
};
|
|
54
|
-
const runNop1 = (n) => {
|
|
55
|
-
const f = fNop1;
|
|
56
|
-
const t0 = Date.now();
|
|
57
|
-
for (let i = 0; i < n; i++)
|
|
58
|
-
f(1);
|
|
59
|
-
return Date.now() - t0;
|
|
60
|
-
};
|
|
61
|
-
const runPid = (n) => {
|
|
62
|
-
const f = fPid;
|
|
63
|
-
const t0 = Date.now();
|
|
64
|
-
for (let i = 0; i < n; i++)
|
|
65
|
-
f();
|
|
66
|
-
return Date.now() - t0;
|
|
67
|
-
};
|
|
68
|
-
const runSm = (n) => {
|
|
69
|
-
const f = fSm;
|
|
70
|
-
const t0 = Date.now();
|
|
71
|
-
for (let i = 0; i < n; i++)
|
|
72
|
-
f(win, WM_NULL, 0, 0x1234);
|
|
73
|
-
return Date.now() - t0;
|
|
74
|
-
};
|
|
75
|
-
const runGuiSm = (n) => {
|
|
76
|
-
const f = gui.SendMessage;
|
|
77
|
-
const t0 = Date.now();
|
|
78
|
-
for (let i = 0; i < n; i++)
|
|
79
|
-
f(win, WM_NULL, 0, 0x1234);
|
|
80
|
-
return Date.now() - t0;
|
|
81
|
-
};
|
|
82
|
-
// 自校准:探针逐级放大 n0 直到 dt≥20ms(量化步长 1ms ≤ 5%),再按 TARGET_MS 定批量。
|
|
83
|
-
// 不预设单次调用量级——我方与 gui 地板相差 100×,固定批量必有一侧撞量化墙。
|
|
84
|
-
function calibrate(label, fn) {
|
|
85
|
-
let n0 = 10_000;
|
|
86
|
-
let dt = 0;
|
|
87
|
-
for (;;) {
|
|
88
|
-
dt = fn(n0);
|
|
89
|
-
if (dt >= 20 || n0 >= 10_000_000)
|
|
90
|
-
break;
|
|
91
|
-
n0 *= 10;
|
|
92
|
-
}
|
|
93
|
-
const per = dt / n0;
|
|
94
|
-
const n = Math.max(10_000, Math.min(10_000_000, Math.round(TARGET_MS / per)));
|
|
95
|
-
step(' calib ' + label + ': probe n=' + String(n0) + ' dt=' + String(dt) +
|
|
96
|
-
'ms → batch=' + String(n));
|
|
97
|
-
return n;
|
|
98
|
-
}
|
|
99
|
-
function mkArm(label, fn) {
|
|
100
|
-
return { label, n: calibrate(label, fn), fn };
|
|
101
|
-
}
|
|
102
|
-
function suite(name, arms) {
|
|
103
|
-
step('suite: ' + name + ' warmup');
|
|
104
|
-
for (const a of arms)
|
|
105
|
-
a.fn(a.n); // 预热各 1 轮
|
|
106
|
-
const times = arms.map(() => []);
|
|
107
|
-
for (let r = 0; r < ROUNDS; r++) {
|
|
108
|
-
// 臂序按 r 轮转起跑,抵消顺序性偏置(温度/调度漂移)
|
|
109
|
-
for (let k = 0; k < arms.length; k++) {
|
|
110
|
-
const i = (r + k) % arms.length;
|
|
111
|
-
times[i].push(arms[i].fn(arms[i].n));
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
// 每轮原始数据只 dump 前 3 轮(15 轮全打没意义,log 只留诊断线索)
|
|
115
|
-
for (let r = 0; r < Math.min(ROUNDS, 3); r++) {
|
|
116
|
-
step(' round ' + String(r) + ': ' +
|
|
117
|
-
arms.map((a, i) => a.label + '=' + String(times[i][r]) + 'ms').join(' '));
|
|
118
|
-
}
|
|
119
|
-
const ns = (i, ms) => (ms * 1e6 / arms[i].n).toFixed(1);
|
|
120
|
-
// median 是主指标(离群稳健);min 留作参照
|
|
121
|
-
const meds = times.map(med);
|
|
122
|
-
const mins = times.map(t => Math.min(...t));
|
|
123
|
-
step(' ** median: ' + arms.map((a, i) => a.label + '=' + ns(i, meds[i]) + 'ns').join(' | '));
|
|
124
|
-
step(' min: ' + arms.map((a, i) => a.label + '=' + ns(i, mins[i]) + 'ns').join(' | '));
|
|
125
|
-
}
|
|
126
|
-
step('bench_ffi_overhead (ours): per-arm calib ~' + String(TARGET_MS) + 'ms, ' +
|
|
127
|
-
String(ROUNDS) + 'r (median 主指标), 对照 test/bench_nodeffi.mjs');
|
|
128
|
-
suite('S0 nop0 0参 void 纯往返', [mkArm('ours', runNop0)]);
|
|
129
|
-
suite('S1 nop1 1×i32 封送往返', [mkArm('ours', runNop1)]);
|
|
130
|
-
suite('S2 GetCurrentProcessId 0参真实API', [mkArm('ours', runPid)]);
|
|
131
|
-
suite('S3 SendMessageW WM_NULL 自有窗口 (gui=直调地板)', [
|
|
132
|
-
mkArm('ours', runSm),
|
|
133
|
-
mkArm('gui ', runGuiSm),
|
|
134
|
-
]);
|
|
135
|
-
gui.DestroyWindow(win);
|
|
136
|
-
step('DONE');
|
|
137
|
-
if (log)
|
|
138
|
-
log.close();
|
|
139
|
-
std.exit(0);
|