Filtered search benchmarks (#112)

* new: add filtered search bench draft

* new: update filtered search benchmark post

* new: render article

* refactoring: add newlines

* new: add filtered search bench file structure, add chart

* fix: fix plot preselect

* fix: fix canvas id

* debug: debug js

* debug: add benchmark filter search script to header

* fix: fix filter search plot selection

* new: update benchmark data

* refactoring: remove commented code

* fix: fix bench data

* new: fix labels order

* new: sort dataset labels

* new: add some text

* fix: fix descriptions

* debug: fix mapping not allowed yaml parsing error

* new: update bench data

* fix: fix dataset name

* new: update bench data

* fix: fix qdrant kw small vocab

* text for the articles

* upd text

* fix: fix typos

* new: replace bars with scatter plots

* fix: fix method calls

* debug: add initializer

* fix: replace debug const with variable

* new: remove data based split, remain only one plot

* fix: remove geo data for milvus

* new: remove range data for weaviate

* new: remove int 2048 for milvus, as it crashes at the moment

* upd graphs

* link

---------

Co-authored-by: Andrey Vasnetsov <andrey@vasnetsov.com>
This commit is contained in:
George
2023-02-16 15:40:22 +01:00
committed by GitHub
co-authored by Andrey Vasnetsov
parent 9e0fe47712
commit faad182279
11 changed files with 2022 additions and 3 deletions
@@ -30,6 +30,14 @@
</div>
{{ end }}
{{ if .Params.filter_data }}
<div class="row clearfix">
<section class="content-side col-lg-12 col-md-12 col-sm-12">
{{ partial "benchmark_filter_chart" . }}
</section>
</div>
{{ end }}
<article class="article article_benchmarks article_narrow">
{{ .Content }}
@@ -12,6 +12,16 @@
{{ end }}
{{ if .Params.filter_data }}
<div class="auto-container pt-3 pt-md-5 single-page">
<div class="row clearfix">
<section class="content-side col-lg-12 col-md-12 col-sm-12">
{{ partial "benchmark_filter_chart" . }}
</section>
</div>
</div>
{{ end }}
<div class="auto-container pt-3 pt-md-2 mb-5 single-page">
<div class="row clearfix">
@@ -110,8 +110,6 @@
fetch(url)
.then(res => res.json())
.then(data => {
console.log(data[0])
const datasets = getDatasetsList(data);
updataDropdown(datasetSelector, datasets);
@@ -0,0 +1,128 @@
<div class="benchmark-wrapper">
<label for="datasets-selector-{{ .Params.id }}">Dataset:</label>
<select name="datasets" id="datasets-selector-{{ .Params.id }}"
onchange="renderFilterSelected('{{ .Params.id }}')">
</select>
Plot values:
<!-- Radio button group to select values to render -->
<label>
<input checked type="radio" name="plot-value-{{ .Params.id }}" value="regular_search"
onclick="updateFilterSelected('{{ .Params.id }}', this.value)"/>
Regular search
</label> |
<label>
<input type="radio" name="plot-value-{{ .Params.id }}" value="filter_search"
onclick="updateFilterSelected('{{ .Params.id }}', this.value)"/>
Filter search
</label> |
<canvas id="chart-{{ .Params.id }}"></canvas>
<i> Download raw data: <a href="{{ .Params.filter_data }}">here</a> </i>
</div>
<script type="module">
let url = "{{ .Params.filter_data }}";
const config = {
type: 'scatter',
data: {
datasets: []
},
options: {
responsive: true,
scales: {
x: {
type: 'linear',
title: {
display: true,
text: 'Precision'
},
min: -0.1,
max: 1.1,
ticks: {
callback: function (value) {
if (value > 1.0) {
return "";
}
if (value < 0.0) {
return "";
}
return value
}
}
},
y: {
type: 'linear',
title: {
display: true,
text: 'RPS'
},
min: -30,
ticks: {
callback: function (value) {
return value > 0 ? value.toFixed(0) : "";
}
}
}
},
plugins: {
tooltip: {
callbacks: {
label: function (tooltipItem) {
return [
tooltipItem.dataset.label
];
},
title: function (tooltipItem) {
return "Precision: " + parseFloat(tooltipItem[0].parsed.x).toFixed(2) + ", RPS: " + parseFloat(tooltipItem[0].parsed.y).toFixed(2);
}
}
},
}
}
};
const chart = new Chart(
document.getElementById('chart-{{ .Params.id }}'),
config
);
let datasetSelector = document.getElementById("datasets-selector-{{ .Params.id }}");
fetch(url)
.then(res => res.json())
.then(data => {
// data - raw data from json file, contains _all_ results
const datasets = getDatasetsList(data);
// datasets - names of the datasets, e.g. "range-100", "range-100-no-filters"
let cleanedDatasetsSet = new Set();
for (let dataset of datasets) {
cleanedDatasetsSet.add(dataset.replace('-no-filters', '').replace('-filters', ''));
}
// remove "keyword-100" from the list of datasets, as we want to reorder it manually
cleanedDatasetsSet.delete("keyword-100");
let cleanedDatasets = ["keyword-100", ...cleanedDatasetsSet];
updataDropdown(datasetSelector, cleanedDatasets);
window.datasets = {"{{ .Params.id }}": data, ...window.datasets}
window.charts = {"{{ .Params.id }}": chart, ...window.charts}
renderFilterSelected("{{ .Params.id }}");
});
</script>
@@ -68,6 +68,7 @@
{{ if eq .Section "benchmarks" }}
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="{{ "js/benchmarks.js" | absURL }}"></script>
<script src="{{ "js/benchmarks_filtered_search.js" | absURL }}"></script>
{{ end }}
<script src="/js/qdr-scroll.min.js" type="module"></script>
@@ -0,0 +1,126 @@
// const ENGINES = [
// "qdrant", "weaviate", "milvus", "redis", "elastic"
// ]
function getFilterSelectedData(chartId) {
let data = window.datasets[chartId];
let datasetSelector = document.getElementById("datasets-selector-" + chartId);
let filteredDatasetName = getSelectedValue(datasetSelector) + '-filters';
let unFilteredDatasetName = getSelectedValue(datasetSelector) + '-no-filters';
return {
"filtered": filterData(data, {
"dataset_name": filteredDatasetName,
}),
"unfiltered": filterData(data, {
"dataset_name": unFilteredDatasetName,
})
}
}
function getFilterPlotDataForEngine(data, engine) {
let filtered = filterData(data, { "engine_name": engine });
let values = [{ "x": 0.0, "y": 0.0 }]
if (filtered.length > 0) {
values[0]["x"] = filtered[0]['mean_precisions'] || 0.0
values[0]["y"] = filtered[0]['rps'] || 0.0
}
return {
label: engine,
data: values,
backgroundColor: engineToColor[engine],
radius: 10,
hoverRadius: 13,
};
}
function getFilterPlotData(data) {
let plotData = [];
let all_uniq_engines = new Set(data.map((d) => d.engine_name));
// To sorted array
all_uniq_engines = [...all_uniq_engines].sort();
for (const engine of all_uniq_engines) {
plotData.push(getFilterPlotDataForEngine(data, engine));
}
return plotData;
}
function renderFilterSelected(chartId) {
// Set default value for radio button
let searchTypeSelector = document.getElementsByName("plot-value-" + chartId);
searchTypeSelector[0].checked = true;
let chart = window.charts[chartId];
let { filtered: filteredData, unfiltered: unFilteredData } = getFilterSelectedData(chartId);
/*
filteredData = [
{
"engine_name": "qdrant",
"p95_time": 0.010619221997512793,
"rps": 1118.8951442866735,
"p99_time": 0.024652249003083857,
"mean_time": 0.00700474982189371,
"mean_precisions": 0.4396960000000001,
"engine_params": {
"parallel": 8,
"search_params": {
"hnsw_ef": 128
}
},
"setup_name": "qdrant-m-16-ef-128",
"dataset_name": "range-100-filters",
"parallel": 8,
"upload_time": 44.026487107999856,
"total_upload_time": 680.002582219
}
]
*/
// Get max value for y axis, ignore underfined data
let maxRPSValue = Math.max(...filteredData.map((d) => d.rps || 0), ...unFilteredData.map((d) => d.rps || 0));
// Render unfiltered data by default
let plotData = getFilterPlotData(unFilteredData);
chart.options.scales.y.max = (maxRPSValue / 100).toFixed() * 100 + 100;
renderPlot(chart, plotData);
}
function updatePlot(chart, plotData) {
chart.data.datasets.forEach((dataset, idx) => {
dataset.data = plotData[idx].data;
});
chart.update();
}
function updateFilterSelected(chartId) {
let chart = window.charts[chartId];
let { filtered: filteredData, unfiltered: unFilteredData } = getFilterSelectedData(chartId);
// Based on radio button value, render filtered or unfiltered data
let data = [];
let searchTypeSelector = document.getElementsByName("plot-value-" + chartId);
let searchType = getRadioButtonValue(searchTypeSelector);
if (searchType === "filter_search") {
data = filteredData;
} else {
data = unFilteredData;
}
let plotData = getFilterPlotData(data);
updatePlot(chart, plotData);
}