David Sehnal 7 år sedan
förälder
incheckning
b6cf50daa2
2 ändrade filer med 426 tillägg och 0 borttagningar
  1. 128 0
      src/perf-tests/binary-search.ts
  2. 298 0
      src/perf-tests/sort.ts

+ 128 - 0
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');
+}())

+ 298 - 0
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);
+
+}())