From c2d066600d6707f5c86dd9563ca431c24eab7064 Mon Sep 17 00:00:00 2001 From: Florian Bopp Date: Thu, 23 May 2024 22:15:00 +0200 Subject: [PATCH 1/6] feat: Add combineResultListFast utility function The `combineResultListFast` utility function is added to the `_internals/utils.ts` file. This function combines a list of `Result` objects and short circuits on the first `Err` value encountered. It returns a `Result` object containing the combined list of `Ok` values or the first `Err` value. --- src/_internals/utils.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/_internals/utils.ts b/src/_internals/utils.ts index 6c108b0d..7f11d77b 100644 --- a/src/_internals/utils.ts +++ b/src/_internals/utils.ts @@ -29,6 +29,25 @@ export type InferAsyncErrTypes = R extends ResultAsync ? E const appendValueToEndOfList = (value: T) => (list: T[]): T[] => [...list, value] +/** + * Short circuits on the FIRST Err value that we find + */ +export const combineResultListFast = ( + resultList: readonly Result[], +): Result => { + let acc = ok([]) as Result + + for (const result of resultList) { + if (result.isErr()) { + acc = err(result.error) + break + } else { + acc.map((list) => list.push(result.value)) + } + } + return acc +} + /** * Short circuits on the FIRST Err value that we find */ From 32e6ce8cfb8c36dd80b485d51407b259e2bc611f Mon Sep 17 00:00:00 2001 From: Florian Bopp Date: Thu, 23 May 2024 22:15:11 +0200 Subject: [PATCH 2/6] feat: Add benchmark script for performance testing --- tests/benchmark.ts | 62 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 tests/benchmark.ts diff --git a/tests/benchmark.ts b/tests/benchmark.ts new file mode 100644 index 00000000..d336c9b4 --- /dev/null +++ b/tests/benchmark.ts @@ -0,0 +1,62 @@ +import { performance } from 'perf_hooks' +import { combineResultList, combineResultListFast } from '../src/_internals/utils' +import { ok, Result } from '../src' + +// Define input sizes +const inputSizes = [100, 1000, 10000, 100000, 500000] + +// Prepare results array +const results: { + numElements: number + timeOld: number + timeNew: number + areResultsSame: boolean + improvement: string +}[] = [] + +// Iterate over input sizes +for (const numElements of inputSizes) { + // Start spinner + let i = 0 + + // Prepare data + const data: Result[] = [] + for (let i = 0; i < numElements; i++) { + data.push(ok(i)) + } + + // Benchmark combineResultList + const t0 = performance.now() + const result = combineResultList(data) + const t1 = performance.now() + + // Benchmark combineResultListFast + const t2 = performance.now() + const resultFast = combineResultListFast(data) + const t3 = performance.now() + + if (numElements === 100) { + console.log('result', result) + console.log('resultFast', resultFast) + } + + // Check if results are the same + const areResultsSame = JSON.stringify(result) === JSON.stringify(resultFast) + + // Calculate relative improvement + const improvement = Math.floor((t1 - t0) / (t3 - t2)) + + // Add results to results array + results.push({ + numElements, + timeOld: t1 - t0, + timeNew: t3 - t2, + areResultsSame, + improvement: `${improvement}x`, + }) +} + + +// Output results as a table +console.table(results) + From 0d6d7e0015dcdc44d5c63c0e53178b49af320bde Mon Sep 17 00:00:00 2001 From: Florian Bopp Date: Fri, 24 May 2024 10:22:28 +0200 Subject: [PATCH 3/6] feat: Refactor combineResultList function for better performance --- src/_internals/utils.ts | 62 ++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/src/_internals/utils.ts b/src/_internals/utils.ts index 7f11d77b..fc0186da 100644 --- a/src/_internals/utils.ts +++ b/src/_internals/utils.ts @@ -27,12 +27,12 @@ export type InferErrTypes = R extends Result ? E : never export type InferAsyncOkTypes = R extends ResultAsync ? T : never export type InferAsyncErrTypes = R extends ResultAsync ? E : never -const appendValueToEndOfList = (value: T) => (list: T[]): T[] => [...list, value] +// const appendValueToEndOfList = (value: T) => (list: T[]): T[] => [...list, value] /** * Short circuits on the FIRST Err value that we find */ -export const combineResultListFast = ( +export const combineResultList = ( resultList: readonly Result[], ): Result => { let acc = ok([]) as Result @@ -48,21 +48,21 @@ export const combineResultListFast = ( return acc } -/** - * Short circuits on the FIRST Err value that we find - */ -export const combineResultList = ( - resultList: readonly Result[], -): Result => - resultList.reduce( - (acc, result) => - acc.isOk() - ? result.isErr() - ? err(result.error) - : acc.map(appendValueToEndOfList(result.value)) - : acc, - ok([]) as Result, - ) +// /** +// * Short circuits on the FIRST Err value that we find +// */ +// export const combineResultList = ( +// resultList: readonly Result[], +// ): Result => + // resultList.reduce( + // (acc, result) => + // acc.isOk() + // ? result.isErr() + // ? err(result.error) + // : acc.map(appendValueToEndOfList(result.value)) + // : acc, + // ok([]) as Result, + // ) /* This is the typesafe version of Promise.all * @@ -81,18 +81,22 @@ export const combineResultAsyncList = ( */ export const combineResultListWithAllErrors = ( resultList: readonly Result[], -): Result => - resultList.reduce( - (acc, result) => - result.isErr() - ? acc.isErr() - ? err([...acc.error, result.error]) - : err([result.error]) - : acc.isErr() - ? acc - : ok([...acc.value, result.value]), - ok([]) as Result, - ) +): Result => { + let acc = ok([]) as Result + + for (const result of resultList) { + if (result.isErr() && acc.isErr()) { + acc.error.push(result.error) + } else if (result.isErr() && acc.isOk()) { + acc = err([result.error]) + } else if (result.isOk() && acc.isErr()) { + // do nothing + } else if (result.isOk() && acc.isOk()) { + acc.value.push(result.value) + } + } + return acc +} export const combineResultAsyncListWithAllErrors = ( asyncResultList: readonly ResultAsync[], From 16b7ababdcdcee2aa0e44f2d73c97e1738a88132 Mon Sep 17 00:00:00 2001 From: Florian Bopp Date: Sat, 1 Jun 2024 06:45:07 +0200 Subject: [PATCH 4/6] feat: Optimize combineResultList function for better performance --- src/_internals/utils.ts | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/_internals/utils.ts b/src/_internals/utils.ts index fc0186da..0e062f0c 100644 --- a/src/_internals/utils.ts +++ b/src/_internals/utils.ts @@ -48,22 +48,6 @@ export const combineResultList = ( return acc } -// /** -// * Short circuits on the FIRST Err value that we find -// */ -// export const combineResultList = ( -// resultList: readonly Result[], -// ): Result => - // resultList.reduce( - // (acc, result) => - // acc.isOk() - // ? result.isErr() - // ? err(result.error) - // : acc.map(appendValueToEndOfList(result.value)) - // : acc, - // ok([]) as Result, - // ) - /* This is the typesafe version of Promise.all * * Takes a list of ResultAsync and success if all inner results are Ok values @@ -89,11 +73,10 @@ export const combineResultListWithAllErrors = ( acc.error.push(result.error) } else if (result.isErr() && acc.isOk()) { acc = err([result.error]) - } else if (result.isOk() && acc.isErr()) { - // do nothing } else if (result.isOk() && acc.isOk()) { acc.value.push(result.value) } + // do nothing when result.isOk() && acc.isErr() } return acc } From b0830f720ecd3ede3581836f83dea6fdb19db9bb Mon Sep 17 00:00:00 2001 From: Florian Bopp Date: Sat, 1 Jun 2024 06:48:08 +0200 Subject: [PATCH 5/6] remove benchmark file --- tests/benchmark.ts | 62 ---------------------------------------------- 1 file changed, 62 deletions(-) delete mode 100644 tests/benchmark.ts diff --git a/tests/benchmark.ts b/tests/benchmark.ts deleted file mode 100644 index d336c9b4..00000000 --- a/tests/benchmark.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { performance } from 'perf_hooks' -import { combineResultList, combineResultListFast } from '../src/_internals/utils' -import { ok, Result } from '../src' - -// Define input sizes -const inputSizes = [100, 1000, 10000, 100000, 500000] - -// Prepare results array -const results: { - numElements: number - timeOld: number - timeNew: number - areResultsSame: boolean - improvement: string -}[] = [] - -// Iterate over input sizes -for (const numElements of inputSizes) { - // Start spinner - let i = 0 - - // Prepare data - const data: Result[] = [] - for (let i = 0; i < numElements; i++) { - data.push(ok(i)) - } - - // Benchmark combineResultList - const t0 = performance.now() - const result = combineResultList(data) - const t1 = performance.now() - - // Benchmark combineResultListFast - const t2 = performance.now() - const resultFast = combineResultListFast(data) - const t3 = performance.now() - - if (numElements === 100) { - console.log('result', result) - console.log('resultFast', resultFast) - } - - // Check if results are the same - const areResultsSame = JSON.stringify(result) === JSON.stringify(resultFast) - - // Calculate relative improvement - const improvement = Math.floor((t1 - t0) / (t3 - t2)) - - // Add results to results array - results.push({ - numElements, - timeOld: t1 - t0, - timeNew: t3 - t2, - areResultsSame, - improvement: `${improvement}x`, - }) -} - - -// Output results as a table -console.table(results) - From 2a34a15242720a25c9f21a50b2808a1f518157b3 Mon Sep 17 00:00:00 2001 From: Florian Bopp Date: Sat, 1 Jun 2024 06:49:31 +0200 Subject: [PATCH 6/6] remove commented unused function --- src/_internals/utils.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/_internals/utils.ts b/src/_internals/utils.ts index 0e062f0c..04196144 100644 --- a/src/_internals/utils.ts +++ b/src/_internals/utils.ts @@ -27,8 +27,6 @@ export type InferErrTypes = R extends Result ? E : never export type InferAsyncOkTypes = R extends ResultAsync ? T : never export type InferAsyncErrTypes = R extends ResultAsync ? E : never -// const appendValueToEndOfList = (value: T) => (list: T[]): T[] => [...list, value] - /** * Short circuits on the FIRST Err value that we find */