diff --git a/src/perf-tests/binary-search.ts b/src/perf-tests/binary-search.ts new file mode 100644 index 0000000000000000000000000000000000000000..b1d63094fa9e016fc001b38d12904f380e6975e7 --- /dev/null +++ b/src/perf-tests/binary-search.ts @@ -0,0 +1,128 @@ + +function createData(n: number) { + const data = [];//new Int32Array(n); + let last = (15 * Math.random()) | 0; + for (let i = 0; i < n; i++) { + data[i] = last; + last += (15 * Math.random()) | 0; + } + return data; +} + +function binarySearchHelper(list: ArrayLike<number>, t: number) { + let min = 0, max = list.length - 1; + while (min <= max) { + if (min + 11 > max) { + for (let i = min; i <= max; i++) { + if (t === list[i]) return i; + } + return -1; + } + + const mid = (min + max) >> 1; + const v = list[mid]; + if (t < v) max = mid - 1; + else if (t > v) min = mid + 1; + else return mid; + } + return -1; +} + +function objSearch(obj: any, val: number) { + return typeof obj[val] !== 'undefined'; +} + +function setSearch(set: Set<number>, val: number) { + return set.has(val); +} + +function prepare(list: ArrayLike<number>) { + const obj = Object.create(null), set = new Set<number>(); + for (let i = 0; i < list.length; i++) { + const v = list[i]; + obj[v] = i; + set.add(v); + } + + return { list, obj, set }; +} + +function prepareSet(list: ArrayLike<number>) { + const set = new Set<number>(); + for (let i = 0; i < list.length; i++) { + const v = list[i]; + set.add(v); + } + return set; +} + +function prepareObj(list: ArrayLike<number>) { + const obj = Object.create(null); + for (let i = 0; i < list.length; i++) { + const v = list[i]; + obj[v] = i; + } + + return obj; +} + +function testBinary(list: ArrayLike<number>, points: ArrayLike<number>) { + let r = 0; + for (let i = 0, _i = points.length; i < _i; i++) { + if (binarySearchHelper(list, points[i]) >= 0) r += points[i]; + } + return r; +} + +function testObj(obj: any, points: ArrayLike<number>) { + let r = 0; + for (let i = 0, _i = points.length; i < _i; i++) { + if (objSearch(obj, points[i])) r += points[i]; + } + return r; +} + +function testSet(set: Set<number>, points: ArrayLike<number>) { + let r = 0; + for (let i = 0, _i = points.length; i < _i; i++) { + if (setSearch(set, points[i])) r += points[i]; + } + return r; +} + +function run(f: () => number, n: number) { + for (let i = 0; i < n; i++) f(); +} + +(function () { + const size = 100000; + const list = createData(size); + const queryPoints = createData(size); + + let obj = prepareObj(list); + let set = prepareSet(list); + + console.log('list', testBinary(list, queryPoints)); + console.log('obj', testObj(obj, queryPoints)); + console.log('set', testSet(set, queryPoints)); + + console.time('obj'); + run(() => testObj(obj, queryPoints), 100); + console.timeEnd('obj'); + + console.time('set'); + run(() => testSet(set, queryPoints), 100); + console.timeEnd('set'); + + console.time('bin-search'); + run(() => testBinary(list, queryPoints), 100); + console.timeEnd('bin-search'); + + console.time('prepare-obj'); + run(() => prepareObj(list), 1); + console.timeEnd('prepare-obj'); + + console.time('prepare-set'); + run(() => prepareSet(list).size, 1); + console.timeEnd('prepare-set'); +}()) diff --git a/src/perf-tests/sort.ts b/src/perf-tests/sort.ts new file mode 100644 index 0000000000000000000000000000000000000000..16bae8b5b40ebf09baf898ef4dfa93b36804ce6b --- /dev/null +++ b/src/perf-tests/sort.ts @@ -0,0 +1,298 @@ +// array A[] has the items to sort; array B[] is a work array +function BottomUpMergeSort(A: number[]) { + const n = A.length; + let src = A, target = new (A as any).constructor(n) as any; + // Each 1-element run in A is already "sorted". + // Make successively longer sorted runs of length 2, 4, 8, 16... until whole array is sorted. + for (let width = 1; width < n; width = 2 * width) { + // Array A is full of runs of length width. + for (let i = 0; i < n; i = i + 2 * width) { + // Merge two runs: A[i:i+width-1] and A[i+width:i+2*width-1] to B[] + // or copy A[i:n-1] to B[] ( if(i+width >= n) ) + BottomUpMerge(src, i, Math.min(i + width, n), Math.min(i + 2 * width, n), target); + } + // Now work array B is full of runs of length 2*width. + // Copy array B to array A for next iteration. + // A more efficient implementation would swap the roles of A and B. + const t = src; + src = target; + target = t; + // Now array A is full of runs of length 2*width. + } + if (src !== A) { + for (let i = 0; i < n; i++) src[i] = target[i]; + } +} + +// Left run is A[iLeft :iRight-1]. +// Right run is A[iRight:iEnd-1 ]. +function BottomUpMerge(A: number[], iLeft: number, iRight: number, iEnd: number, B: number[]) { + let i = iLeft, j = iRight; + // While there are elements in the left or right runs... + for (let k = iLeft; k < iEnd; k++) { + // If left run head exists and is <= existing right run head. + const u = A[i], v = A[j]; + if (i < iRight && (j >= iEnd || u <= v)) { + B[k] = u; + i = i + 1; + } else { + B[k] = v; + j = j + 1; + } + } +} + +function mergeSort(a: ArrayLike<number>) { + BottomUpMergeSort(a as any); + return a; +} + +function heapify(arr: number[], n: number, i: number) { + const l = 2 * i + 1; // left = 2*i + 1 + const r = 2 * i + 2; // right = 2*i + 2 + + let largest = i; // Initialize largest as root + + // If left child is larger than root + if (l < n && arr[l] > arr[largest]) + largest = l; + + // If right child is larger than largest so far + if (r < n && arr[r] > arr[largest]) + largest = r; + + // If largest is not root + if (largest !== i) { + //swap(arr[i], arr[largest]); + const t = arr[i]; + arr[i] = arr[largest]; + arr[largest] = t; + + // Recursively heapify the affected sub-tree + heapify(arr, n, largest); + } +} + +// main function to do heap sort +function heapSort(arr: number[]) { + const n = arr.length; + // Build heap (rearrange array) + for (let i = n / 2 - 1; i >= 0; i--) + heapify(arr, n, i); + + // One by one extract an element from heap + for (let i = n - 1; i >= 0; i--) { + // Move current root to end + //swap(arr[0], arr[i]); + const t = arr[0]; + arr[0] = arr[i]; + arr[i] = t; + + // call max heapify on the reduced heap + heapify(arr, i, 0); + } + return arr; +} + +function createTestData(n: number) { + const data = new Int32Array(n); //new Array(n); + for (let i = 0; i < n; i++) { + data[i] = (n * Math.random());// | 0; + } + return data; + + //return [ 5, 9, 10, 5, 7, 6, 8, 7, 0, 0, 0, 1, 2, 3, 4 ]; +} + +let swapCount = 0; +function swap(arr: number[], i: number, j: number) { + const temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; +} + +function medianPivot(arr: number[], left: number, right: number) { + const l = arr[left], r = arr[right], m = arr[(left + right) >> 1]; + if (l > r) return l > m ? Math.max(m, r) : l; + else return r > m ? Math.max(m, l) : r; +} + +const _qsParts = [0, 0]; + +function partition3(xs: number[], l: number, r: number) { + const v = medianPivot(xs, l, r) + let pivot = l, equals = l; + for (let i = l; i <= r; i++) { + const t = xs[i]; + if (t < v) { + /*if (pivot !== i)*/ swap(xs, i, pivot); + pivot++; + } else if (t === v) { + swap(xs, i, pivot); + swap(xs, pivot, equals); + equals++; + pivot++; + } + } + for (let i = l; i < equals; i++) { + swap(xs, i, l + pivot - i - 1) + } + _qsParts[0] = pivot - equals + l; + _qsParts[1] = pivot - 1; +} + +function partition3_1(xs: number[], l: number, r: number) { + const v = medianPivot(xs, l, r); + // console.log('P', v); + // console.log('I', xs.slice(l, r + 1)); + let equals = l, tail = r; + + while (xs[tail] > v) --tail; + for (let i = l; i <= tail; i++) { + const t = xs[i]; + if (t > v) { + swap(xs, i, tail); + tail--; + while (xs[tail] > v) --tail; + i--; + } else if (t === v) { + swap(xs, i, equals); + equals++; + } + } + //console.log('M', xs.slice(l, r + 1), equals, tail); + for (let i = l; i < equals; i++) { + swap(xs, i, l + tail - i) + } + //console.log('F', xs.slice(l, r + 1), tail - equals + l + 1, tail); + _qsParts[0] = tail - equals + l + 1; + _qsParts[1] = tail; +} + +function insertionSort(xs: number[], start: number, end: number) { + for (let i = start + 1; i <= end; i++) { + const key = xs[i]; + let j = i - 1; + while (j >= 0 && xs[j] > key) { + xs[j + 1] = xs[j]; + j = j - 1; + } + xs[j + 1] = key; + } +} + +function quickSort(xs: number[], low: number, high: number) { + while (low < high) { + if (high - low < 16) { + insertionSort(xs, low, high); + return; + } + + partition3_1(xs, low, high); + const li = _qsParts[0], ri = _qsParts[1]; + + if (li - low < high - ri) { + quickSort(xs, low, li - 1); + low = ri + 1; + } else { // Else recur for right part + quickSort(xs, ri + 1, high); + high = li - 1; + } + } +} + +function checkSorted(arr: ArrayLike<number>) { + for (let i = 0; i < arr.length - 1; i++) { + if (arr[i] > arr[i + 1]) { + console.log('not sorted'); + return; + } + } + console.log('sorted'); +} + +(function test() { + // console.log(medianPivot([1, 2, 3], 0, 2)) + // console.log(medianPivot([1, 3, 2], 0, 2)) + // console.log(medianPivot([2, 1, 3], 0, 2)) + // console.log(medianPivot([3, 1, 2], 0, 2)) + // console.log(medianPivot([2, 3, 1], 0, 2)) + // console.log(medianPivot([3, 2, 1], 0, 2)) + + const n = 10000; + + Array.prototype.sort.call(createTestData(n), (a: number, b: number) => a - b); + mergeSort(createTestData(n)); + + let sd; + + + + sd = createTestData(n); + quickSort(sd as any, 0, sd.length - 1); + + //console.log(sd); + + // sd = createTestData(n); + // console.time('merge'); + // mergeSort(sd); + // console.timeEnd('merge'); + // checkSorted(sd); + + sd = createTestData(n); + + checkSorted(sd); + console.time('heap'); + heapSort(sd as any); + console.timeEnd('heap'); + checkSorted(sd); + + console.time('heap-sorted'); + heapSort(sd as any); + console.timeEnd('heap-sorted'); + checkSorted(sd); + + console.log('--------------'); + + //console.log('quick', sd); + + sd = createTestData(n); + checkSorted(sd); + console.time('qs'); + quickSort(sd as any, 0, sd.length - 1); + console.timeEnd('qs'); + checkSorted(sd); + + console.time('qs-sorted'); + quickSort(sd as any, 0, sd.length - 1); + console.timeEnd('qs-sorted'); + checkSorted(sd); + + const reverseSorted = new Int32Array(n); + for (let i = 0; i < n; i++) { + reverseSorted[i] = sd[n - i - 1]; + } + + console.time('qs-reverse-sorted'); + quickSort(reverseSorted as any, 0, reverseSorted.length - 1); + console.timeEnd('qs-reverse-sorted'); + checkSorted(reverseSorted); + + console.log('swap count', swapCount); + + console.log('--------------'); + + sd = createTestData(n); + checkSorted(sd); + console.time('native'); + sd.sort((a: number, b: number) => a - b); + console.timeEnd('native'); + checkSorted(sd); + + console.time('native-sorted'); + sd.sort((a: number, b: number) => a - b); + console.timeEnd('native-sorted'); + checkSorted(sd); + //console.log(sd); + +}()) \ No newline at end of file