perf: `s += a[i]` over a `Uint8Array` parameter is 4x slower than over other typed arrays
メンテナーはふだん 1 日以内に返信
関連するプルリクエストがすでにマージされています。
- #13777 @delef による — マージ済み
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 18/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 停滞
- 技術スタック
- rust, typescript
- 領域
- compilers, performance
調査の方向性
The binding proof is in collectors/spec_abi_sites.rs, which only accepts Expr::TypedArrayNew, while lower/expr_new.rs lowers new Uint8Array(x) to Uint8ArrayNew. Start by checking how spec_ta_kind_is_numeric and the TaPtr path handle TYPED_ARRAY_KIND_UINT8, then confirm the sum$spec_b_b entry with the reproduction. Done means the specialized entry becomes sum$spec_ta* with the same output. A linked PR (#13777) is already open, so review that rather than starting a parallel fix.
索引モデルが issue の本文から書いたものです。
説明
What happened
s += a[i] over a Uint8Array parameter runs about 4× slower than the same loop over a Uint16Array or any other numeric typed array. A Uint8Array argument never reaches its callee through the typed-array spec ABI (TaPtr). The callee keeps the boxed ABI, the accumulator s loses its Number-by-construction proof, and every += goes through js_dynamic_string_or_number_add.
What you expected
The same code for Uint8Array as for the other numeric typed-array kinds, close to Node.
Minimal reproduction
function sum(a: Uint8Array, n: number): number {
let s = 0;
for (let i = 0; i < n; i += 1) s += a[i];
return s;
}
const a = new Uint8Array(1 << 20);
for (let i = 0; i < a.length; i++) a[i] = i & 255;
const t0 = Date.now();
let t = 0;
for (let r = 0; r < 400; r++) t += sum(a, a.length);
console.log(Date.now() - t0, t);
perry compile sum.ts -o sum && ./sum
node --experimental-strip-types sum.ts
Median of 3 runs, ms. All variants print the same sum, 53477376000.
| array | Perry | specialized entry | Node |
|---|---|---|---|
Uint8Array |
1442 | sum$spec_b_b |
250 |
Uint16Array |
356 | sum$spec_ta3_b |
249 |
A literal length (new Uint8Array(1048576)) does not help: the entry is sum$spec_b_b either way.
The canonical for (let i = 0; i < n; i++) hides the problem, because the indexed_owner loop versioning serves it with its own loop (307 ms). Other loop forms show it: a += 1 step, a non-zero start, a while loop.
Cause
collectors/spec_abi_sites.rs proves a typed-array binding only when its initializer is Expr::TypedArrayNew { kind, arg }:
if let Stmt::Let {
id,
init: Some(Expr::TypedArrayNew { kind, arg }),
..
} = s
HIR lowers new Uint8Array(x) to Expr::Uint8ArrayNew(x) instead (lower/expr_new.rs: "Uint8Array stays on the Buffer path above"), so a Uint8Array binding is never proven and every call site passing it is judged Boxed.
The exclusion is not deliberate. The TaPtr code already lists the kind: spec_ta_kind_is_numeric includes TYPED_ARRAY_KIND_UINT8, spec_ta_kind_elem_width maps it to BufferElem::U8, and spec_ta_kind_class_name to "Uint8Array". The runtime object is the same too: js_uint8array_alloc and typed_array_alloc(UINT8, n) both call store_alloc with GC_TYPE_BUFFER_UINT8ARRAY (0x40, KIND_BRANDS[UINT8] = 0), the non-movable byte store that TaPtr relies on. sealed_buffers already treats Uint8ArrayNew as a fresh array, so the .buffer exposure check covers it.
Proposed fix
Accept Uint8ArrayNew(arg) in the TaPtr binding collector as kind TYPED_ARRAY_KIND_UINT8, with the same judge_ctor_arg rules for arg, and treat its argument as a copied source like TypedArrayNew's. I will open a PR.
Environment
- Perry version: 0.5.1663,
mainate79c398b5, built from source with--release - Host OS: macOS 27.2, Apple M3 Pro
- Target: native
- Node: 26.12.1
Anything else
Related: #13740 (the same proof lost for a non-literal length) and #8619 (the TaPtr read rule).
- 主要言語
- Rust
- スター
- 4.9k
- フォーク
- 166
- 平均マージ
- 3時間 46分
- マージ済み PR(30日)
- 891
環境構築
- Dockerfile または Docker Compose ファイルあり
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
PerryTS/perry のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
メンテナーはふだん 1 日以内に返信
-
package-audit performance
難易度 5/5 1週間以上 初心者へのやさしさ 25/100
PerryTS/perry#13976 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
bug package-audit performance
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
PerryTS/perry#13974 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
bug package-audit
難易度 4/5 3〜5日 初心者へのやさしさ 46/100
メンテナーはふだん 1 日以内に返信
-
bug package-audit
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
メンテナーはふだん 1 日以内に返信
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
0xPlaygrounds/rig#3176 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
packaging
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
SalomonAvila/memtop#23 ·
メンテナーはふだん 1 日以内に返信
-
Anthropic provider: 400 on tools.N.custom.name after tool_search adds web.run (name contains a dot)オープンarea:install area:mcp area:state area:tui os:macos
難易度 2/5 1〜3時間 初心者へのやさしさ 73/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
メンテナーはふだん 1 日以内に返信
-
area:core
難易度 2/5 1〜3時間 初心者へのやさしさ 67/100
メンテナーはふだん 1 日以内に返信