Skip to content
Snippets Groups Projects
Commit f9419bb4 authored by David Sehnal's avatar David Sehnal
Browse files

collections

parent a0ee98a7
Branches
Tags
No related merge requests found
import * as B from 'benchmark'
import ChunkedArray from '../utils/chunked-array'
function testNative(size: number) {
const xs = new Array(size);
for (let i = 0; i < size; i++) xs[i] = i * i;
return xs;
}
function testChunkedTyped(size: number, chunk: number, linear: boolean) {
const xs = ChunkedArray.create(s => new Int32Array(s), 1, chunk, linear);
for (let i = 0; i < size; i++) ChunkedArray.add(xs, i * i);
return ChunkedArray.compact(xs);
}
function testChunkedNative(size: number, chunk: number) {
const xs = ChunkedArray.create(s => [], 1, chunk, false);
for (let i = 0; i < size; i++) ChunkedArray.add(xs, i * i);
return ChunkedArray.compact(xs);
}
const suite = new B.Suite();
const N = 70000;
suite
.add('native', () => testNative(N))
.add('chunkedT 0.1k', () => testChunkedTyped(N, 100, false))
.add('chunkedT 4k', () => testChunkedTyped(N, 4096, false))
.add('chunkedT 4k lin', () => testChunkedTyped(N, 4096, true))
.add('chunkedT N / 2', () => testChunkedTyped(N, N / 2, false))
.add('chunkedT N', () => testChunkedTyped(N, N, false))
.add('chunkedT 2 * N', () => testChunkedTyped(N, 2 * N, false))
.add('chunkedN N', () => testChunkedNative(N, N))
.add('chunkedN 0.1k', () => testChunkedNative(N, 100))
.add('chunkedN N / 2', () => testChunkedNative(N, N / 2))
.add('chunkedN 2 * N', () => testChunkedNative(N, 2 * N))
.on('cycle', (e: any) => {
console.log(String(e.target));
})
.run();
//console.log(testChunkedTyped(10, 16));
......@@ -24,11 +24,6 @@ class RangeImpl implements Impl {
size: number;
has(x: number) { return x >= this.min && x <= this.max; }
indexOf(x: number) { return x >= this.min && x <= this.max ? x - this.min : -1; }
toArray() {
const ret = new Array(this.size);
for (let i = 0; i < this.size; i++) ret[i] = i + this.min;
return ret;
}
elementAt(i: number) { return this.min + i; }
elements() { return Iterator.Range(this.min, this.max); }
......@@ -54,6 +49,17 @@ class ArrayImpl implements Impl {
}
namespace OrderedSet {
export function ofSingleton(value: number): OrderedSet { return new RangeImpl(value, value); }
export function ofRange(min: number, max: number): OrderedSet { return max < min ? Empty : new RangeImpl(min, max); }
/** It is the responsibility of the caller to ensure the array is sorted and contains unique values. */
export function ofSortedArray(xs: ArrayLike<number>): OrderedSet {
if (!xs.length) return Empty;
// check if the array is just a range
if (xs[xs.length - 1] - xs[0] + 1 === xs.length) return ofRange(xs[0], xs[xs.length - 1]);
return new ArrayImpl(xs);
}
export const Empty = new RangeImpl(0, -1);
export function isEmpty(a: OrderedSet) { return a.size === 0; }
export function hashCode(a: OrderedSet) {
......@@ -117,17 +123,6 @@ namespace OrderedSet {
return intersectAA((a as ArrayImpl).values, (b as ArrayImpl).values);
}
}
export function ofSingleton(value: number): OrderedSet { return new RangeImpl(value, value); }
export function ofRange(min: number, max: number): OrderedSet { return max < min ? Empty : new RangeImpl(min, max); }
/** It is the responsibility of the caller to ensure the array is sorted and contains unique values. */
export function ofSortedArray(xs: ArrayLike<number>): OrderedSet {
if (!xs.length) return Empty;
// check if the array is just a range
if (xs[xs.length - 1] - xs[0] + 1 === xs.length) return ofRange(xs[0], xs[xs.length - 1]);
return new ArrayImpl(xs);
}
export const Empty = new RangeImpl(0, -1);
}
function min(a: OrderedSet) { return (a as Impl).min; }
......
......@@ -9,125 +9,129 @@
* A generic chunked array builder.
*
* When adding elements, the array growns by a specified number
* of elements and no copying is done until ChunkedArray.compact
* is called.
* of elements (either linear or exponential growth) and no copying
* is done until ChunkedArray.compact is called.
*/
export interface ChunkedArray<T> {
creator: (size: number) => any;
elementSize: number;
chunkSize: number;
current: any;
currentIndex: number;
parts: any[];
elementCount: number;
interface ChunkedArray<T> {
ctor: (size: number) => any,
elementSize: number,
linearGrowth: boolean,
initialSize: number,
allocatedSize: number,
elementCount: number,
currentSize: number,
currentChunk: any,
currentIndex: number,
chunks: any[]
}
export namespace ChunkedArray {
// TODO: better api, write tests
namespace ChunkedArray {
export function is(x: any): x is ChunkedArray<any> {
return x.creator && x.chunkSize;
}
export function add4<T>(array: ChunkedArray<T>, x: T, y: T, z: T, w: T) {
if (array.currentIndex >= array.chunkSize) {
function allocateNext(array: ChunkedArray<any>) {
let nextSize = !array.allocatedSize || array.linearGrowth
? array.initialSize * array.elementSize
: Math.max(Math.ceil(0.61 * array.allocatedSize), 1);
if (nextSize % array.elementSize !== 0) nextSize += nextSize % array.elementSize;
array.currentSize = nextSize;
array.currentIndex = 0;
array.current = array.creator(array.chunkSize);
array.parts[array.parts.length] = array.current;
array.currentChunk = array.ctor(nextSize);
array.allocatedSize += nextSize;
array.chunks[array.chunks.length] = array.currentChunk;
}
array.current[array.currentIndex++] = x;
array.current[array.currentIndex++] = y;
array.current[array.currentIndex++] = z;
array.current[array.currentIndex++] = w;
export function add4<T>(array: ChunkedArray<T>, x: T, y: T, z: T, w: T) {
if (array.currentIndex >= array.currentSize) allocateNext(array);
const c = array.currentChunk;
c[array.currentIndex++] = x;
c[array.currentIndex++] = y;
c[array.currentIndex++] = z;
c[array.currentIndex++] = w;
return array.elementCount++;
}
export function add3<T>(array: ChunkedArray<T>, x: T, y: T, z: T) {
if (array.currentIndex >= array.chunkSize) {
array.currentIndex = 0;
array.current = array.creator(array.chunkSize);
array.parts[array.parts.length] = array.current;
}
array.current[array.currentIndex++] = x;
array.current[array.currentIndex++] = y;
array.current[array.currentIndex++] = z;
if (array.currentIndex >= array.currentSize) allocateNext(array);
const c = array.currentChunk;
c[array.currentIndex++] = x;
c[array.currentIndex++] = y;
c[array.currentIndex++] = z;
return array.elementCount++;
}
export function add2<T>(array: ChunkedArray<T>, x: T, y: T) {
if (array.currentIndex >= array.chunkSize) {
array.currentIndex = 0;
array.current = array.creator(array.chunkSize);
array.parts[array.parts.length] = array.current;
}
array.current[array.currentIndex++] = x;
array.current[array.currentIndex++] = y;
if (array.currentIndex >= array.currentSize) allocateNext(array);
const c = array.currentChunk;
c[array.currentIndex++] = x;
c[array.currentIndex++] = y;
return array.elementCount++;
}
export function add<T>(array: ChunkedArray<T>, x: T) {
if (array.currentIndex >= array.chunkSize) {
array.currentIndex = 0;
array.current = array.creator(array.chunkSize);
array.parts[array.parts.length] = array.current;
}
array.current[array.currentIndex++] = x;
if (array.currentIndex >= array.currentSize) allocateNext(array);
array.currentChunk[array.currentIndex++] = x;
return array.elementCount++;
}
export function compact<T>(array: ChunkedArray<T>): T[] {
const ret = array.creator(array.elementSize * array.elementCount)
const offset = (array.parts.length - 1) * array.chunkSize
let offsetInner = 0
let part: any
export function compact<T>(array: ChunkedArray<T>): ArrayLike<T> {
const { ctor, chunks, currentIndex } = array;
if (array.parts.length > 1) {
if (array.parts[0].buffer) {
for (let i = 0; i < array.parts.length - 1; i++) {
ret.set(array.parts[i], array.chunkSize * i);
if (!chunks.length) return ctor(0);
if (chunks.length === 1 && currentIndex === array.allocatedSize) {
return chunks[0];
}
} else {
for (let i = 0; i < array.parts.length - 1; i++) {
offsetInner = array.chunkSize * i;
part = array.parts[i];
const ret = ctor(array.elementSize * array.elementCount);
let offset = 0;
for (let j = 0; j < array.chunkSize; j++) {
ret[offsetInner + j] = part[j];
}
if (ret.buffer) {
for (let i = 0, _i = chunks.length - 1; i < _i; i++) {
ret.set(chunks[i], offset);
offset += chunks[i].length;
}
} else {
for (let i = 0, _i = chunks.length - 1; i < _i; i++) {
const chunk = chunks[i];
for (let j = 0, _j = chunk.length; j < _j; j++) ret[offset + j] = chunk[j];
offset += chunk.length;
}
}
if (array.current.buffer && array.currentIndex >= array.chunkSize) {
ret.set(array.current, array.chunkSize * (array.parts.length - 1));
const lastChunk = chunks[chunks.length - 1];
if (ret.buffer && currentIndex >= array.currentSize) {
ret.set(lastChunk, offset);
} else {
for (let i = 0; i < array.currentIndex; i++) {
ret[offset + i] = array.current[i];
}
}
return ret as any;
for (let j = 0, _j = lastChunk.length; j < _j; j++) ret[offset + j] = lastChunk[j];
}
export function create<T>(creator: (size: number) => any, chunkElementCount: number, elementSize: number): ChunkedArray<T> {
chunkElementCount = chunkElementCount | 0;
if (chunkElementCount <= 0) chunkElementCount = 1;
let chunkSize = chunkElementCount * elementSize;
let current = creator(chunkSize)
return ret;
}
export function create<T>(ctor: (size: number) => any, elementSize: number, initialSize: number, linearGrowth: boolean): ChunkedArray<T> {
return {
ctor,
elementSize,
chunkSize,
creator,
current,
parts: [current],
linearGrowth,
initialSize,
allocatedSize: 0,
elementCount: 0,
currentSize: 0,
currentChunk: void 0,
currentIndex: 0,
elementCount: 0
} as ChunkedArray<T>
chunks: []
} as ChunkedArray<T>;
}
}
export default ChunkedArray
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment