parent
f1328e22a8
commit
47358a6b9a
@ -0,0 +1,254 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>M-Tree vs Sequential Search Comparison</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
#results {
|
||||||
|
padding: 20px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>M-Tree vs Sequential Search Comparison</h1>
|
||||||
|
<form id="test-form">
|
||||||
|
<label>
|
||||||
|
Dataset size (points):
|
||||||
|
<input type="number" id="dataset-size-min" value="1000" step="1000" />
|
||||||
|
to
|
||||||
|
<input type="number" id="dataset-size-max" value="100000" step="1000" />
|
||||||
|
by
|
||||||
|
<input type="number" id="dataset-size-step" value="1000" step="1000" />
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
<label>
|
||||||
|
Dimension:
|
||||||
|
<input type="number" id="dimension-min" value="2" step="1" />
|
||||||
|
to
|
||||||
|
<input type="number" id="dimension-max" value="10" step="1" />
|
||||||
|
by
|
||||||
|
<input type="number" id="dimension-step" value="1" step="1" />
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
<label>
|
||||||
|
M-Tree node size:
|
||||||
|
<input type="number" id="m-tree-node-size-min" value="4" step="1" />
|
||||||
|
to
|
||||||
|
<input type="number" id="m-tree-node-size-max" value="10" step="1" />
|
||||||
|
by
|
||||||
|
<input type="number" id="m-tree-node-size-step" value="1" step="1" />
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
<button id="run-tests" type="submit">Run tests</button>
|
||||||
|
</form>
|
||||||
|
<div id="results"></div>
|
||||||
|
<br>
|
||||||
|
<button id="export-results" type="button">Export results to CSV</button>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const exportButton = document.getElementById('export-results');
|
||||||
|
exportButton.addEventListener('click', () => {
|
||||||
|
const table = resultsDiv.firstElementChild;
|
||||||
|
const csv = [];
|
||||||
|
for (const row of table.rows) {
|
||||||
|
const rowArray = [];
|
||||||
|
for (const cell of row.cells) {
|
||||||
|
rowArray.push(cell.textContent);
|
||||||
|
}
|
||||||
|
csv.push(rowArray.join(','));
|
||||||
|
}
|
||||||
|
const csvString = csv.join('\n');
|
||||||
|
const blob = new Blob([csvString], { type: 'text/csv' });
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = URL.createObjectURL(blob);
|
||||||
|
//const configName = `dataset_${datasetSizeMin}`;//-${datasetSizeMax}_dimension_${dimensionMin}-${dimensionMax}_nodeSize_${mTreeNodeSizeMin}-${mTreeNodeSizeMax}`;
|
||||||
|
link.download = `m-tree-test_${document.getElementById('dataset-size-min').value}.csv`;
|
||||||
|
link.click();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const form = document.getElementById('test-form');
|
||||||
|
const resultsDiv = document.getElementById('results');
|
||||||
|
|
||||||
|
form.addEventListener('submit', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const datasetSizeMin = Number(document.getElementById('dataset-size-min').value);
|
||||||
|
const datasetSizeMax = Number(document.getElementById('dataset-size-max').value);
|
||||||
|
const datasetSizeStep = Number(document.getElementById('dataset-size-step').value);
|
||||||
|
const dimensionMin = Number(document.getElementById('dimension-min').value);
|
||||||
|
const dimensionMax = Number(document.getElementById('dimension-max').value);
|
||||||
|
const dimensionStep = Number(document.getElementById('dimension-step').value);
|
||||||
|
const mTreeNodeSizeMin = Number(document.getElementById('m-tree-node-size-min').value);
|
||||||
|
const mTreeNodeSizeMax = Number(document.getElementById('m-tree-node-size-max').value);
|
||||||
|
const mTreeNodeSizeStep = Number(document.getElementById('m-tree-node-size-step').value);
|
||||||
|
|
||||||
|
resultsDiv.innerHTML = '';
|
||||||
|
|
||||||
|
runTestsLoop(datasetSizeMin, datasetSizeMax, datasetSizeStep, dimensionMin, dimensionMax, dimensionStep, mTreeNodeSizeMin, mTreeNodeSizeMax, mTreeNodeSizeStep);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function runTestsLoop(datasetSizeMin, datasetSizeMax, datasetSizeStep, dimensionMin, dimensionMax, dimensionStep, mTreeNodeSizeMin, mTreeNodeSizeMax, mTreeNodeSizeStep) {
|
||||||
|
|
||||||
|
const table = document.createElement('table');
|
||||||
|
const thead = document.createElement('thead');
|
||||||
|
const tbody = document.createElement('tbody');
|
||||||
|
table.appendChild(thead);
|
||||||
|
table.appendChild(tbody);
|
||||||
|
resultsDiv.appendChild(table);
|
||||||
|
|
||||||
|
const headerRow = document.createElement('tr');
|
||||||
|
['Dataset size', 'Dimension', 'M-Tree node size', 'Tree creation time', 'Range query time (MTree)', 'Range query time (Sequential)', 'KNN query time (MTree)', 'KNN query time (Sequential)', 'MTree range query calls', 'Sequential range query calls', 'MTree KNN query calls', 'Sequential KNN query calls']
|
||||||
|
.forEach(header => {
|
||||||
|
headerRow.appendChild(document.createElement('th')).textContent = header;
|
||||||
|
});
|
||||||
|
thead.appendChild(headerRow);
|
||||||
|
|
||||||
|
for (let datasetSize = datasetSizeMin; datasetSize <= datasetSizeMax; datasetSize += datasetSizeStep) {
|
||||||
|
for (let dimension = dimensionMin; dimension <= dimensionMax; dimension += dimensionStep) {
|
||||||
|
for (let mTreeNodeSize = mTreeNodeSizeMin; mTreeNodeSize <= mTreeNodeSizeMax; mTreeNodeSize += mTreeNodeSizeStep) {
|
||||||
|
const result = await runTests(datasetSize, dimension, mTreeNodeSize);
|
||||||
|
|
||||||
|
const row = document.createElement('tr');
|
||||||
|
[
|
||||||
|
datasetSize,
|
||||||
|
dimension,
|
||||||
|
mTreeNodeSize,
|
||||||
|
`${Number(result.timingResults.treeCreationTime).toFixed(5)}`,
|
||||||
|
`${Number(result.timingResults.rangeQueryTime.mtreeRangeQuery).toFixed(5)}`,
|
||||||
|
`${Number(result.timingResults.rangeQueryTime.sequentialSearch).toFixed(5)}`,
|
||||||
|
`${Number(result.timingResults.knnQueryTime.mtreeKNNQuery).toFixed(5)}`,
|
||||||
|
`${Number(result.timingResults.knnQueryTime.sequentialSearch).toFixed(5)}`,
|
||||||
|
`${result.functionCallCounts.rangeQuery.mtreeRangeQuery}`,
|
||||||
|
`${result.functionCallCounts.rangeQuery.sequentialSearch}`,
|
||||||
|
`${result.functionCallCounts.knnQuery.mtreeKNNQuery}`,
|
||||||
|
`${result.functionCallCounts.knnQuery.sequentialSearch}`,
|
||||||
|
].forEach((value, index) => {
|
||||||
|
row.appendChild(document.createElement('td')).textContent = value;
|
||||||
|
});
|
||||||
|
tbody.appendChild(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function runTests(datasetSize, dimension, mTreeNodeSize) {
|
||||||
|
console.log(`running tests for dataset size ${datasetSize}, dimension ${dimension}, m-tree node size ${mTreeNodeSize}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const startTime = performance.now();
|
||||||
|
const timingResults = {
|
||||||
|
treeCreationTime: 0,
|
||||||
|
rangeQueryTime: {
|
||||||
|
mtreeRangeQuery: 0,
|
||||||
|
sequentialSearch: 0
|
||||||
|
},
|
||||||
|
knnQueryTime: {
|
||||||
|
mtreeKNNQuery: 0,
|
||||||
|
sequentialSearch: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const functionCallCounts = {
|
||||||
|
rangeQuery: {
|
||||||
|
mtreeRangeQuery: 0,
|
||||||
|
sequentialSearch: 0
|
||||||
|
},
|
||||||
|
knnQuery: {
|
||||||
|
mtreeKNNQuery: 0,
|
||||||
|
sequentialSearch: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (let j = 0; j < 5; j++) {
|
||||||
|
|
||||||
|
const response = await fetch('/recreate', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
dimensions: dimension,
|
||||||
|
pointCount: datasetSize,
|
||||||
|
capacity: mTreeNodeSize,
|
||||||
|
distanceFunction: 'euclidean'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`error recreating tree: ${await response.text()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { treeCreationTime } = await response.json();
|
||||||
|
timingResults.treeCreationTime += treeCreationTime;
|
||||||
|
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
|
||||||
|
const queryPoint = Array(dimension).fill(i / 5);
|
||||||
|
const knnResponse = await fetch(`/kNNQuery?queryPoint=${encodeURIComponent(JSON.stringify(queryPoint))}&k=${encodeURIComponent((i + 1)*3)}`);
|
||||||
|
|
||||||
|
if (!knnResponse.ok) {
|
||||||
|
throw new Error(`error running KNN query: ${knnResponse.status} ${await knnResponse.text()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const knnResult = await knnResponse.json();
|
||||||
|
const radius = knnResult.values[knnResult.values.length - 1].distance;
|
||||||
|
|
||||||
|
const rangeResponse = await fetch(`/rangeQuery?queryPoint=${encodeURIComponent(JSON.stringify(queryPoint))}&radius=${encodeURIComponent(radius)}`);
|
||||||
|
|
||||||
|
if (!rangeResponse.ok) {
|
||||||
|
throw new Error(`error running range query: ${rangeResponse.status} ${await rangeResponse.text()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const rangeResult = await rangeResponse.json();
|
||||||
|
if (!rangeResult || !rangeResult.timingResult || !rangeResult.functionCalls) {
|
||||||
|
throw new Error('error parsing result');
|
||||||
|
}
|
||||||
|
|
||||||
|
timingResults.rangeQueryTime.mtreeRangeQuery += rangeResult.timingResult.mtreeRangeQuery;
|
||||||
|
functionCallCounts.rangeQuery.mtreeRangeQuery += rangeResult.functionCalls.mtreeRangeQuery;
|
||||||
|
timingResults.rangeQueryTime.sequentialSearch += rangeResult.timingResult.sequentialSearch;
|
||||||
|
functionCallCounts.rangeQuery.sequentialSearch += rangeResult.functionCalls.sequentialSearch;
|
||||||
|
timingResults.knnQueryTime.mtreeKNNQuery += knnResult.timingResult.mtreeKNNQuery;
|
||||||
|
functionCallCounts.knnQuery.mtreeKNNQuery += knnResult.functionCalls.mtreeKNNQuery;
|
||||||
|
timingResults.knnQueryTime.sequentialSearch += knnResult.timingResult.sequentialSearch;
|
||||||
|
functionCallCounts.knnQuery.sequentialSearch += knnResult.functionCalls.sequentialSearch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timingResults.treeCreationTime /= 5;
|
||||||
|
timingResults.rangeQueryTime.mtreeRangeQuery /= 25;
|
||||||
|
functionCallCounts.rangeQuery.mtreeRangeQuery /= 25;
|
||||||
|
timingResults.rangeQueryTime.sequentialSearch /= 25;
|
||||||
|
functionCallCounts.rangeQuery.sequentialSearch /= 25;
|
||||||
|
timingResults.knnQueryTime.mtreeKNNQuery /= 25;
|
||||||
|
functionCallCounts.knnQuery.mtreeKNNQuery /= 25;
|
||||||
|
timingResults.knnQueryTime.sequentialSearch /= 25;
|
||||||
|
functionCallCounts.knnQuery.sequentialSearch /= 25;
|
||||||
|
|
||||||
|
return { timingResults, functionCallCounts };
|
||||||
|
|
||||||
|
const endTime = performance.now();
|
||||||
|
console.log(`finished at ${endTime} after ${endTime - startTime}ms`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('error running tests:', error);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Loading…
Reference in new issue