Skip to content

Commit

Permalink
Added performancer
Browse files Browse the repository at this point in the history
  • Loading branch information
sweko committed Dec 8, 2024
1 parent f38c555 commit 91b515f
Showing 1 changed file with 123 additions and 16 deletions.
139 changes: 123 additions & 16 deletions 2024/ts/utils/performancer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,24 @@ export type PerformancerInterval = {
}

export type PerformancerData = {
intervals: PerformancerInterval[];
name: string;
get totalDuration(): number;
get count(): number;
}

const createPerformancerItem = (name: string): PerformancerData => {
type SimplePerformancerData = PerformancerData & {
set count(value: number);
set totalDuration(value: number);
lastStart: number;
}

export type FullPerformancerData = PerformancerData & {
intervals: PerformancerInterval[];
}

type PerformanceWrapper = <Args extends any[], Return>(fn: (...args: Args) => Return, name?: string) => (...args: Args) => Return;

const createFullPerformancerItem = (name: string): FullPerformancerData => {
const intervals: PerformancerInterval[] = [];
return {
intervals,
Expand All @@ -25,25 +36,85 @@ const createPerformancerItem = (name: string): PerformancerData => {
};
};

const createSimplePerformancerItem = (name: string): SimplePerformancerData => {
let count = 0;
let totalDuration = 0;
return {
name,
get totalDuration() {
return totalDuration;
},
set totalDuration(value: number) {
totalDuration = value;
},
get count() {
return count;
},
set count(value: number) {
count = value;
},
lastStart: 0
};
}

export type PerformancerOptions = {
readonly trackDetails?: boolean;
}

const PerformancerOptions = {
default: {
trackDetails: false
}
};

export type Performancer = {
readonly start: (name: string) => void;
readonly monitor: PerformanceWrapper;
readonly begin: (name: string, options?: PerformancerOptions) => void;
readonly end: (name: string) => void;
readonly get: (name: string) => PerformancerData | undefined;
readonly printAll: (printDetails?: boolean) => void;
readonly print: (name: string, printDetails?: boolean) => void;
}

const isSimpleItem = (item: PerformancerData): item is SimplePerformancerData => {
return (item as SimplePerformancerData).lastStart !== undefined;
}

const isFullItem = (item: PerformancerData): item is FullPerformancerData => {
return (item as FullPerformancerData).intervals !== undefined;
}

const toThreeDecimals = (value: number) => Math.round(value * 1000) / 1000;

const createPerformancer = (): Performancer => {
const monitor = <Args extends any[], Return>(fn: (...args: Args) => Return, name: string = fn.name) => {
return (...args: Args) => {
begin(name);
const result = fn(...args);
end(name);
return result;
};
}

const items: Map<string, PerformancerData> = new Map();

const start = (name: string) => {
if (!items.has(name)) {
items.set(name, createPerformancerItem(name));
const begin = (name: string, options: PerformancerOptions = PerformancerOptions.default) => {
if (options.trackDetails) {
if (!items.has(name)) {
items.set(name, createFullPerformancerItem(name));
}
const start = performance.now();
const performancerItem = items.get(name) as FullPerformancerData;
performancerItem.intervals.push({ start, end: 0, duration: 0 });
} else {
if (!items.has(name)) {
items.set(name, createSimplePerformancerItem(name));
}
const start = performance.now();
const performancerItem = items.get(name) as SimplePerformancerData;
performancerItem.lastStart = start;
performancerItem.count += 1;
}
const start = performance.now();
items.get(name)!.intervals.push({ start, end: 0, duration: 0 });
};

const end = (name: string) => {
Expand All @@ -52,13 +123,19 @@ const createPerformancer = (): Performancer => {
if (!item) {
throw new Error("No item found");
}
const interval = item.intervals.pop();
if (!interval) {
throw new Error("No interval found");
if (isSimpleItem(item)) {
item.totalDuration += end - item.lastStart;
} else if (isFullItem(item)) {
const interval = item.intervals.pop();
if (!interval) {
throw new Error("No interval found");
}
const duration = toThreeDecimals(end - interval.start);
const finishedInterval = { ...interval, end, duration };
item.intervals.push(finishedInterval);
} else {
throw new Error("Invalid item type");
}
const duration = toThreeDecimals(end - interval.start);
const finishedInterval = { ...interval, end, duration };
item.intervals.push(finishedInterval);
};

const get = (name: string) => items.get(name);
Expand All @@ -77,7 +154,7 @@ const createPerformancer = (): Performancer => {
return;
}
console.log(`Performance for ${name}:`);
if (printDetails) {
if (printDetails && isFullItem(item)) {
for (const interval of item.intervals) {
console.log(` - ${interval.duration}ms`);
}
Expand All @@ -89,7 +166,8 @@ const createPerformancer = (): Performancer => {
}

return {
start,
monitor,
begin,
end,
get,
printAll,
Expand All @@ -99,3 +177,32 @@ const createPerformancer = (): Performancer => {


export const Performancer = createPerformancer();


export const monitored = <Args extends any[], Return>(fn: (...args: Args) => Return, name: string = fn.name) => {

const performanceData = {
name,
count: 0,
totalDuration: 0,
print: () => {
console.log(`Performance for ${name}:`);
console.log(`${performanceData.count} total calls`);
console.log(`Total duration: ${performanceData.totalDuration.toFixed(3)}ms`);
console.log(`Average duration: ${(performanceData.totalDuration / performanceData.count).toFixed(3)}ms`);
}
}

const result = (...args: Args) => {
const start = performance.now();
const result = fn(...args);
const end = performance.now();
performanceData.count += 1;
performanceData.totalDuration += end - start;
return result;
};

result.performance = performanceData;

return result;
}

0 comments on commit 91b515f

Please sign in to comment.