diff --git a/benchmark/react/lynx.config.js b/benchmark/react/lynx.config.js
index 4f4df1d39c..feccce3d66 100644
--- a/benchmark/react/lynx.config.js
+++ b/benchmark/react/lynx.config.js
@@ -15,6 +15,7 @@ export default defineConfig({
jsOptions: {
minimizerOptions: {
mangle: false,
+ minify: false,
},
},
},
@@ -28,7 +29,6 @@ export default defineConfig({
'./cases/001-fib/index.ts',
],
'002-hello-reactLynx': [
- 'event-target-polyfill',
'./src/patchProfile.ts',
'./cases/002-hello-reactLynx/index.tsx',
],
diff --git a/benchmark/react/src/RunBenchmarkUntil.tsx b/benchmark/react/src/RunBenchmarkUntil.tsx
index 2fbe655a3a..d8cb4f1e2c 100644
--- a/benchmark/react/src/RunBenchmarkUntil.tsx
+++ b/benchmark/react/src/RunBenchmarkUntil.tsx
@@ -2,6 +2,16 @@
// 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 { useEffect, useState } from '@lynx-js/react';
+
export const RunBenchmarkUntilHydrate = () => {
return ;
};
+
+export const RunBenchmarkUntilEffect = () => {
+ const [stopBenchmark, setStopBenchmark] = useState(false);
+ useEffect(() => {
+ setStopBenchmark(true);
+ }, []);
+ return ;
+};
diff --git a/benchmark/react/src/patchProfile.ts b/benchmark/react/src/patchProfile.ts
index 755f7da113..1544211679 100644
--- a/benchmark/react/src/patchProfile.ts
+++ b/benchmark/react/src/patchProfile.ts
@@ -4,37 +4,66 @@
import { hook } from './hook.js';
-if (__BACKGROUND__) {
- // eslint-disable-next-line no-global-assign
- console = { ...console };
-}
-
const PREFIX = __REPO_FILEPATH__.split('/').slice(0, -2).join('/');
const ignored: Record = {};
const stack: string[] = [];
+let depth = 0;
-hook(console, 'profile', (old, name) => {
- old!(name);
- if (
- (ignored[name!] ??= name === 'commitChanges'
- || name!.startsWith('ReactLynx::diff::'))
- ) {
- stack.push('__IGNORED__');
- } else {
- stack.push(name!);
+if (typeof Codspeed !== 'undefined') {
+ function shouldIgnoreBenchmark(name: string | undefined) {
+ if (
+ !name
+ || name === 'ReactLynx::commit'
+ || name === 'ReactLynx::commitChanges'
+ || name === 'ReactLynx::transferRoot'
+ || name === 'ReactLynx::BSI::setAttribute'
+ || name.startsWith('OnLifecycleEvent::')
+ || name.startsWith('ReactLynx::diff::')
+ || name.startsWith('ReactLynx::render::')
+ ) {
+ return true;
+ }
+ return false;
}
- Codspeed.startBenchmark();
-});
-hook(console, 'profileEnd', (old) => {
- Codspeed.stopBenchmark();
- const name = stack.pop();
- if (name === '__IGNORED__') {
- Codspeed.zeroStats();
- } else {
- Codspeed.setExecutedBenchmark(
- `${PREFIX}::${__webpack_chunkname__}-${name!}`,
- );
- }
- old!();
-});
+ hook(lynx.performance, 'profileStart', (old, name, option) => {
+ old!.call(lynx.performance, name, option);
+ if ((ignored[name] ??= shouldIgnoreBenchmark(name))) {
+ stack.push('__IGNORED__');
+ } else {
+ stack.push(name);
+ depth++;
+ if (depth > 1) {
+ console.log(
+ `Benchmark ${name} is ignored because it is nested, the stack: ${
+ stack.join(', ')
+ }`,
+ );
+ } else {
+ Codspeed.startBenchmark();
+ }
+ }
+ });
+
+ hook(lynx.performance, 'profileEnd', (old) => {
+ const name = stack.pop()!;
+ if (name === '__IGNORED__') {
+ // Codspeed.zeroStats();
+ } else {
+ if (depth > 1) {
+ // ignored
+ } else {
+ Codspeed.stopBenchmark();
+ Codspeed.setExecutedBenchmark(
+ `${PREFIX}::${__webpack_chunkname__}-${
+ name
+ .replace(/^ReactLynx::/, '')
+ .replace(/::/g, '__')
+ }`,
+ );
+ }
+ depth--;
+ }
+ old!.call(lynx.performance);
+ });
+}
diff --git a/packages/lynx/benchx_cli/scripts/build.mjs b/packages/lynx/benchx_cli/scripts/build.mjs
index 3a308edce9..55d9e0cc36 100644
--- a/packages/lynx/benchx_cli/scripts/build.mjs
+++ b/packages/lynx/benchx_cli/scripts/build.mjs
@@ -30,7 +30,7 @@ console.log('noop')
}
const COMMIT = 'd6dd806293012c62e5104ad7ed2bed5c66f4f833';
-const PICK_COMMIT = '033e8243747fa0b3ffc01e4b2df321d73a30597f';
+const PICK_COMMIT = '1524ac02060bfc2e354e1865c989bc6f7a9882b1';
function checkCwd() {
try {
diff --git a/packages/react/runtime/src/backgroundSnapshot.ts b/packages/react/runtime/src/backgroundSnapshot.ts
index f93d00b9cd..6f72835dc0 100644
--- a/packages/react/runtime/src/backgroundSnapshot.ts
+++ b/packages/react/runtime/src/backgroundSnapshot.ts
@@ -10,6 +10,7 @@
import type { Worklet } from '@lynx-js/react/worklet-runtime/bindings';
+import { profileEnd, profileStart } from './debug/utils.js';
import { processGestureBackground } from './gesture/processGestureBagkround.js';
import type { GestureKind } from './gesture/types.js';
import { diffArrayAction, diffArrayLepus } from './hydrate.js';
@@ -212,7 +213,7 @@ export class BackgroundSnapshotInstance {
setAttribute(key: string | number, value: unknown): void {
if (__PROFILE__) {
- console.profile('setAttribute');
+ profileStart('ReactLynx::BSI::setAttribute');
}
if (key === 'values') {
if (__globalSnapshotPatch) {
@@ -260,7 +261,7 @@ export class BackgroundSnapshotInstance {
}
this.__values = value as unknown[];
if (__PROFILE__) {
- console.profileEnd();
+ profileEnd();
}
return;
}
@@ -279,7 +280,7 @@ export class BackgroundSnapshotInstance {
value,
);
if (__PROFILE__) {
- console.profileEnd();
+ profileEnd();
}
}
diff --git a/packages/react/runtime/src/debug/utils.ts b/packages/react/runtime/src/debug/utils.ts
new file mode 100644
index 0000000000..8fe37662f9
--- /dev/null
+++ b/packages/react/runtime/src/debug/utils.ts
@@ -0,0 +1,23 @@
+// Copyright 2024 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.
+
+/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
+
+const noop = () => {};
+
+export const profileStart = /* @__PURE__ */ ((() => {
+ let p;
+ if (!(p = lynx.performance) || typeof p.profileStart !== 'function') {
+ return noop;
+ }
+ return p.profileStart.bind(p);
+})()) as typeof lynx.performance.profileStart;
+
+export const profileEnd = /* @__PURE__ */ ((() => {
+ let p;
+ if (!(p = lynx.performance) || typeof p.profileEnd !== 'function') {
+ return noop;
+ }
+ return p.profileEnd.bind(p);
+})()) as typeof lynx.performance.profileEnd;
diff --git a/packages/react/runtime/src/lifecycle/destroy.ts b/packages/react/runtime/src/lifecycle/destroy.ts
index ab21014241..39c24055a3 100644
--- a/packages/react/runtime/src/lifecycle/destroy.ts
+++ b/packages/react/runtime/src/lifecycle/destroy.ts
@@ -7,10 +7,11 @@ import { __root } from '../root.js';
import { delayedEvents } from './event/delayEvents.js';
import { delayedLifecycleEvents } from './event/delayLifecycleEvents.js';
import { globalCommitTaskMap } from './patch/commit.js';
+import { profileEnd, profileStart } from '../debug/utils.js';
function destroyBackground(): void {
if (__PROFILE__) {
- console.profile('destroyBackground');
+ profileStart('ReactLynx::destroyBackground');
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
@@ -27,7 +28,7 @@ function destroyBackground(): void {
delayedEvents.length = 0;
}
if (__PROFILE__) {
- console.profileEnd();
+ profileEnd();
}
}
diff --git a/packages/react/runtime/src/lifecycle/event/jsReady.ts b/packages/react/runtime/src/lifecycle/event/jsReady.ts
index 9bed196a2b..526451b866 100644
--- a/packages/react/runtime/src/lifecycle/event/jsReady.ts
+++ b/packages/react/runtime/src/lifecycle/event/jsReady.ts
@@ -1,6 +1,7 @@
// 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 { profileEnd, profileStart } from '../../debug/utils.js';
import { LifecycleConstant } from '../../lifecycleConstant.js';
import { __root } from '../../root.js';
@@ -9,13 +10,25 @@ let jsReadyEventIdSwap: Record;
function jsReady(): void {
isJSReady = true;
+
+ if (__PROFILE__) {
+ profileStart('ReactLynx::transferRoot');
+ profileStart('ReactLynx::serializeRoot');
+ }
+ const root = JSON.stringify(__root);
+ if (__PROFILE__) {
+ profileEnd();
+ }
__OnLifecycleEvent([
LifecycleConstant.firstScreen, /* FIRST_SCREEN */
{
- root: JSON.stringify(__root),
+ root,
jsReadyEventIdSwap,
},
]);
+ if (__PROFILE__) {
+ profileEnd();
+ }
jsReadyEventIdSwap = {};
}
diff --git a/packages/react/runtime/src/lifecycle/patch/commit.ts b/packages/react/runtime/src/lifecycle/patch/commit.ts
index b85df21440..67cac9ba55 100644
--- a/packages/react/runtime/src/lifecycle/patch/commit.ts
+++ b/packages/react/runtime/src/lifecycle/patch/commit.ts
@@ -31,6 +31,7 @@ import { takeWorkletRefInitValuePatch } from '../../worklet/workletRefPool.js';
import { getReloadVersion } from '../pass.js';
import type { SnapshotPatch } from './snapshotPatch.js';
import { takeGlobalSnapshotPatch } from './snapshotPatch.js';
+import { profileEnd, profileStart } from '../../debug/utils.js';
let globalFlushOptions: FlushOptions = {};
function takeGlobalFlushOptions() {
@@ -181,7 +182,7 @@ function commitPatchUpdate(patchList: PatchList, patchOptions: GlobalPatchOption
// console.debug('commitPatchUpdate:', prettyFormatSnapshotPatch(patchList.patchList[0]?.snapshotPatch));
if (__PROFILE__) {
- console.profile('commitChanges');
+ profileStart('ReactLynx::commitChanges');
}
markTiming('packChangesStart');
const obj: {
@@ -200,7 +201,7 @@ function commitPatchUpdate(patchList: PatchList, patchOptions: GlobalPatchOption
setPipeline(undefined);
}
if (__PROFILE__) {
- console.profileEnd();
+ profileEnd();
}
return obj;
diff --git a/packages/react/runtime/src/lifecycle/reload.ts b/packages/react/runtime/src/lifecycle/reload.ts
index ed2760d838..6a973d53fe 100644
--- a/packages/react/runtime/src/lifecycle/reload.ts
+++ b/packages/react/runtime/src/lifecycle/reload.ts
@@ -23,10 +23,11 @@ import { increaseReloadVersion } from './pass.js';
import { deinitGlobalSnapshotPatch } from './patch/snapshotPatch.js';
import { shouldDelayUiOps } from './ref/delay.js';
import { renderMainThread } from './render.js';
+import { profileEnd, profileStart } from '../debug/utils.js';
function reloadMainThread(data: unknown, options: UpdatePageOption): void {
if (__PROFILE__) {
- console.profile('reloadTemplate');
+ profileStart('ReactLynx::reloadMainThread');
}
increaseReloadVersion();
@@ -64,14 +65,14 @@ function reloadMainThread(data: unknown, options: UpdatePageOption): void {
__FlushElementTree(__page, options);
if (__PROFILE__) {
- console.profileEnd();
+ profileEnd();
}
return;
}
function reloadBackground(updateData: Record): void {
if (__PROFILE__) {
- console.profile('reload');
+ profileStart('ReactLynx::reloadBackground');
}
deinitGlobalSnapshotPatch();
@@ -88,7 +89,7 @@ function reloadBackground(updateData: Record): void {
render(__root.__jsx, __root as any);
if (__PROFILE__) {
- console.profileEnd();
+ profileEnd();
}
}
diff --git a/packages/react/runtime/src/lifecycle/render.ts b/packages/react/runtime/src/lifecycle/render.ts
index b2abb12d3a..e3b50106c6 100644
--- a/packages/react/runtime/src/lifecycle/render.ts
+++ b/packages/react/runtime/src/lifecycle/render.ts
@@ -8,6 +8,7 @@
import { isValidElement } from 'preact';
+import { profileEnd, profileStart } from '../debug/utils.js';
import { renderOpcodesInto } from '../opcodes.js';
import { render as renderToString } from '../renderToOpcodes/index.js';
import { __root } from '../root.js';
@@ -17,7 +18,7 @@ function renderMainThread(): void {
let opcodes;
try {
if (__PROFILE__) {
- console.profile('renderToString');
+ profileStart('ReactLynx::renderMainThread');
}
opcodes = renderToString(__root.__jsx, undefined);
} catch (e) {
@@ -25,7 +26,7 @@ function renderMainThread(): void {
opcodes = [];
} finally {
if (__PROFILE__) {
- console.profileEnd();
+ profileEnd();
}
}
@@ -40,7 +41,7 @@ function renderMainThread(): void {
}
if (__PROFILE__) {
- console.profile('renderOpcodesInto');
+ profileStart('ReactLynx::renderOpcodes');
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
renderOpcodesInto(opcodes, __root as any);
@@ -48,7 +49,7 @@ function renderMainThread(): void {
__root.__opcodes = opcodes;
}
if (__PROFILE__) {
- console.profileEnd();
+ profileEnd();
}
}
diff --git a/packages/react/runtime/src/lynx-api.ts b/packages/react/runtime/src/lynx-api.ts
index 71f17c9213..185e116b01 100644
--- a/packages/react/runtime/src/lynx-api.ts
+++ b/packages/react/runtime/src/lynx-api.ts
@@ -7,6 +7,7 @@ import { useState } from 'preact/hooks';
import type { Consumer, FC, ReactNode } from 'react';
import { factory, withInitDataInState } from './compat/initData.js';
+import { profileEnd, profileStart } from './debug/utils.js';
import { useLynxGlobalEventListener } from './hooks/useLynxGlobalEventListener.js';
import { LifecycleConstant } from './lifecycleConstant.js';
import { flushDelayedLifecycleEvents } from './lynx/tt.js';
@@ -94,8 +95,14 @@ export const root: Root = {
preactProcess = cb;
};
try {
+ if (__PROFILE__) {
+ profileStart('ReactLynx::renderBackground');
+ }
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
render(jsx, __root as any);
+ if (__PROFILE__) {
+ profileEnd();
+ }
(preactProcess as (() => void) | undefined)?.();
} finally {
options.debounceRendering = oldDebounceRendering!;
diff --git a/packages/react/runtime/src/lynx/env.ts b/packages/react/runtime/src/lynx/env.ts
index 519698de7d..616942b9fb 100644
--- a/packages/react/runtime/src/lynx/env.ts
+++ b/packages/react/runtime/src/lynx/env.ts
@@ -1,6 +1,7 @@
// Copyright 2024 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 { profileEnd, profileStart } from '../debug/utils.js';
import type { DataProcessorDefinition, InitData, InitDataRaw } from '../lynx-api.js';
export function setupLynxEnv(): void {
@@ -51,7 +52,7 @@ export function setupLynxEnv(): void {
let hasDefaultDataProcessorExecuted = false;
globalThis.processData = (data, processorName) => {
if (__PROFILE__) {
- console.profile('processData');
+ profileStart('processData');
}
let r: InitData | InitDataRaw;
@@ -70,7 +71,7 @@ export function setupLynxEnv(): void {
}
if (__PROFILE__) {
- console.profileEnd();
+ profileEnd();
}
if (hasDefaultDataProcessorExecuted === false) {
diff --git a/packages/react/runtime/src/lynx/tt.ts b/packages/react/runtime/src/lynx/tt.ts
index 51a3b6cdfe..1cc0f84e94 100644
--- a/packages/react/runtime/src/lynx/tt.ts
+++ b/packages/react/runtime/src/lynx/tt.ts
@@ -8,6 +8,7 @@ import type { FirstScreenData } from '../lifecycleConstant.js';
import { PerformanceTimingFlags, PipelineOrigins, beginPipeline, markTiming } from './performance.js';
import { BackgroundSnapshotInstance, hydrate } from '../backgroundSnapshot.js';
import { runWithForce } from './runWithForce.js';
+import { profileEnd, profileStart } from '../debug/utils.js';
import { destroyBackground } from '../lifecycle/destroy.js';
import { delayedEvents, delayedPublishEvent } from '../lifecycle/event/delayEvents.js';
import { delayLifecycleEvent, delayedLifecycleEvents } from '../lifecycle/event/delayLifecycleEvents.js';
@@ -52,7 +53,7 @@ function onLifecycleEvent([type, data]: [LifecycleConstant, unknown]) {
}
if (__PROFILE__) {
- console.profile(`OnLifecycleEvent::${type}`);
+ profileStart(`OnLifecycleEvent::${type}`);
}
try {
@@ -62,7 +63,7 @@ function onLifecycleEvent([type, data]: [LifecycleConstant, unknown]) {
}
if (__PROFILE__) {
- console.profileEnd();
+ profileEnd();
}
}
@@ -71,7 +72,7 @@ function onLifecycleEventImpl(type: LifecycleConstant, data: unknown): void {
case LifecycleConstant.firstScreen: {
const { root: lepusSide, jsReadyEventIdSwap } = data as FirstScreenData;
if (__PROFILE__) {
- console.profile('hydrate');
+ profileStart('ReactLynx::hydrate');
}
beginPipeline(true, PipelineOrigins.reactLynxHydrate, PerformanceTimingFlags.reactLynxHydrate);
markTiming('hydrateParseSnapshotStart');
@@ -83,7 +84,7 @@ function onLifecycleEventImpl(type: LifecycleConstant, data: unknown): void {
__root as BackgroundSnapshotInstance,
);
if (__PROFILE__) {
- console.profileEnd();
+ profileEnd();
}
markTiming('diffVdomEnd');
@@ -109,9 +110,6 @@ function onLifecycleEventImpl(type: LifecycleConstant, data: unknown): void {
// console.debug("********** After hydration:");
// printSnapshotInstance(__root as BackgroundSnapshotInstance);
- if (__PROFILE__) {
- console.profile('commitChanges');
- }
const commitTaskId = genCommitTaskId();
const patchList: PatchList = {
patchList: [{ snapshotPatch, id: commitTaskId }],
diff --git a/packages/react/runtime/src/snapshot/gesture.ts b/packages/react/runtime/src/snapshot/gesture.ts
index 3927a2d02e..8f1307d7e1 100644
--- a/packages/react/runtime/src/snapshot/gesture.ts
+++ b/packages/react/runtime/src/snapshot/gesture.ts
@@ -16,15 +16,9 @@ export function updateGesture(
if (!snapshot.__elements) {
return;
}
- if (__PROFILE__) {
- console.profile('updateGesture');
- }
const value = snapshot.__values![expIndex] as GestureKind;
if (workletType === 'main-thread') {
processGesture(snapshot.__elements[elementIndex]!, value, oldValue as GestureKind, isMainThreadHydrating);
}
- if (__PROFILE__) {
- console.profileEnd();
- }
}
diff --git a/packages/react/runtime/vitest.config.ts b/packages/react/runtime/vitest.config.ts
index a363785a67..5b0ce2a1be 100644
--- a/packages/react/runtime/vitest.config.ts
+++ b/packages/react/runtime/vitest.config.ts
@@ -88,6 +88,7 @@ export default defineConfig({
'src/root.ts',
'src/debug/component-stack.ts',
'src/debug/debug.ts',
+ 'src/debug/utils.ts',
'src/lynx/calledByNative.ts',
'src/lynx/component.ts',
'src/lynx/dynamic-js.ts',