diff --git a/benchmark/react/cases/004-various-update/index.tsx b/benchmark/react/cases/004-various-update/index.tsx
new file mode 100644
index 0000000000..e828593dc5
--- /dev/null
+++ b/benchmark/react/cases/004-various-update/index.tsx
@@ -0,0 +1,204 @@
+// Copyright 2025 The Lynx Authors. All rights reserved.
+// Licensed under the Apache License Version 2.0 that can be found in the
+// LICENSE file in the root directory of this source tree.
+
+import { root, useEffect, useState } from '@lynx-js/react';
+import { SnapshotInstance } from '@lynx-js/react/internal';
+import type { CSSProperties, MainThread, NodesRef } from '@lynx-js/types';
+
+import { hook, isMainThread } from '../../src/hook.js';
+
+if (__MAIN_THREAD__) {
+ hook(
+ SnapshotInstance.prototype,
+ 'setAttribute',
+ function(this: SnapshotInstance, old, key, value) {
+ if (!this.__elements) {
+ // skip when there is no underlying FiberElement
+ return old!.call(this, key, value);
+ }
+
+ let name = '';
+ switch (key) {
+ case 0:
+ name = 'Attr';
+ break;
+ case 1:
+ name = 'Dataset';
+ break;
+ case 2:
+ name = 'Event';
+ break;
+ case 3:
+ name = 'MT_Event';
+ break;
+ case 4:
+ name = 'StyleString';
+ break;
+ case 5:
+ name = 'StyleObject';
+ break;
+ case 6:
+ name = 'Class';
+ break;
+ case 7:
+ name = 'Id';
+ break;
+ case 8:
+ name = 'Ref';
+ break;
+ case 9:
+ name = 'TimingFlag';
+ break;
+ case 10:
+ name = 'MT_Ref';
+ break;
+ case 11:
+ name = 'ListItemPlatformInfo';
+ break;
+ case 12:
+ name = 'ListItemPlatformInfoSpread';
+ break;
+ case 13:
+ name = 'Spread';
+ break;
+ case 'values':
+ name = 'BatchedValues';
+ break;
+ }
+
+ if (!name) {
+ // skip what we are not concerned
+ return old!.call(this, key, value);
+ }
+
+ Codspeed.startBenchmark();
+ const ret = old!.call(this, key, value);
+ Codspeed.stopBenchmark();
+ Codspeed.setExecutedBenchmark(
+ `${__REPO_FILEPATH__}::${__webpack_chunkname__}-setAttribute__${name}`,
+ );
+
+ return ret;
+ },
+ );
+}
+
+function F() {
+ const [stopBenchmark, setStopBenchmark] = useState(false);
+ const [values, setValues] = useState(
+ [
+ 'some-exposure-id' as string,
+ 'some-dataset' as string,
+ () => {},
+ () => {
+ 'main thread';
+ },
+ 'width: 100rpx; height: 100rpx; background-color: #FACE00;' as string,
+ {
+ width: '100rpx',
+ height: '100rpx',
+ backgroundColor: '#FACE00',
+ } as CSSProperties,
+ 'some-css-class' as string,
+ 'some-id' as string,
+ (_e: NodesRef) => {},
+ 'some_lynx_timing_flag' as string,
+ (_e: MainThread.Element) => {
+ 'main thread';
+ },
+ 'some-item-key' as string,
+ ] as const,
+ );
+
+ useEffect(() => {
+ setValues([
+ 'some-other-exposure-id',
+ 'some-other-dataset',
+ () => {},
+ () => {
+ 'main thread';
+ },
+ 'width: 200rpx; height: 100rpx; background-color: #FACE00;',
+ {
+ width: '200rpx',
+ height: '100rpx',
+ backgroundColor: '#FACE00',
+ },
+ 'some-other-css-class',
+ 'some-other-id',
+ (_e: NodesRef) => {},
+ 'some_other_lynx_timing_flag',
+ (_e: MainThread.Element) => {
+ 'main thread';
+ },
+ 'some-other-item-key',
+ ]);
+ setStopBenchmark(true);
+ }, []);
+
+ if (isMainThread) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+root.render(
+ ,
+);
diff --git a/benchmark/react/lynx.config.js b/benchmark/react/lynx.config.js
index 80d0974327..451c28de76 100644
--- a/benchmark/react/lynx.config.js
+++ b/benchmark/react/lynx.config.js
@@ -37,6 +37,9 @@ export default defineConfig({
'./src/patchUpdateListCallbacks.ts',
'./cases/003-hello-list/index.tsx',
],
+ '004-various-update': [
+ './cases/004-various-update/index.tsx',
+ ],
},
},
plugins: [
diff --git a/benchmark/react/package.json b/benchmark/react/package.json
index 99ca743eeb..e7a854c345 100644
--- a/benchmark/react/package.json
+++ b/benchmark/react/package.json
@@ -8,12 +8,14 @@
"bench:001-fib": "benchx_cli run dist/001-fib.lynx.bundle",
"bench:002-hello-reactLynx": "benchx_cli run dist/002-hello-reactLynx.lynx.bundle --wait-for-id=stop-benchmark-true",
"bench:003-hello-list": "benchx_cli run dist/003-hello-list.lynx.bundle --wait-for-id=stop-benchmark-true",
+ "bench:004-various-update": "benchx_cli run dist/004-various-update.lynx.bundle --wait-for-id=stop-benchmark-true",
"build": "rspeedy build",
"dev": "rspeedy dev",
"perfetto": "pnpm run --sequential --stream --aggregate-output '/^perfetto:.*/'",
"perfetto:001-fib": "benchx_cli -o dist/001-fib.ptrace run dist/001-fib.lynx.bundle",
"perfetto:002-hello-reactLynx": "benchx_cli -o dist/002-hello-reactLynx.ptrace run dist/002-hello-reactLynx.lynx.bundle --wait-for-id=stop-benchmark-true",
"perfetto:003-hello-list": "benchx_cli -o dist/003-hello-list.ptrace run dist/003-hello-list.lynx.bundle --wait-for-id=stop-benchmark-true",
+ "perfetto:004-various-update": "benchx_cli -o dist/004-various-update.ptrace run dist/004-various-update.lynx.bundle --wait-for-id=stop-benchmark-true",
"test": "echo 'No tests specified'"
},
"dependencies": {
diff --git a/benchmark/react/src/hook.ts b/benchmark/react/src/hook.ts
index 492c02de1c..5254c17d3e 100644
--- a/benchmark/react/src/hook.ts
+++ b/benchmark/react/src/hook.ts
@@ -16,3 +16,7 @@ export function hook(
}
export const PREFIX = __REPO_FILEPATH__.split('/').slice(0, -2).join('/');
+
+export const isMainThread =
+ // @ts-expect-error safely check
+ typeof __CreatePage === 'function';