feat(new-tool): simple benchmark calculator

This commit is contained in:
Corentin Thomasset 2023-04-05 22:55:40 +02:00 committed by Corentin THOMASSET
parent 004cb83719
commit 6e84ea4061
5 changed files with 225 additions and 1 deletions

View file

@ -0,0 +1,34 @@
import _ from 'lodash';
export { computeAverage, computeVariance, arrayToMarkdownTable };
function computeAverage({ data }: { data: number[] }) {
if (data.length === 0) {
return 0;
}
return _.sum(data) / data.length;
}
function computeVariance({ data }: { data: number[] }) {
const mean = computeAverage({ data });
const squaredDiffs = data.map((value) => Math.pow(value - mean, 2));
return computeAverage({ data: squaredDiffs });
}
function arrayToMarkdownTable({ data, headerMap = {} }: { data: unknown[]; headerMap?: Record<string, string> }) {
if (!Array.isArray(data) || data.length === 0) {
return '';
}
const headers = Object.keys(data[0]);
const rows = data.map((obj) => Object.values(obj));
const headerRow = `| ${headers.map((header) => headerMap[header] ?? header).join(' | ')} |`;
const separatorRow = `| ${headers.map(() => '---').join(' | ')} |`;
const dataRows = rows.map((row) => `| ${row.join(' | ')} |`).join('\n');
return `${headerRow}\n${separatorRow}\n${dataRows}`;
}

View file

@ -0,0 +1,117 @@
<template>
<n-scrollbar style="flex: 1" x-scrollable>
<n-space :wrap="false" style="flex: 1" justify="center" :size="0">
<div v-for="(suite, index) of suites" :key="index">
<n-card style="width: 292px; margin: 0 8px">
<n-form-item label="Suite name:" :show-feedback="false" label-placement="left">
<n-input v-model:value="suite.title" />
</n-form-item>
<n-divider></n-divider>
<n-form-item label="Suite values" :show-feedback="false">
<dynamic-values v-model:values="suite.data" />
</n-form-item>
</n-card>
<n-space justify="center">
<n-button quaternary class="delete-suite" @click="suites.splice(index, 1)">
<template #icon>
<n-icon :component="Trash" depth="3" />
</template>
Delete suite
</n-button>
<n-button
quaternary
class="delete-suite"
@click="suites.splice(index + 1, 0, { data: [0], title: `Suite ${suites.length + 1}` })"
>
<template #icon>
<n-icon :component="Plus" depth="3" />
</template>
Add suite
</n-button>
</n-space>
</div>
</n-space>
<br />
</n-scrollbar>
<div style="flex: 0 0 100%">
<div style="max-width: 600px; margin: 0 auto">
<n-table>
<thead>
<tr>
<th>{{ header.position }}</th>
<th>{{ header.title }}</th>
<th>{{ header.size }}</th>
<th>{{ header.mean }}</th>
<th>{{ header.variance }}</th>
</tr>
</thead>
<tbody>
<tr v-for="{ title, size, mean, variance, position } of results" :key="title">
<td>{{ position }}</td>
<td>{{ title }}</td>
<td>{{ size }}</td>
<td>{{ mean }}</td>
<td>{{ variance }}</td>
</tr>
</tbody>
</n-table>
<br />
<n-space justify="center">
<n-button tertiary @click="copyAsMarkdown">Copy as markdown table</n-button>
</n-space>
</div>
</div>
</template>
<script setup lang="ts">
import { Trash, Plus } from '@vicons/tabler';
import { useClipboard, useStorage } from '@vueuse/core';
import _ from 'lodash';
import { computed } from 'vue';
import { computeAverage, computeVariance, arrayToMarkdownTable } from './benchmark-builder.models';
import DynamicValues from './dynamic-values.vue';
const suites = useStorage('benchmark-builder:suites', [
{ title: 'Suite 1', data: [5, 10] },
{ title: 'Suite 2', data: [8, 12] },
]);
const results = computed(() => {
return suites.value
.map(({ data: dirtyData, title }) => {
const data = dirtyData.filter(_.isNumber);
return {
title,
size: data.length,
mean: computeAverage({ data }),
variance: computeVariance({ data }),
};
})
.sort((a, b) => a.mean - b.mean)
.map((value, index) => ({ position: index + 1, ...value }));
});
const { copy } = useClipboard();
const header = {
title: 'Suite name',
size: 'Sample count',
mean: 'Mean',
variance: 'Variance',
position: 'Position',
};
function copyAsMarkdown() {
copy(arrayToMarkdownTable({ data: results.value, headerMap: header }));
}
</script>
<style lang="less" scoped>
.delete-suite {
margin-top: 15px;
}
</style>

View file

@ -0,0 +1,61 @@
<template>
<div>
<n-space v-for="(value, index) of values" :key="index" :wrap="false" style="margin-bottom: 5px" :size="5">
<n-input-number
:ref="refs.set"
v-model:value="values[index]"
:show-button="false"
placeholder="Set your measure..."
autofocus
@keydown.enter="onInputEnter(index)"
/>
<n-tooltip>
<template #trigger>
<n-button circle quaternary @click="values.splice(index, 1)">
<template #icon>
<n-icon :component="Trash" depth="3" />
</template>
</n-button>
</template>
Delete value
</n-tooltip>
</n-space>
<n-button tertiary @click="addValue">
<template #icon>
<n-icon :component="Plus" />
</template>
Add a measure
</n-button>
</div>
</template>
<script setup lang="ts">
import { Trash, Plus } from '@vicons/tabler';
import { useTemplateRefsList, useVModel } from '@vueuse/core';
import { NInputNumber } from 'naive-ui';
import { nextTick } from 'vue';
const refs = useTemplateRefsList<typeof NInputNumber>();
const props = defineProps<{ values: (number | null)[] }>();
const emit = defineEmits(['update:values']);
const values = useVModel(props, 'values', emit);
async function addValue() {
values.value.push(null);
await nextTick();
refs.value.at(-1)?.focus();
}
function onInputEnter(index: number) {
if (index === values.value.length - 1) {
addValue();
return;
}
refs.value.at(index + 1)?.focus();
}
</script>
<style scoped></style>

View file

@ -0,0 +1,11 @@
import { SpeedFilled } from '@vicons/material';
import { defineTool } from '../tool';
export const tool = defineTool({
name: 'Benchmark builder',
path: '/benchmark-builder',
description: 'Easily compare execution time of tasks with this very simple online benchmark builder.',
keywords: ['benchmark', 'builder', 'execution', 'duration', 'mean', 'variance'],
component: () => import('./benchmark-builder.vue'),
icon: SpeedFilled,
});

View file

@ -1,6 +1,7 @@
import { tool as base64FileConverter } from './base64-file-converter';
import { tool as base64StringConverter } from './base64-string-converter';
import { tool as basicAuthGenerator } from './basic-auth-generator';
import { tool as benchmarkBuilder } from './benchmark-builder';
import { tool as ipv4SubnetCalculator } from './ipv4-subnet-calculator';
import { tool as dockerRunToDockerComposeConverter } from './docker-run-to-docker-compose-converter';
import { tool as htmlWysiwygEditor } from './html-wysiwyg-editor';
@ -107,7 +108,7 @@ export const toolsByCategory: ToolCategory[] = [
},
{
name: 'Measurement',
components: [chronometer, temperatureConverter],
components: [chronometer, temperatureConverter, benchmarkBuilder],
},
{
name: 'Text',