Skip to content

Commit

Permalink
Add first prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
yamiteru committed Mar 2, 2023
1 parent faddc7f commit 29ca9d0
Show file tree
Hide file tree
Showing 17 changed files with 455 additions and 2 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"build": "pnpm clean && ts-node scripts/build.ts && tsc --emitDeclarationOnly --outDir dist",
"clean": "rm -rf dist",
"prepublishOnly": "pnpm build",
"release": "release-it"
"release": "release-it",
"start": "node --expose-gc -r ts-node/register src/index.ts"
},
"license": "MIT",
"devDependencies": {
Expand Down
45 changes: 45 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Offsets, Options } from "./types";

export const OPTIONS = {
cpu: {
chunkSize: 100,
compareSize: 10,
rangePercent: 10,
},
ram: {
chunkSize: 5,
compareSize: 5,
rangePercent: 5,
},
general: {
substractSelf: true,
allowGc: true,
}
} satisfies Options;

export const OFFSETS = {
async: {
cpu: {
min: 0,
max: 0,
median: 0,
},
ram: {
min: 0,
max: 0,
median: 0,
},
},
sync: {
cpu: {
min: 0,
max: 0,
median: 0,
},
ram: {
min: 0,
max: 0,
median: 0,
},
}
} satisfies Offsets;
27 changes: 27 additions & 0 deletions src/createStores.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Options, Stores } from "./types";

// memoize store by options and reuse it if possible
export function createStores(options: Options): Stores {
return {
cpu: {
chunk: {
array: new Uint32Array(new ArrayBuffer(options.cpu.chunkSize * 4)),
index: 0
},
main: {
array: new Uint32Array(new ArrayBuffer(options.cpu.chunkSize * 4)),
index: 0
},
},
ram: {
chunk: {
array: new Uint32Array(new ArrayBuffer(options.ram.chunkSize * 4)),
index: 0
},
main: {
array: new Uint32Array(new ArrayBuffer(options.ram.chunkSize * 4)),
index: 0
},
},
};
}
18 changes: 18 additions & 0 deletions src/getAllOffsets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { getOffset } from "./getOffset";
import { Offsets, Options, Stores } from "./types";

export async function getAllOffsets(
stores: Stores,
options: Options
): Promise<Offsets> {
return {
async: {
cpu: await getOffset({ type: "async", mode: "cpu" }, stores, options),
ram: await getOffset({ type: "async", mode: "ram" }, stores, options),
},
sync: {
cpu: await getOffset({ type: "sync", mode: "cpu" }, stores, options),
ram: await getOffset({ type: "sync", mode: "ram" }, stores, options),
},
};
}
20 changes: 20 additions & 0 deletions src/getCpuStats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { getMedian } from "./getMedian";
import { getMinMax } from "./getMinMax";
import { positive } from "./positive";
import { OffsetData, Offsets, Store } from "./types";

export function getCpuStats(
{ array, index }: Store,
{ mode, type }: OffsetData,
offsets: Offsets
) {
const median = getMedian(array, index);
const { min, max } = getMinMax(array, index);
const ctx = offsets[type][mode];

return {
median: positive(median - ctx.median),
min: positive(min - ctx.min),
max: positive(max - ctx.max)
};
}
9 changes: 9 additions & 0 deletions src/getMedian.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export function getMedian(
array: Uint32Array,
length: number
) {
return array
.slice(0, length)
.sort()
.at(Math.floor(length / 2)) as number;
}
16 changes: 16 additions & 0 deletions src/getMinMax.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export function getMinMax(
array: Uint32Array,
length: number
) {
let min = array[0];
let max = array[0];

for(let i = 1; i < length; ++i) {
const value = array[i];

if(value < min) min = value;
if(value > max) max = value;
}

return { min, max };
}
50 changes: 50 additions & 0 deletions src/getOffset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { OFFSETS } from "./constants";
import { getCpuStats } from "./getCpuStats";
import { getMedian } from "./getMedian";
import { getMinMax } from "./getMinMax";
import { getRamStats } from "./getRamStats";
import { measure } from "./measure";
import { OffsetData, Options, Stores } from "./types";

// TODO: use "run" function to get rid of duplication
// TODO: run as many times as needed to get to zero
export async function getOffset(
{ type, mode }: OffsetData,
stores: Stores,
options: Options
) {
const { chunk, main } = stores[mode];
const { chunkSize, compareSize, rangePercent } = options[mode];
const getStats = mode === "cpu" ? getCpuStats: getRamStats;
const fn = type === "async" ? async () => { /* */ }: () => { /* */ };

main.index = -1;
chunk.index = -1;

while(true as any) {
if(chunk.index === chunkSize) {
main.array[++main.index] = getMedian(chunk.array, chunk.index);
chunk.index = -1;

if(main.index >= compareSize) {
const { min, max } = getMinMax(
main.array.slice(main.index - compareSize),
compareSize
);

if((max - (max / 100 * rangePercent)) <= min) {
break;
}
}
}

if(main.index === chunkSize) {
main.array[0] = getMedian(main.array, main.index);
main.index = 0;
}

await measure({ fn, mode, store: chunk }, options);
}

return getStats(main, { mode, type: fn instanceof Promise ? "async": "sync" }, OFFSETS);
}
23 changes: 23 additions & 0 deletions src/getOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { OPTIONS } from "./constants";
import { DeepPartial, Options } from "./types";

export function getOptions(
partialOptions?: DeepPartial<Options>
) {
return {
cpu: {
...OPTIONS.cpu,
...(partialOptions?.cpu || {})
},
ram: {
...OPTIONS.ram,
...(partialOptions?.ram || {})
},
general: {
...OPTIONS.general,
...(partialOptions?.general || {})
}
} satisfies Options;
}


20 changes: 20 additions & 0 deletions src/getRamStats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { getMedian } from "./getMedian";
import { getMinMax } from "./getMinMax";
import { positive } from "./positive";
import { OffsetData, Offsets, Store } from "./types";

export function getRamStats(
{ array, index }: Store,
{ mode, type }: OffsetData,
offsets: Offsets
) {
const median = getMedian(array, index);
const { min, max } = getMinMax(array, index);
const ctx = offsets[type][mode];

return {
median: positive(median - ctx.median),
min: positive(min - ctx.min),
max: positive(max - ctx.max)
};
}
14 changes: 13 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,13 @@
export const add = (...n: number[]) => n.reduce((acc, v) => acc + v, 0);
import { preset } from "./preset";

const defaultSuite = preset();
const callibration = defaultSuite({
emptySync: () => { /* */ },
emptyAsync: async () => { /* */ },
});

(async () => {
for await (const result of callibration()) {
console.log(result);
}
})();
28 changes: 28 additions & 0 deletions src/measure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { MeasureData, Options } from "./types";

export async function measure(
{ fn, mode, store }: MeasureData,
{ general: { allowGc } }: Options
) {
const isAsync = fn instanceof Promise;

if(mode === "cpu") {
const start = process.hrtime.bigint();

isAsync ? (await fn()): fn();

const end = process.hrtime.bigint();

store.array[++store.index] = Math.round(Number(end - start));
} else {
allowGc && global.gc?.();

const start = process.memoryUsage().heapUsed;

isAsync ? (await fn()): fn();

const end = process.memoryUsage().heapUsed;

store.array[++store.index] = Math.round(Number(end - start));
}
}
3 changes: 3 additions & 0 deletions src/positive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function positive(value: number) {
return value < 0 ? 0: value;
}
33 changes: 33 additions & 0 deletions src/preset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { createStores } from "./createStores";
import { getAllOffsets } from "./getAllOffsets";
import { getOptions } from "./getOptions";
import { runBenchmark } from "./runBenchmark";
import { DeepPartial, Options, Benchmarks } from "./types";

export function preset(partialOptions?: DeepPartial<Options>) {
const options = getOptions(partialOptions);
const stores = createStores(options);

return function createSuite<
$Benchmarks extends Benchmarks
>(
benchmarks: $Benchmarks
) {
return async function* runSuite() {
const offsets = await getAllOffsets(stores, options);

console.log(offsets);

for(const benchmarkName in benchmarks) {
options.general.allowGc && global.gc?.();

yield await runBenchmark(
benchmarks[benchmarkName],
stores,
offsets,
options
);
}
}
}
}
50 changes: 50 additions & 0 deletions src/run.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { getCpuStats } from "./getCpuStats";
import { getMedian } from "./getMedian";
import { getMinMax } from "./getMinMax";
import { getRamStats } from "./getRamStats";
import { measure } from "./measure";
import { Benchmark, OffsetData, Offsets, Options, Stores } from "./types";

export async function run(
fn: Benchmark,
mode: OffsetData["mode"],
stores: Stores,
offsets: Offsets,
options: Options
) {
const { chunk, main } = stores[mode];
const { chunkSize, compareSize, rangePercent } = options[mode];
const getStats = mode === "cpu"
? getCpuStats
: getRamStats;

main.index = -1;
chunk.index = -1;

while(true as any) {
if(chunk.index === chunkSize) {
main.array[++main.index] = getMedian(chunk.array, chunk.index);
chunk.index = -1;

if(main.index >= compareSize) {
const { min, max } = getMinMax(
main.array.slice(main.index - compareSize),
compareSize
);

if((max - (max / 100 * rangePercent)) <= min) {
break;
}
}
}

if(main.index === chunkSize) {
main.array[0] = getMedian(main.array, main.index);
main.index = 0;
}

await measure({ fn, mode, store: chunk }, options);
}

return getStats(main, { mode, type: fn instanceof Promise ? "async": "sync" }, offsets);
}
14 changes: 14 additions & 0 deletions src/runBenchmark.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { run } from "./run";
import { Benchmark, Offsets, Options, Stores } from "./types";

export async function runBenchmark(
benchmark: Benchmark,
stores: Stores,
offsets: Offsets,
options: Options
) {
return {
cpu: await run(benchmark, "cpu", stores, offsets, options),
ram: await run(benchmark, "ram", stores, offsets, options)
};
}
Loading

0 comments on commit 29ca9d0

Please sign in to comment.