You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

254 lines
12 KiB

<!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 < 2; j++) {
const response = await fetch('/recreate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
dimensions: dimension,
pointCount: datasetSize,
capacity: mTreeNodeSize,
distanceFunction: 'euclidean'
}),
timeout: 6000000
});
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 < 4; i++) {
const queryPoint = Array(dimension).fill(i / 4);
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 /= 2;
timingResults.rangeQueryTime.mtreeRangeQuery /= 8;
functionCallCounts.rangeQuery.mtreeRangeQuery /= 8;
timingResults.rangeQueryTime.sequentialSearch /= 8;
functionCallCounts.rangeQuery.sequentialSearch /= 8;
timingResults.knnQueryTime.mtreeKNNQuery /= 8;
functionCallCounts.knnQuery.mtreeKNNQuery /= 8;
timingResults.knnQueryTime.sequentialSearch /= 8;
functionCallCounts.knnQuery.sequentialSearch /= 8;
const endTime = performance.now();
console.log(`finished at ${endTime} after ${endTime - startTime}ms`);
return { timingResults, functionCallCounts };
} catch (error) {
console.error('error running tests:', error);
return {};
}
}
</script>
</body>
</html>

Powered by TurnKey Linux.