Files
code-server/src/vs/base/test/common/skipList.test.ts
Joe Previte be3e823608 Squashed 'lib/vscode/' content from commit e5a624b788
git-subtree-dir: lib/vscode
git-subtree-split: e5a624b788d92b8d34d1392e4c4d9789406efe8f
2020-12-15 15:52:33 -07:00

219 lines
6.1 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { SkipList } from 'vs/base/common/skipList';
import { StopWatch } from 'vs/base/common/stopwatch';
import { binarySearch } from 'vs/base/common/arrays';
suite('SkipList', function () {
function assertValues<V>(list: SkipList<any, V>, expected: V[]) {
assert.equal(list.size, expected.length);
assert.deepEqual([...list.values()], expected);
let valuesFromEntries = [...list.entries()].map(entry => entry[1]);
assert.deepEqual(valuesFromEntries, expected);
let valuesFromIter = [...list].map(entry => entry[1]);
assert.deepEqual(valuesFromIter, expected);
let i = 0;
list.forEach((value, _key, map) => {
assert.ok(map === list);
assert.deepEqual(value, expected[i++]);
});
}
function assertKeys<K>(list: SkipList<K, any>, expected: K[]) {
assert.equal(list.size, expected.length);
assert.deepEqual([...list.keys()], expected);
let keysFromEntries = [...list.entries()].map(entry => entry[0]);
assert.deepEqual(keysFromEntries, expected);
let keysFromIter = [...list].map(entry => entry[0]);
assert.deepEqual(keysFromIter, expected);
let i = 0;
list.forEach((_value, key, map) => {
assert.ok(map === list);
assert.deepEqual(key, expected[i++]);
});
}
test('set/get/delete', function () {
let list = new SkipList<number, number>((a, b) => a - b);
assert.equal(list.get(3), undefined);
list.set(3, 1);
assert.equal(list.get(3), 1);
assertValues(list, [1]);
list.set(3, 3);
assertValues(list, [3]);
list.set(1, 1);
list.set(4, 4);
assert.equal(list.get(3), 3);
assert.equal(list.get(1), 1);
assert.equal(list.get(4), 4);
assertValues(list, [1, 3, 4]);
assert.equal(list.delete(17), false);
assert.equal(list.delete(1), true);
assert.equal(list.get(1), undefined);
assert.equal(list.get(3), 3);
assert.equal(list.get(4), 4);
assertValues(list, [3, 4]);
});
test('Figure 3', function () {
let list = new SkipList<number, boolean>((a, b) => a - b);
list.set(3, true);
list.set(6, true);
list.set(7, true);
list.set(9, true);
list.set(12, true);
list.set(19, true);
list.set(21, true);
list.set(25, true);
assertKeys(list, [3, 6, 7, 9, 12, 19, 21, 25]);
list.set(17, true);
assert.deepEqual(list.size, 9);
assertKeys(list, [3, 6, 7, 9, 12, 17, 19, 21, 25]);
});
test('capacity max', function () {
let list = new SkipList<number, boolean>((a, b) => a - b, 10);
list.set(1, true);
list.set(2, true);
list.set(3, true);
list.set(4, true);
list.set(5, true);
list.set(6, true);
list.set(7, true);
list.set(8, true);
list.set(9, true);
list.set(10, true);
list.set(11, true);
list.set(12, true);
assertKeys(list, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
});
const cmp = (a: number, b: number): number => {
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
};
function insertArraySorted(array: number[], element: number) {
let idx = binarySearch(array, element, cmp);
if (idx >= 0) {
array[idx] = element;
} else {
idx = ~idx;
// array = array.slice(0, idx).concat(element, array.slice(idx));
array.splice(idx, 0, element);
}
return array;
}
function delArraySorted(array: number[], element: number) {
let idx = binarySearch(array, element, cmp);
if (idx >= 0) {
// array = array.slice(0, idx).concat(array.slice(idx));
array.splice(idx, 1);
}
return array;
}
test('perf', function () {
this.skip();
// data
const max = 2 ** 16;
const values = new Set<number>();
for (let i = 0; i < max; i++) {
let value = Math.floor(Math.random() * max);
values.add(value);
}
console.log(values.size);
// init
let list = new SkipList<number, boolean>(cmp, max);
let sw = new StopWatch(true);
values.forEach(value => list.set(value, true));
sw.stop();
console.log(`[LIST] ${list.size} elements after ${sw.elapsed()}ms`);
let array: number[] = [];
sw = new StopWatch(true);
values.forEach(value => array = insertArraySorted(array, value));
sw.stop();
console.log(`[ARRAY] ${array.length} elements after ${sw.elapsed()}ms`);
// get
sw = new StopWatch(true);
let someValues = [...values].slice(0, values.size / 4);
someValues.forEach(key => {
let value = list.get(key); // find
console.assert(value, '[LIST] must have ' + key);
list.get(-key); // miss
});
sw.stop();
console.log(`[LIST] retrieve ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`);
sw = new StopWatch(true);
someValues.forEach(key => {
let idx = binarySearch(array, key, cmp); // find
console.assert(idx >= 0, '[ARRAY] must have ' + key);
binarySearch(array, -key, cmp); // miss
});
sw.stop();
console.log(`[ARRAY] retrieve ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`);
// insert
sw = new StopWatch(true);
someValues.forEach(key => {
list.set(-key, false);
});
sw.stop();
console.log(`[LIST] insert ${sw.elapsed()}ms (${(sw.elapsed() / someValues.length).toPrecision(4)}ms/op)`);
sw = new StopWatch(true);
someValues.forEach(key => {
array = insertArraySorted(array, -key);
});
sw.stop();
console.log(`[ARRAY] insert ${sw.elapsed()}ms (${(sw.elapsed() / someValues.length).toPrecision(4)}ms/op)`);
// delete
sw = new StopWatch(true);
someValues.forEach(key => {
list.delete(key); // find
list.delete(-key); // miss
});
sw.stop();
console.log(`[LIST] delete ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`);
sw = new StopWatch(true);
someValues.forEach(key => {
array = delArraySorted(array, key); // find
array = delArraySorted(array, -key); // miss
});
sw.stop();
console.log(`[ARRAY] delete ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`);
});
});