Skip to content

Instantly share code, notes, and snippets.

@dhermes
Last active October 23, 2019 16:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dhermes/ed324dbdc068755d8ff040d9a7293629 to your computer and use it in GitHub Desktop.
Save dhermes/ed324dbdc068755d8ff040d9a7293629 to your computer and use it in GitHub Desktop.
Benchmarking Benefits of "Passing Along" a Promise
node_modules/

Benchmarking Benefits of "Passing Along" a Promise

Running the microbenchmark produces

$ npm run benchmark

> @ benchmark .../promise-forwarding-microbenchmark
> ts-node index.ts

withForward x 436,408 ops/sec ±0.67% (76 runs sampled)
withResolve x 392,169 ops/sec ±0.48% (83 runs sampled)

The comparison is between a call chain that await-s all Promise-s before returning

async function threeArgs(x: number, y: number, z: number): Promise<number> {
  return x + y + z;
}
async function twoArgsResolve(x: number, y: number): Promise<number> {
  const value = await threeArgs(x, y, 42.0);
  return value;
}
async function oneArgResolve(x: number): Promise<number> {
  const value = await twoArgsResolve(x, 7.5);
  return value;
}

and one where the Promise is just forwarded along

async function threeArgs(x: number, y: number, z: number): Promise<number> {
  return x + y + z;
}
async function twoArgsForward(x: number, y: number): Promise<number> {
  return threeArgs(x, y, 42.0);
}
async function oneArgForward(x: number): Promise<number> {
  return twoArgsForward(x, 7.5);
}

Sanity Check

I had to dig a little bit to figure out how to test async functions with Benchmark. As a result, I was worried that the { defer: true, fn: wrap(fn1) } approach wasn't working as expected. So I added a sanity check to make sure that an async function that sleeps 50ms has twice the throughput as a function that sleeps 100ms:

$ npm run sanity-check

> @ sanity-check .../promise-forwarding-microbenchmark
> SANITY_CHECK=true ts-node index.ts

sleep50 x 19.03 ops/sec ±0.72% (68 runs sampled)
sleep100 x 9.77 ops/sec ±0.47% (49 runs sampled)
import Benchmark from "benchmark";
import * as process from "process";
type BooleanResult = () => Promise<boolean>;
interface Deferred {
resolve: () => void;
}
type Wrapped = (deferred: Deferred) => Promise<boolean>;
async function threeArgs(x: number, y: number, z: number): Promise<number> {
return x + y + z;
}
async function twoArgsForward(x: number, y: number): Promise<number> {
return threeArgs(x, y, 42.0);
}
async function twoArgsResolve(x: number, y: number): Promise<number> {
const value = await threeArgs(x, y, 42.0);
return value;
}
async function oneArgForward(x: number): Promise<number> {
return twoArgsForward(x, 7.5);
}
async function oneArgResolve(x: number): Promise<number> {
const value = await twoArgsResolve(x, 7.5);
return value;
}
async function withForward(): Promise<boolean> {
const value = await oneArgForward(2.5);
return value === 52.0;
}
async function withResolve(): Promise<boolean> {
const value = await oneArgResolve(2.5);
return value === 52.0;
}
async function sleep(sleepTimeMs: number): Promise<void> {
return new Promise(resolve => setTimeout(() => resolve(), sleepTimeMs));
}
function makeSleepFn(sleepTimeMs: number): BooleanResult {
return async function(): Promise<boolean> {
await sleep(sleepTimeMs);
return true;
};
}
function wrap(fn: BooleanResult): Wrapped {
return async function(deferred: Deferred): Promise<boolean> {
const value = await fn();
deferred.resolve();
return value;
};
}
function displayEventTarget(event: Benchmark.Event): void {
console.log(String(event.target));
}
function runSuite(name1: string, fn1: BooleanResult, name2: string, fn2: BooleanResult): void {
const suite = new Benchmark.Suite();
suite
.add(name1, { defer: true, fn: wrap(fn1) })
.add(name2, { defer: true, fn: wrap(fn2) })
.on("cycle", displayEventTarget)
.run({ async: true });
}
/**
* This is a sanity check on `runSuite()`.
*
* It makes sure that a function that sleeps 50ms has twice the throughput as a function
* that sleeps 100ms.
*/
function sanityCheck(): void {
const sleep50 = makeSleepFn(50);
const sleep100 = makeSleepFn(100);
runSuite("sleep50", sleep50, "sleep100", sleep100);
}
function main(): void {
if ("SANITY_CHECK" in process.env) {
sanityCheck();
return;
}
runSuite(withForward.name, withForward, withResolve.name, withResolve);
}
if (require.main === module) {
main();
}
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"@types/benchmark": {
"version": "1.0.31",
"resolved": "https://registry.npmjs.org/@types/benchmark/-/benchmark-1.0.31.tgz",
"integrity": "sha512-F6fVNOkGEkSdo/19yWYOwVKGvzbTeWkR/XQYBKtGBQ9oGRjBN9f/L4aJI4sDcVPJO58Y1CJZN8va9V2BhrZapA=="
},
"@types/node": {
"version": "12.11.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.2.tgz",
"integrity": "sha512-dsfE4BHJkLQW+reOS6b17xhZ/6FB1rB8eRRvO08nn5o+voxf3i74tuyFWNH6djdfgX7Sm5s6LD8t6mJug4dpDw=="
},
"arg": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.1.tgz",
"integrity": "sha512-SlmP3fEA88MBv0PypnXZ8ZfJhwmDeIE3SP71j37AiXQBXYosPV0x6uISAaHYSlSVhmHOVkomen0tbGk6Anlebw=="
},
"benchmark": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz",
"integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=",
"requires": {
"lodash": "^4.17.4",
"platform": "^1.3.3"
}
},
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
},
"diff": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz",
"integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q=="
},
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
},
"make-error": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
"integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g=="
},
"platform": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz",
"integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q=="
},
"prettier": {
"version": "1.18.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz",
"integrity": "sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw=="
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"source-map-support": {
"version": "0.5.13",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
"integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
"requires": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"ts-node": {
"version": "8.4.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.4.1.tgz",
"integrity": "sha512-5LpRN+mTiCs7lI5EtbXmF/HfMeCjzt7DH9CZwtkr6SywStrNQC723wG+aOWFiLNn7zT3kD/RnFqi3ZUfr4l5Qw==",
"requires": {
"arg": "^4.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"source-map-support": "^0.5.6",
"yn": "^3.0.0"
}
},
"typescript": {
"version": "3.6.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz",
"integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg=="
},
"yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="
}
}
}
{
"scripts": {
"benchmark": "ts-node index.ts",
"prettier": "prettier index.ts --write",
"sanity-check": "SANITY_CHECK=true ts-node index.ts"
},
"prettier": {
"parser": "typescript",
"printWidth": 120,
"quoteProps": "consistent",
"semi": true,
"singleQuote": false,
"trailingComma": "all"
},
"dependencies": {
"@types/benchmark": "1.0.31",
"@types/node": "12.11.2",
"benchmark": "2.1.4",
"prettier": "1.18.2",
"ts-node": "8.4.1",
"typescript": "3.6.4"
}
}
{
"compilerOptions": {
"esModuleInterop": true
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment