mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-27 06:43:19 +08:00
Version loops to a typed array's length on every typed array they index (#1762)
This commit is contained in:
@@ -26,6 +26,7 @@ Application-shaped workloads for measuring the speed of compiled executables. Ea
|
||||
| `region-builds` | cpu | a watch-mode build loop over `LifetimeRegion`: each build binds a program in a new region and releases the previous one, which a cache keeps referenced for two builds |
|
||||
| `template-render` | cpu | template literals, escaping, `map`/`join` |
|
||||
| `thread-slots`, `thread-slots-cache` | cpu | worker threads reading one published type graph from an `InternTable` and keeping per-thread state for each type they visit: a `ThreadSlots` family, or a direct-mapped cache by id in front of a `Map` by id (the `-cache` entry); under Node.js both are per thread |
|
||||
| `typed-array-kernels` | cpu | element-wise loops that run to one typed array's length and index others at the same position: `Float64Array` axpy, `Float32Array` blend, `Uint8Array` mask, wrapping `Int32Array` add, RGBA to gray |
|
||||
| `typed-array-scan`, `typed-array-scan-workers` | cpu | element-by-element scan loops over `Uint8Array`, `Uint16Array`, `Int32Array` and `Float64Array`; the second entry is a worker-enabled executable (it can start a worker), so each access also checks for shared storage |
|
||||
| `unicode-lexer` | cpu | a hand-written lexer over a 2.7M-unit bundled module whose comments and strings are in several languages: `charCodeAt` scans, line starts |
|
||||
| `validate-errors` | cpu | throw/catch on a fraction of inputs, small call chains |
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"entry": "workloads/typed-array-kernels.ts",
|
||||
"args": [
|
||||
"1"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// Element-wise loops over several typed arrays: each loop runs to one
|
||||
// array's length and reads or writes the others at the same index.
|
||||
|
||||
function axpy(a: number, x: Float64Array, y: Float64Array, out: Float64Array): void {
|
||||
for (let i = 0; i < out.length; i++) out[i] = a * x[i]! + y[i]!;
|
||||
}
|
||||
|
||||
function blend(t: number, a: Float32Array, b: Float32Array, out: Float32Array): void {
|
||||
for (let i = 0; i < out.length; i++) out[i] = a[i]! * (1 - t) + b[i]! * t;
|
||||
}
|
||||
|
||||
function mask(data: Uint8Array, key: Uint8Array, out: Uint8Array): void {
|
||||
for (let i = 0; i < out.length; i++) out[i] = data[i]! ^ key[i]!;
|
||||
}
|
||||
|
||||
function addWrap(a: Int32Array, b: Int32Array, out: Int32Array): void {
|
||||
for (let i = 0; i < out.length; i++) out[i] = (a[i]! + b[i]!) | 0;
|
||||
}
|
||||
|
||||
function gray(rgba: Uint8Array, out: Uint8Array): void {
|
||||
for (let i = 0; i < out.length; i++) {
|
||||
const j = i * 4;
|
||||
out[i] = (rgba[j]! * 77 + rgba[j + 1]! * 150 + rgba[j + 2]! * 29) >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
const scale = Number(process.argv[2] ?? "1");
|
||||
const n = 1 << 16;
|
||||
const x = new Float64Array(n);
|
||||
const y = new Float64Array(n);
|
||||
const d = new Float64Array(n);
|
||||
const fa = new Float32Array(n);
|
||||
const fb = new Float32Array(n);
|
||||
const fo = new Float32Array(n);
|
||||
const data = new Uint8Array(n);
|
||||
const key = new Uint8Array(n);
|
||||
const masked = new Uint8Array(n);
|
||||
const ia = new Int32Array(n);
|
||||
const ib = new Int32Array(n);
|
||||
const io = new Int32Array(n);
|
||||
const rgba = new Uint8Array(n * 4);
|
||||
const luma = new Uint8Array(n);
|
||||
let state = 2463534242;
|
||||
const next = (): number => {
|
||||
state ^= state << 13;
|
||||
state ^= state >>> 17;
|
||||
state ^= state << 5;
|
||||
return state >>> 0;
|
||||
};
|
||||
for (let i = 0; i < n; i++) {
|
||||
x[i] = (next() % 20000) / 100 - 100;
|
||||
y[i] = (next() % 20000) / 100;
|
||||
fa[i] = (next() % 1000) / 7;
|
||||
fb[i] = (next() % 1000) / 9;
|
||||
data[i] = next() & 255;
|
||||
key[i] = next() & 255;
|
||||
ia[i] = next() | 0;
|
||||
ib[i] = next() | 0;
|
||||
}
|
||||
for (let i = 0; i < rgba.length; i++) rgba[i] = next() & 255;
|
||||
|
||||
const rounds = Math.round(1200 * scale);
|
||||
let f64Sum = 0;
|
||||
let f32Sum = 0;
|
||||
let byteSum = 0;
|
||||
let intSum = 0;
|
||||
let lumaSum = 0;
|
||||
for (let r = 0; r < rounds; r++) {
|
||||
// Change one input element per round so no round repeats the last.
|
||||
const at = (r * 7919) & (n - 1);
|
||||
x[at] = r;
|
||||
fa[at] = r;
|
||||
data[at] = r & 255;
|
||||
ia[at] = r * 65537;
|
||||
rgba[at * 4] = r & 255;
|
||||
axpy(1.5, x, y, d);
|
||||
blend((r & 15) / 16, fa, fb, fo);
|
||||
mask(data, key, masked);
|
||||
addWrap(ia, ib, io);
|
||||
gray(rgba, luma);
|
||||
const probe = (r * 104729) & (n - 1);
|
||||
f64Sum += d[probe]!;
|
||||
f32Sum += fo[probe]!;
|
||||
byteSum += masked[probe]!;
|
||||
intSum = (intSum + io[probe]!) | 0;
|
||||
lumaSum += luma[probe]!;
|
||||
}
|
||||
console.log(`float64 ${f64Sum.toFixed(3)}`);
|
||||
console.log(`float32 ${f32Sum.toFixed(3)}`);
|
||||
console.log(`uint8 ${byteSum}`);
|
||||
console.log(`int32 ${intSum}`);
|
||||
console.log(`gray ${lumaSum}`);
|
||||
@@ -209,6 +209,47 @@ test("emitted loops version the body and tag typed-array memory", () => {
|
||||
expect(elements).not.toBe(header);
|
||||
});
|
||||
|
||||
test("loops to a typed array's length version the other typed arrays they index", () => {
|
||||
const length = (receiver: IrExpr): IrExpr => ({
|
||||
kind: "bytesIntrinsic",
|
||||
method: "length",
|
||||
receiver,
|
||||
args: [],
|
||||
type: F64,
|
||||
loc,
|
||||
});
|
||||
const emit = (body: IrStmt[]): string => {
|
||||
const mod: IrModule = {
|
||||
irVersion: 17,
|
||||
sourceFile: loc.file,
|
||||
entry: "__main",
|
||||
functions: [
|
||||
{ name: "__main", params: [], returnType: VOID, locals: [], body: [], loc },
|
||||
fn(body),
|
||||
],
|
||||
};
|
||||
return /^define internal [^\n]*@sc_bf_kernel[^]*?^}/m.exec(emitLlvmModule(mod))![0];
|
||||
};
|
||||
// for (let i = 0; i < out.length; i++) out[i] = src[i] * 2
|
||||
const scaled = set(ref("out", f64s), ref("i"), bin("*", get(ref("src", f64s), ref("i")), num(2)));
|
||||
const body = emit([counted([scaled], length(ref("out", f64s)))]);
|
||||
const fast = /^bytes\.version\.fast\d+:$[^]*?^bytes\.version\.done\d+:$/m.exec(body)![0];
|
||||
// The loop keeps its integer induction, and the fast copy reads `src`
|
||||
// unchecked.
|
||||
expect(body).toContain("integer induction i");
|
||||
expect(fast).not.toContain("bytes.index");
|
||||
expect(body.slice(0, body.indexOf(fast))).toContain("bytes.index.invalid");
|
||||
// A body that rebinds `src` keeps one checked loop.
|
||||
const rebound = emit([
|
||||
counted(
|
||||
[scaled, { kind: "assign", localId: "src", value: ref("out", f64s), loc }],
|
||||
length(ref("out", f64s)),
|
||||
),
|
||||
]);
|
||||
expect(rebound).not.toContain("bytes.version.fast");
|
||||
expect(rebound).toContain("bytes.index.invalid");
|
||||
});
|
||||
|
||||
test("Float16Array accesses version in elements and share the element tag", () => {
|
||||
const f16s: IrType = { kind: "bytes", elem: "f16" };
|
||||
const withHalves = (body: IrStmt[]): IrFunction => {
|
||||
|
||||
@@ -945,12 +945,25 @@ export function emitStmtBody(emitter: LlEmitter, s: IrStmt): void {
|
||||
B.startBlock(done);
|
||||
break;
|
||||
}
|
||||
const version =
|
||||
const versionable =
|
||||
emitter.bytesLoopVersionsEnabled &&
|
||||
emitter.bytesLoopVersioning.size < MAX_BYTES_LOOP_VERSION_DEPTH &&
|
||||
!emitter.bytesLoopVersioning.has(s)
|
||||
? emitter.matchBytesLoopVersion(s, countedLoop)
|
||||
: null;
|
||||
!emitter.bytesLoopVersioning.has(s);
|
||||
// A loop to a typed array's or an array's length keeps its integer
|
||||
// induction. Its counted form only gives the version guard the
|
||||
// counter's iteration space, so the guard also covers accesses of
|
||||
// other typed arrays at the counter, such as `x[i]` and `y[i]` in
|
||||
// `for (let i = 0; i < out.length; i++) out[i] = x[i] * y[i]`.
|
||||
const versionCounted =
|
||||
versionable && !countedLoop && integerLoop && emitter.countedLoopsEnabled
|
||||
? matchIntegerCountedForLoop(
|
||||
s,
|
||||
emitter.numericLocals,
|
||||
emitter.integerRanges,
|
||||
emitter.unaliasedArrays,
|
||||
)
|
||||
: countedLoop;
|
||||
const version = versionable ? emitter.matchBytesLoopVersion(s, versionCounted) : null;
|
||||
if (version) {
|
||||
const fast = B.newLabel("bytes.version.fast"),
|
||||
slow = B.newLabel("bytes.version.slow"),
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/6992-typed-array-length-loop-versions.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/6993-typed-array-length-loop-bindings.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
// Loops bounded by one typed array's length that index other typed arrays:
|
||||
// equal and different lengths, offsets, mixed element types and views that
|
||||
// share a buffer. Out-of-bounds reads give undefined (NaN in arithmetic) and
|
||||
// out-of-bounds writes are ignored, whichever array is shorter.
|
||||
|
||||
function mulInto(x: Float32Array, y: Float32Array, out: Float32Array): void {
|
||||
for (let i = 0; i < out.length; i++) out[i] = x[i]! * y[i]!;
|
||||
}
|
||||
|
||||
function axpy(a: number, x: Float64Array, y: Float64Array, out: Float64Array): void {
|
||||
for (let i = 0; i < out.length; i++) out[i] = a * x[i]! + y[i]!;
|
||||
}
|
||||
|
||||
function xorInto(a: Uint8Array, b: Uint8Array, out: Uint8Array): void {
|
||||
for (let i = 0; i < out.length; i++) out[i] = a[i]! ^ b[i]!;
|
||||
}
|
||||
|
||||
function addWrap(a: Int32Array, b: Int32Array, out: Int32Array): void {
|
||||
for (let i = 0; i < out.length; i++) out[i] = (a[i]! + b[i]!) | 0;
|
||||
}
|
||||
|
||||
function narrow(src: Float64Array, i8: Int8Array, u16: Uint16Array, u32: Uint32Array): void {
|
||||
for (let i = 0; i < src.length; i++) {
|
||||
i8[i] = src[i]!;
|
||||
u16[i] = src[i]!;
|
||||
u32[i] = src[i]!;
|
||||
}
|
||||
}
|
||||
|
||||
function neighbors(a: Int32Array, out: Int32Array): void {
|
||||
for (let i = 0; i < a.length; i++) out[i] = a[i + 1]! - a[i]!;
|
||||
}
|
||||
|
||||
function previous(a: Float64Array, out: Float64Array): void {
|
||||
for (let i = 0; i < out.length; i++) out[i] = a[i - 1]!;
|
||||
}
|
||||
|
||||
function gray(px: Uint8Array, out: Uint8Array): void {
|
||||
for (let i = 0; i < out.length; i++) {
|
||||
const j = i * 4;
|
||||
out[i] = (px[j]! * 77 + px[j + 1]! * 150 + px[j + 2]! * 29) >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
function sumPairs(a: Float64Array, b: Float64Array): number {
|
||||
let s = 0;
|
||||
for (let i = 0; i < a.length; i++) s += a[i]! * b[i]!;
|
||||
return s;
|
||||
}
|
||||
|
||||
function shiftUp(view: Uint8Array, from: Uint8Array): void {
|
||||
for (let i = 0; i < view.length; i++) view[i] = from[i]! + 1;
|
||||
}
|
||||
|
||||
const n = 37;
|
||||
const fx = new Float32Array(n);
|
||||
const fy = new Float32Array(n);
|
||||
const dx = new Float64Array(n);
|
||||
const dy = new Float64Array(n);
|
||||
const ba = new Uint8Array(n);
|
||||
const bb = new Uint8Array(n);
|
||||
const ia = new Int32Array(n);
|
||||
const ib = new Int32Array(n);
|
||||
for (let i = 0; i < n; i++) {
|
||||
fx[i] = i / 3;
|
||||
fy[i] = 0.1 * i - 1.7;
|
||||
dx[i] = i * 1.25 - 9;
|
||||
dy[i] = (i * 7) % 11;
|
||||
ba[i] = (i * 29) & 255;
|
||||
bb[i] = (i * 113 + 7) & 255;
|
||||
ia[i] = i * 0x3fffffff;
|
||||
ib[i] = i * -0x2ffffff1;
|
||||
}
|
||||
|
||||
const fo = new Float32Array(n);
|
||||
mulInto(fx, fy, fo);
|
||||
console.log("f32", fo.join(","));
|
||||
const shortF = new Float32Array(5);
|
||||
mulInto(fx, fy.subarray(0, 3), shortF);
|
||||
console.log("f32 short input", shortF.join(","));
|
||||
const longF = new Float32Array(n + 4);
|
||||
mulInto(fx, fy, longF);
|
||||
console.log("f32 long output", longF.subarray(n - 2).join(","));
|
||||
|
||||
const dout = new Float64Array(n);
|
||||
axpy(-2.5, dx, dy, dout);
|
||||
console.log("axpy", dout.join(","));
|
||||
axpy(0.5, dx.subarray(10), dy, dout);
|
||||
console.log("axpy short x", dout.subarray(20).join(","));
|
||||
|
||||
const bo = new Uint8Array(n);
|
||||
xorInto(ba, bb, bo);
|
||||
console.log("xor", bo.join(","));
|
||||
const io = new Int32Array(n);
|
||||
addWrap(ia, ib, io);
|
||||
console.log("i32", io.join(","));
|
||||
|
||||
const src = new Float64Array([0, -1, 1.5, -128.5, 255.7, 256, 65535.9, -65537, 4294967296.5, NaN, Infinity, -0]);
|
||||
const i8 = new Int8Array(src.length);
|
||||
const u16 = new Uint16Array(src.length - 2);
|
||||
const u32 = new Uint32Array(src.length + 1);
|
||||
narrow(src, i8, u16, u32);
|
||||
console.log("narrow", i8.join(","), "|", u16.join(","), "|", u32.join(","));
|
||||
|
||||
const diff = new Int32Array(n);
|
||||
neighbors(ia, diff);
|
||||
console.log("neighbors", diff.subarray(n - 3).join(","));
|
||||
const before = new Float64Array(4);
|
||||
previous(dx, before);
|
||||
console.log("previous", before.join(","));
|
||||
|
||||
const px = new Uint8Array(n * 4);
|
||||
for (let i = 0; i < px.length; i++) px[i] = (i * 53) & 255;
|
||||
const g = new Uint8Array(n);
|
||||
gray(px, g);
|
||||
console.log("gray", g.join(","));
|
||||
const wide = new Uint8Array(n + 3);
|
||||
gray(px, wide);
|
||||
console.log("gray past input", wide.subarray(n - 1).join(","));
|
||||
|
||||
console.log("dot", sumPairs(dx, dy), sumPairs(dx, dy.subarray(0, 30)));
|
||||
|
||||
// Views of one buffer: writes through one are visible to reads through the
|
||||
// other on later iterations.
|
||||
const buffer = new Uint8Array(24);
|
||||
for (let i = 0; i < buffer.length; i++) buffer[i] = (i * 37) & 255;
|
||||
shiftUp(buffer.subarray(1), buffer);
|
||||
console.log("overlap forward", buffer.join(","));
|
||||
for (let i = 0; i < buffer.length; i++) buffer[i] = (i * 37) & 255;
|
||||
shiftUp(buffer, buffer.subarray(1));
|
||||
console.log("overlap backward", buffer.join(","));
|
||||
const shared = new ArrayBuffer(64);
|
||||
const asFloat = new Float32Array(shared);
|
||||
const asBytes = new Uint8Array(shared, 4, 16);
|
||||
for (let i = 0; i < asFloat.length; i++) asFloat[i] = i + 0.5;
|
||||
const fromBytes = new Float32Array(16);
|
||||
for (let i = 0; i < fromBytes.length; i++) {
|
||||
asBytes[i] = i * 3;
|
||||
fromBytes[i] = asFloat[i]!;
|
||||
}
|
||||
console.log("mixed views", fromBytes.join(","), asBytes.join(","));
|
||||
@@ -0,0 +1,146 @@
|
||||
// Loops bounded by a typed array's length whose bindings change while they
|
||||
// run, that leave early, or that run at module level, in generators and in
|
||||
// async functions. Each keeps the reads, writes and bounds the source names
|
||||
// on every iteration.
|
||||
|
||||
function swapBound(a: Float64Array, b: Float64Array, out: Float64Array): number {
|
||||
let n = 0;
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
out[i] = a[i]! + b[i]!;
|
||||
n++;
|
||||
if (i === 2) a = b;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
function swapOperand(a: Int16Array, b: Int16Array, out: Int16Array): void {
|
||||
for (let i = 0; i < out.length; i++) {
|
||||
out[i] = a[i]! * 3;
|
||||
if (i === 3) a = b;
|
||||
}
|
||||
}
|
||||
|
||||
function closureSwap(a: Uint16Array, b: Uint16Array, out: Uint16Array): void {
|
||||
const swap = (): void => {
|
||||
out = b;
|
||||
};
|
||||
for (let i = 0; i < out.length; i++) {
|
||||
out[i] = a[i]! + 1000;
|
||||
if (i === 1) swap();
|
||||
}
|
||||
}
|
||||
|
||||
function firstAbove(values: Float32Array, limits: Float32Array): number {
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
if (values[i]! > limits[i]!) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function copyUntilZero(src: Uint8Array, out: Uint8Array): number {
|
||||
let copied = 0;
|
||||
for (let i = 0; i < src.length; i++) {
|
||||
if (src[i] === 0) break;
|
||||
out[i] = src[i]!;
|
||||
copied++;
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
|
||||
function checkedScale(src: Int32Array, out: Int32Array): void {
|
||||
for (let i = 0; i < src.length; i++) {
|
||||
if (src[i]! < 0) throw new RangeError(`negative at ${i}: ${src[i]}`);
|
||||
out[i] = src[i]! * 2;
|
||||
}
|
||||
}
|
||||
|
||||
function fromList(values: number[], out: Float64Array): void {
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
out[i] = values[i]! / 2;
|
||||
if (values.length < 6) values.push(i * 10);
|
||||
}
|
||||
}
|
||||
|
||||
function rows(table: Uint32Array[], totals: Uint32Array): void {
|
||||
for (let r = 0; r < table.length; r++) {
|
||||
const row = table[r]!;
|
||||
let sum = 0;
|
||||
for (let c = 0; c < row.length; c++) sum += row[c]! * (c + 1);
|
||||
totals[r] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
function* pairs(a: Int8Array, b: Int8Array): Generator<number> {
|
||||
for (let i = 0; i < a.length; i++) yield a[i]! * b[i]!;
|
||||
}
|
||||
|
||||
async function addLater(a: Float64Array, b: Float64Array, out: Float64Array): Promise<void> {
|
||||
for (let i = 0; i < out.length; i++) {
|
||||
out[i] = a[i]! + b[i]!;
|
||||
if (i % 2 === 0) await null;
|
||||
}
|
||||
}
|
||||
|
||||
const a = new Float64Array([1, 2, 3, 4, 5, 6]);
|
||||
const b = new Float64Array([10, 20, 30]);
|
||||
const sum = new Float64Array(8);
|
||||
console.log("swap bound", swapBound(a, b, sum), sum.join(","));
|
||||
|
||||
const s16 = new Int16Array([1, -2, 3, -4, 5, -6, 7]);
|
||||
const t16 = new Int16Array([100, 200, 300, 400, 500, 600]);
|
||||
const o16 = new Int16Array(7);
|
||||
swapOperand(s16, t16, o16);
|
||||
console.log("swap operand", o16.join(","));
|
||||
|
||||
const u = new Uint16Array([5, 6, 7, 8]);
|
||||
const other = new Uint16Array(2);
|
||||
const first = new Uint16Array(4);
|
||||
closureSwap(u, other, first);
|
||||
console.log("closure swap", first.join(","), other.join(","));
|
||||
|
||||
const values = new Float32Array([0.5, 1.5, 2.5, 3.5]);
|
||||
console.log("first above", firstAbove(values, new Float32Array([1, 1, 1, 1])));
|
||||
console.log("first above short", firstAbove(values, new Float32Array([9])));
|
||||
console.log("first above none", firstAbove(values, new Float32Array([9, 9, 9, 9])));
|
||||
|
||||
const text = new Uint8Array([72, 105, 33, 0, 88]);
|
||||
const copy = new Uint8Array(2);
|
||||
console.log("copy", copyUntilZero(text, copy), copy.join(","));
|
||||
|
||||
const ints = new Int32Array([4, 8, -15, 16]);
|
||||
const doubled = new Int32Array(4);
|
||||
try {
|
||||
checkedScale(ints, doubled);
|
||||
} catch (error) {
|
||||
console.log((error as Error).name, (error as Error).message, doubled.join(","));
|
||||
}
|
||||
|
||||
const list = [3, 5];
|
||||
const halves = new Float64Array(5);
|
||||
fromList(list, halves);
|
||||
console.log("from list", list.join(","), halves.join(","));
|
||||
|
||||
const table = [new Uint32Array([1, 2, 3]), new Uint32Array([]), new Uint32Array([4294967295, 1])];
|
||||
const totals = new Uint32Array(4);
|
||||
rows(table, totals);
|
||||
console.log("rows", totals.join(","));
|
||||
|
||||
console.log("pairs", [...pairs(new Int8Array([1, -2, 3, 127]), new Int8Array([4, 5, -6]))].join(","));
|
||||
|
||||
const left = new Float64Array([0.25, 0.5, 0.75, 1]);
|
||||
const right = new Float64Array([1, 2, 3, 4, 5]);
|
||||
const later = new Float64Array(5);
|
||||
const pending = addLater(left, right, later);
|
||||
console.log("before await", later.join(","));
|
||||
|
||||
// Module-level bindings, including one a loop body reassigns.
|
||||
let source = new Uint8Array([9, 8, 7, 6, 5]);
|
||||
const replacement = new Uint8Array([1, 2]);
|
||||
const target = new Uint8Array(5);
|
||||
for (let i = 0; i < source.length; i++) {
|
||||
target[i] = source[i]! + 100;
|
||||
if (i === 1) source = replacement;
|
||||
}
|
||||
console.log("module", target.join(","), source.join(","));
|
||||
|
||||
pending.then(() => console.log("after await", later.join(",")));
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 3800, "san": 5600 }
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 2700, "san": 4000 }
|
||||
Reference in New Issue
Block a user