2023-04-05 22:55:40 +02:00
|
|
|
<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">
|
2023-04-05 22:55:40 +02:00
|
|
|
<n-card style="width: 292px; margin: 0 8px 5px">
|
2023-04-05 22:55:40 +02:00
|
|
|
<n-form-item label="Suite name:" :show-feedback="false" label-placement="left">
|
2023-04-06 13:33:51 +02:00
|
|
|
<n-input v-model:value="suite.title" placeholder="Suite name..." />
|
2023-04-05 22:55:40 +02:00
|
|
|
</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">
|
2023-04-05 22:55:40 +02:00
|
|
|
<n-button v-if="suites.length > 1" quaternary @click="suites.splice(index, 1)">
|
2023-04-05 22:55:40 +02:00
|
|
|
<template #icon>
|
|
|
|
<n-icon :component="Trash" depth="3" />
|
|
|
|
</template>
|
|
|
|
Delete suite
|
|
|
|
</n-button>
|
2023-04-05 22:55:40 +02:00
|
|
|
<n-button quaternary @click="suites.splice(index + 1, 0, { data: [0], title: `Suite ${suites.length + 1}` })">
|
2023-04-05 22:55:40 +02:00
|
|
|
<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">
|
2023-04-06 13:33:51 +02:00
|
|
|
<n-space justify="center">
|
|
|
|
<n-form-item label="Unit:" label-placement="left">
|
|
|
|
<n-input v-model:value="unit" placeholder="Unit (eg: ms)" />
|
|
|
|
</n-form-item>
|
|
|
|
|
|
|
|
<n-button
|
|
|
|
tertiary
|
|
|
|
@click="
|
|
|
|
suites = [
|
|
|
|
{ title: 'Suite 1', data: [] },
|
|
|
|
{ title: 'Suite 2', data: [] },
|
|
|
|
]
|
|
|
|
"
|
|
|
|
>Reset suites</n-button
|
|
|
|
>
|
|
|
|
</n-space>
|
|
|
|
|
2023-04-05 22:55:40 +02:00
|
|
|
<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>
|
2023-04-06 13:33:51 +02:00
|
|
|
<n-button tertiary @click="copyAsBulletList">Copy as bullet list</n-button>
|
2023-04-05 22:55:40 +02:00
|
|
|
</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] },
|
|
|
|
]);
|
|
|
|
|
2023-04-06 13:33:51 +02:00
|
|
|
const unit = useStorage('benchmark-builder:unit', '');
|
|
|
|
|
|
|
|
const round = (v: number) => Math.round(v * 1000) / 1000;
|
|
|
|
|
2023-04-05 22:55:40 +02:00
|
|
|
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)
|
2023-04-06 13:33:51 +02:00
|
|
|
.map(({ mean, variance, ...value }, index, suites) => {
|
|
|
|
const cleanUnit = unit.value.trim();
|
|
|
|
const bestMean: number = suites[0].mean;
|
|
|
|
const deltaWithBestMean = mean - bestMean;
|
|
|
|
const ratioWithBestMean = bestMean === 0 ? '∞' : round(mean / bestMean);
|
|
|
|
|
|
|
|
const comparisonValues: string =
|
|
|
|
index !== 0 && bestMean !== mean ? ` (+${round(deltaWithBestMean)}${cleanUnit} ; x${ratioWithBestMean})` : '';
|
|
|
|
|
|
|
|
return {
|
|
|
|
position: index + 1,
|
|
|
|
mean: `${round(mean)}${cleanUnit}${comparisonValues}`,
|
|
|
|
variance: `${round(variance)}${cleanUnit}${cleanUnit ? '²' : ''}`,
|
|
|
|
...value,
|
|
|
|
};
|
|
|
|
});
|
2023-04-05 22:55:40 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
const { copy } = useClipboard();
|
|
|
|
|
|
|
|
const header = {
|
2023-04-06 13:33:51 +02:00
|
|
|
title: 'Suite',
|
|
|
|
size: 'Samples',
|
2023-04-05 22:55:40 +02:00
|
|
|
mean: 'Mean',
|
|
|
|
variance: 'Variance',
|
|
|
|
position: 'Position',
|
|
|
|
};
|
|
|
|
|
|
|
|
function copyAsMarkdown() {
|
|
|
|
copy(arrayToMarkdownTable({ data: results.value, headerMap: header }));
|
|
|
|
}
|
2023-04-06 13:33:51 +02:00
|
|
|
|
|
|
|
function copyAsBulletList() {
|
|
|
|
const bulletList = results.value
|
|
|
|
.flatMap(({ title, ...sections }) => {
|
|
|
|
return [
|
|
|
|
` - ${title}`,
|
|
|
|
...Object.entries(sections).map(
|
|
|
|
([key, value]) => ` - ${header[key as keyof typeof header] ?? key}: ${value}`,
|
|
|
|
),
|
|
|
|
];
|
|
|
|
})
|
|
|
|
.join('\n');
|
|
|
|
|
|
|
|
copy(bulletList);
|
|
|
|
}
|
2023-04-05 22:55:40 +02:00
|
|
|
</script>
|
|
|
|
|
2023-04-05 22:55:40 +02:00
|
|
|
<style lang="less" scoped></style>
|