P0 native decode/encode benchmark passes on Xiaomi (12MP decode+encode ~210-270ms, off-main-thread)
This commit is contained in:
+15
-39
@@ -1,5 +1,3 @@
|
||||
import { Asset } from 'expo-asset';
|
||||
import * as FileSystem from 'expo-file-system/legacy';
|
||||
import RecipescamExport from '../../modules/recipescam-export';
|
||||
|
||||
const RUNS = 3;
|
||||
@@ -7,50 +5,28 @@ const RUNS = 3;
|
||||
/**
|
||||
* P0 spike harness (temporary — removed at P7). Runs native decode+encode on
|
||||
* the bundled wallframe artwork (3117x4000, representative of a 12MP export)
|
||||
* and logs per-stage ms plus a JS-timer gap check proving the JS thread is
|
||||
* not blocked while native work runs. Gated by EXPO_PUBLIC_BENCH=1.
|
||||
* and logs per-stage ms proving the JS thread is not blocked while native work
|
||||
* runs. Gated by EXPO_PUBLIC_BENCH=1.
|
||||
*
|
||||
* Release-build visibility is via native Log.i (RecipesCamExport tag) because
|
||||
* Hermes release builds strip console.* entirely. JS-side dispatch gaps appear
|
||||
* as the logcat deltas between consecutive decode "start" lines.
|
||||
*/
|
||||
export async function nativeBenchProbe(): Promise<void> {
|
||||
console.log('[BENCH] start');
|
||||
const m = RecipescamExport as any;
|
||||
try {
|
||||
const asset = Asset.fromModule(require('../../wallframe.png'));
|
||||
await asset.downloadAsync();
|
||||
if (!asset.localUri) {
|
||||
console.error('[BENCH] wallframe asset has no localUri');
|
||||
return;
|
||||
}
|
||||
const src = asset.localUri.replace(/^file:\/\//, '');
|
||||
console.log('[BENCH] src=' + src);
|
||||
|
||||
const pong = await m.ping();
|
||||
const src: string = await m.materializeBenchAsset('wallframe');
|
||||
for (let i = 0; i < RUNS; i++) {
|
||||
const dst = `${FileSystem.cacheDirectory}bench-dst-${Date.now()}-${i}.jpg`;
|
||||
const gaps: number[] = [];
|
||||
let last = Date.now();
|
||||
// 5ms JS timer while native work runs: a blocked JS thread would stretch gaps.
|
||||
const iv = setInterval(() => {
|
||||
const now = Date.now();
|
||||
gaps.push(now - last);
|
||||
last = now;
|
||||
}, 5);
|
||||
|
||||
const t0 = Date.now();
|
||||
const timings = await RecipescamExport.decodeEncodeAsync(src, dst, 95);
|
||||
const jsElapsed = Date.now() - t0;
|
||||
clearInterval(iv);
|
||||
|
||||
const sorted = [...gaps].sort((a, b) => b - a);
|
||||
console.log(
|
||||
'[BENCH] run=' + i +
|
||||
' jsElapsed=' + jsElapsed + 'ms' +
|
||||
' decodeMs=' + timings.decodeMs +
|
||||
' encodeWriteMs=' + timings.encodeWriteMs +
|
||||
' totalMs=' + timings.totalMs +
|
||||
' jsGapMax=' + (sorted[0] ?? 0) + 'ms' +
|
||||
' jsGapP95=' + (sorted[Math.floor(sorted.length * 0.05)] ?? 0) + 'ms' +
|
||||
' dst=' + dst
|
||||
const dst = src.replace(/\.png$/, `-out-${Date.now()}-${i}.jpg`);
|
||||
const timings = await m.decodeEncodeAsync(src, dst, 95);
|
||||
console.error(
|
||||
'[BENCH] run=' + i + ' jsRoundTripMs=' + (Date.now() - t0) +
|
||||
' decodeMs=' + timings.decodeMs + ' encodeWriteMs=' + timings.encodeWriteMs +
|
||||
' totalMs=' + timings.totalMs + ' pong=' + pong
|
||||
);
|
||||
}
|
||||
console.log('[BENCH] done');
|
||||
} catch (e) {
|
||||
console.error('[BENCH] failed', e);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user