From cee98352948bfa309aad119910ffa9fea6a7dc6a Mon Sep 17 00:00:00 2001
From: Manoj Vivek
Date: Tue, 14 Feb 2023 10:44:22 +0530
Subject: [PATCH] Benchmark tool support to fetch data from custom apiEndpoint
---
.../ProfileIcicleGraph-12H.benchmark.tsx | 39 +++++++++++++++++
.../ProfileIcicleGraph-15M.benchmark.tsx | 39 +++++++++++++++++
...sx => ProfileIcicleGraph-1D.benchmark.tsx} | 9 ++--
...sx => ProfileIcicleGraph-1H.benchmark.tsx} | 9 ++--
...sx => ProfileIcicleGraph-3D.benchmark.tsx} | 11 ++---
.../ProfileIcicleGraph-6H.benchmark.tsx | 39 +++++++++++++++++
.../benchmarks/benchdata/common.js | 12 ++++++
.../benchmarks/benchdata/populateData.js | 35 +++++++++-------
.../benchmarks/benchdata/populateData.js | 29 ++++++++-----
ui/scripts/README.md | 42 +++++++++++++++++++
ui/scripts/run-benchmark.mts | 15 ++++---
11 files changed, 236 insertions(+), 43 deletions(-)
create mode 100644 ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-12H.benchmark.tsx
create mode 100644 ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-15M.benchmark.tsx
rename ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/{ProfileIcicleGraph-10M.benchmark.tsx => ProfileIcicleGraph-1D.benchmark.tsx} (84%)
rename ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/{ProfileIcicleGraph-20M.benchmark.tsx => ProfileIcicleGraph-1H.benchmark.tsx} (84%)
rename ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/{ProfileIcicleGraph-1M.benchmark.tsx => ProfileIcicleGraph-3D.benchmark.tsx} (81%)
create mode 100644 ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-6H.benchmark.tsx
create mode 100644 ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/benchdata/common.js
create mode 100644 ui/scripts/README.md
diff --git a/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-12H.benchmark.tsx b/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-12H.benchmark.tsx
new file mode 100644
index 00000000000..c851933a41a
--- /dev/null
+++ b/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-12H.benchmark.tsx
@@ -0,0 +1,39 @@
+// Copyright 2022 The Parca Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import React from 'react';
+import ProfileIcicleGraph from '..';
+import {Provider} from 'react-redux';
+import {store} from '@parca/store';
+import parcaGraphData from './benchdata/parca-12h.json';
+import {Flamegraph} from '@parca/client';
+
+const {store: reduxStore} = store();
+
+const parcaGraph = parcaGraphData as Flamegraph;
+
+export default function ({callback = () => {}}): React.ReactElement {
+ return (
+
+ );
+}
diff --git a/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-15M.benchmark.tsx b/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-15M.benchmark.tsx
new file mode 100644
index 00000000000..7b2465dfaad
--- /dev/null
+++ b/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-15M.benchmark.tsx
@@ -0,0 +1,39 @@
+// Copyright 2022 The Parca Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import React from 'react';
+import ProfileIcicleGraph from '..';
+import {Provider} from 'react-redux';
+import {store} from '@parca/store';
+import parcaGraphData from './benchdata/parca-15m.json';
+import {Flamegraph} from '@parca/client';
+
+const {store: reduxStore} = store();
+
+const parcaGraph = parcaGraphData as Flamegraph;
+
+export default function ({callback = () => {}}): React.ReactElement {
+ return (
+
+ );
+}
diff --git a/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-10M.benchmark.tsx b/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-1D.benchmark.tsx
similarity index 84%
rename from ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-10M.benchmark.tsx
rename to ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-1D.benchmark.tsx
index bbb61a9b301..e0ee5cc03d6 100644
--- a/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-10M.benchmark.tsx
+++ b/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-1D.benchmark.tsx
@@ -15,20 +15,21 @@ import React from 'react';
import ProfileIcicleGraph from '..';
import {Provider} from 'react-redux';
import {store} from '@parca/store';
-import parca10mGraphData from './benchdata/parca-10m.json';
+import parcaGraphData from './benchdata/parca-1d.json';
import {Flamegraph} from '@parca/client';
const {store: reduxStore} = store();
-const parca10mGraph = parca10mGraphData as Flamegraph;
+const parcaGraph = parcaGraphData as Flamegraph;
export default function ({callback = () => {}}): React.ReactElement {
return (
{}}
/>
diff --git a/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-20M.benchmark.tsx b/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-1H.benchmark.tsx
similarity index 84%
rename from ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-20M.benchmark.tsx
rename to ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-1H.benchmark.tsx
index 4fd9f53649b..f81960fd40e 100644
--- a/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-20M.benchmark.tsx
+++ b/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-1H.benchmark.tsx
@@ -15,20 +15,21 @@ import React from 'react';
import ProfileIcicleGraph from '..';
import {Provider} from 'react-redux';
import {store} from '@parca/store';
-import parca20mGraphData from './benchdata/parca-20m.json';
+import parcaGraphData from './benchdata/parca-1h.json';
import {Flamegraph} from '@parca/client';
const {store: reduxStore} = store();
-const parca20mGraph = parca20mGraphData as Flamegraph;
+const parcaGraph = parcaGraphData as Flamegraph;
export default function ({callback = () => {}}): React.ReactElement {
return (
{}}
/>
diff --git a/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-1M.benchmark.tsx b/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-3D.benchmark.tsx
similarity index 81%
rename from ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-1M.benchmark.tsx
rename to ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-3D.benchmark.tsx
index 4ea1ad8a6e6..1b6e8da9ebd 100644
--- a/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-1M.benchmark.tsx
+++ b/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-3D.benchmark.tsx
@@ -12,23 +12,24 @@
// limitations under the License.
import React from 'react';
-import ProfileIcicleGraph from '../';
+import ProfileIcicleGraph from '..';
import {Provider} from 'react-redux';
import {store} from '@parca/store';
+import parcaGraphData from './benchdata/parca-3d.json';
import {Flamegraph} from '@parca/client';
-import parca1mGraphData from './benchdata/parca-1m.json';
const {store: reduxStore} = store();
-const parca1mGraph = parca1mGraphData as Flamegraph;
+const parcaGraph = parcaGraphData as Flamegraph;
export default function ({callback = () => {}}): React.ReactElement {
return (
{}}
/>
diff --git a/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-6H.benchmark.tsx b/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-6H.benchmark.tsx
new file mode 100644
index 00000000000..3a1f68198d9
--- /dev/null
+++ b/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/ProfileIcicleGraph-6H.benchmark.tsx
@@ -0,0 +1,39 @@
+// Copyright 2022 The Parca Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import React from 'react';
+import ProfileIcicleGraph from '..';
+import {Provider} from 'react-redux';
+import {store} from '@parca/store';
+import parcaGraphData from './benchdata/parca-6h.json';
+import {Flamegraph} from '@parca/client';
+
+const {store: reduxStore} = store();
+
+const parcaGraph = parcaGraphData as Flamegraph;
+
+export default function ({callback = () => {}}): React.ReactElement {
+ return (
+
+ );
+}
diff --git a/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/benchdata/common.js b/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/benchdata/common.js
new file mode 100644
index 00000000000..b26e83dd106
--- /dev/null
+++ b/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/benchdata/common.js
@@ -0,0 +1,12 @@
+import commandLineArgs from 'command-line-args';
+
+const optionDefinitions = [{name: 'apiEndpoint', type: String}];
+const options = commandLineArgs(optionDefinitions);
+
+export const getApiEndPoint = () => {
+ return options.apiEndpoint ?? 'https://demo.parca.dev';
+};
+
+export const getGrpcMetadata = () => {
+ return {meta: JSON.parse(process.env.GRPC_METADATA ?? '{}')};
+};
diff --git a/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/benchdata/populateData.js b/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/benchdata/populateData.js
index 006bf4fb8df..6bbb94f11a0 100644
--- a/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/benchdata/populateData.js
+++ b/ui/packages/shared/profile/src/ProfileIcicleGraph/benchmarks/benchdata/populateData.js
@@ -17,12 +17,13 @@ const fs = require('fs-extra');
const path = require('path');
// const {fileURLToPath} = require('url');
const fetch = require('node-fetch');
+const {getApiEndPoint, getGrpcMetadata} = require('./common');
globalThis.fetch = fetch;
globalThis.Headers = fetch.Headers;
const DIR_NAME = __dirname; // path.dirname(fileURLToPath(import.meta.url));
-const apiEndpoint = 'https://demo.parca.dev';
+const apiEndpoint = getApiEndPoint();
const queryClient = new client.QueryServiceClient(
new GrpcWebFetchTransport({
@@ -35,18 +36,21 @@ const populateDataIfNeeded = async (from, filename) => {
if (Object.keys(await readFile(filePath)).length > 0) {
return;
}
- const {response} = await queryClient.query({
- options: {
- oneofKind: 'merge',
- merge: {
- start: client.Timestamp.fromDate(from),
- end: client.Timestamp.fromDate(new Date()),
- query: 'parca_agent_cpu:samples:count:cpu:nanoseconds:delta{container="parca"}',
+ const {response} = await queryClient.query(
+ {
+ options: {
+ oneofKind: 'merge',
+ merge: {
+ start: client.Timestamp.fromDate(from),
+ end: client.Timestamp.fromDate(new Date()),
+ query: 'parca_agent_cpu:samples:count:cpu:nanoseconds:delta{}',
+ },
},
+ reportType: client.QueryRequest_ReportType.FLAMEGRAPH_TABLE,
+ mode: client.QueryRequest_Mode.MERGE,
},
- reportType: client.QueryRequest_ReportType.FLAMEGRAPH_TABLE,
- mode: client.QueryRequest_Mode.MERGE,
- });
+ getGrpcMetadata()
+ );
if (response.report.oneofKind !== 'flamegraph') {
throw new Error('Expected flamegraph report');
}
@@ -68,9 +72,12 @@ const readFile = async filename => {
const run = async () => {
await Promise.all([
- populateDataIfNeeded(new Date(new Date().getTime() - 1000 * 60), 'parca-1m.json'),
- populateDataIfNeeded(new Date(new Date().getTime() - 1000 * 60 * 10), 'parca-10m.json'),
- populateDataIfNeeded(new Date(new Date().getTime() - 1000 * 60 * 20), 'parca-20m.json'),
+ populateDataIfNeeded(new Date(new Date().getTime() - 1000 * 60 * 15), 'parca-15m.json'),
+ populateDataIfNeeded(new Date(new Date().getTime() - 1000 * 60 * 60), 'parca-1h.json'),
+ populateDataIfNeeded(new Date(new Date().getTime() - 1000 * 60 * 60 * 6), 'parca-6h.json'),
+ populateDataIfNeeded(new Date(new Date().getTime() - 1000 * 60 * 60 * 12), 'parca-12h.json'),
+ populateDataIfNeeded(new Date(new Date().getTime() - 1000 * 60 * 60 * 24), 'parca-1d.json'),
+ populateDataIfNeeded(new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * 3), 'parca-3d.json'),
]);
};
diff --git a/ui/packages/shared/profile/src/TopTable/benchmarks/benchdata/populateData.js b/ui/packages/shared/profile/src/TopTable/benchmarks/benchdata/populateData.js
index b65d46b8924..fec577c4771 100644
--- a/ui/packages/shared/profile/src/TopTable/benchmarks/benchdata/populateData.js
+++ b/ui/packages/shared/profile/src/TopTable/benchmarks/benchdata/populateData.js
@@ -17,12 +17,16 @@ const fs = require('fs-extra');
const path = require('path');
// const {fileURLToPath} = require('url');
const fetch = require('node-fetch');
+const {
+ getApiEndPoint,
+ getGrpcMetadata,
+} = require('../../../ProfileIcicleGraph/benchmarks/benchdata/common');
globalThis.fetch = fetch;
globalThis.Headers = fetch.Headers;
const DIR_NAME = __dirname; // path.dirname(fileURLToPath(import.meta.url));
-const apiEndpoint = 'https://demo.parca.dev';
+const apiEndpoint = getApiEndPoint();
const queryClient = new client.QueryServiceClient(
new GrpcWebFetchTransport({
@@ -35,18 +39,21 @@ const populateDataIfNeeded = async (from, filename) => {
if (Object.keys(await readFile(filePath)).length > 0) {
return;
}
- const {response} = await queryClient.query({
- options: {
- oneofKind: 'merge',
- merge: {
- start: client.Timestamp.fromDate(from),
- end: client.Timestamp.fromDate(new Date()),
- query: 'parca_agent_cpu:samples:count:cpu:nanoseconds:delta{container="parca"}',
+ const {response} = await queryClient.query(
+ {
+ options: {
+ oneofKind: 'merge',
+ merge: {
+ start: client.Timestamp.fromDate(from),
+ end: client.Timestamp.fromDate(new Date()),
+ query: 'parca_agent_cpu:samples:count:cpu:nanoseconds:delta{container="parca"}',
+ },
},
+ reportType: client.QueryRequest_ReportType.TOP,
+ mode: client.QueryRequest_Mode.MERGE,
},
- reportType: client.QueryRequest_ReportType.TOP,
- mode: client.QueryRequest_Mode.MERGE,
- });
+ getGrpcMetadata()
+ );
if (response.report.oneofKind !== 'top') {
throw new Error('Expected topTable report');
}
diff --git a/ui/scripts/README.md b/ui/scripts/README.md
new file mode 100644
index 00000000000..152db59f101
--- /dev/null
+++ b/ui/scripts/README.md
@@ -0,0 +1,42 @@
+# Benchmark Runner
+
+It is a simple benchmark tool that loads the given React component in the browser and measures the time it takes to render the component. It also has support for running dataPopulation scripts that can be used to populate the data from the remote api that the components need to render.
+
+## Usage
+
+```bash
+$ yarn benchmark
+```
+
+## Options
+
+```bash
+yarn benchmark --pattern "ProfileIcicleGraph*"
+```
+
+To run only the benchmarks that match the given pattern.
+
+```bash
+yarn benchmark --pattern "ProfileIcicleGraph*" --name flamegraphBefore
+```
+
+To run only the benchmarks that match the given pattern and save the result with the given name.
+
+```bash
+yarn benchmark --pattern "ProfileIcicleGraph*" --compare "flamegraphBefore"
+```
+
+To run only the benchmarks that match the given pattern and compare them to the given result name.
+
+```bash
+yarn benchmark --apiEndpoint='https://api.example.com'
+```
+
+To run the dataPopulation script against a different API endpoint.
+
+```bash
+export GRPC_METADATA='{"key":"value","authorization":"Bearer ...."}'
+yarn benchmark --apiEndpoint='https://api.example.com'
+```
+
+To run the dataPopulation script against a different API endpoint with custom headers.
diff --git a/ui/scripts/run-benchmark.mts b/ui/scripts/run-benchmark.mts
index 311d921a3ba..ae5b35ad3bc 100644
--- a/ui/scripts/run-benchmark.mts
+++ b/ui/scripts/run-benchmark.mts
@@ -31,6 +31,7 @@ const optionDefinitions = [
{name: 'debug', alias: 'd', type: Boolean},
{name: 'compare', alias: 'c', type: String},
{name: 'pattern', alias: 'p', type: String, defaultValue: '*'},
+ {name: 'apiEndpoint', type: String},
];
const options = commandLineArgs(optionDefinitions);
const IS_DEBUG = process.env.DEBUG === 'true' || options.debug === true;
@@ -160,11 +161,15 @@ const populateBenchmarkData = async (): Promise => {
try {
stopwatch.start();
spinner.start(`Running data population script: ${file}`);
- const {stdout} = await execa('babel-node', [
- '--config-file',
- path.join(DIR_NAME, '../babel.config.cjs'),
- path.join(DIR_NAME, `../${file}`),
- ]);
+ const {stdout} = await execa(
+ 'babel-node',
+ [
+ '--config-file',
+ path.join(DIR_NAME, '../babel.config.cjs'),
+ path.join(DIR_NAME, `../${file}`),
+ options.apiEndpoint != null ? `--apiEndpoint=${options.apiEndpoint}` : '',
+ ].filter(Boolean)
+ );
console.log('stdout', stdout);
spinner.succeed(`Data population script: ${file} completed ${stopwatch.stopAndReset()}ms`);
} catch (error) {