Add benchmark script (#104)
authorJérôme Benoit <jerome.benoit@sap.com>
Wed, 10 Feb 2021 11:43:21 +0000 (12:43 +0100)
committerGitHub <noreply@github.com>
Wed, 10 Feb 2021 11:43:21 +0000 (12:43 +0100)
Also improve benchmarking

12 files changed:
.eslintrc.js
benchmarks/bench.js
benchmarks/myBench.js
benchmarks/threadWorker.js [moved from benchmarks/yourWorker.js with 84% similarity]
benchmarks/workerThreadsWorker.js [moved from benchmarks/externalWorker.js with 100% similarity]
benchmarks/workerpoolWorker.js [new file with mode: 0644]
examples/dynamicExample.js
examples/multiFunctionExample.js
examples/staticExample.js
package-lock.json
package.json
src/workers.ts

index 6c710303838f3cfbcbb05084ea11b739872fbe61..04c5b05b596ab76bf7a9aa897900b190e4791b49 100644 (file)
@@ -9,7 +9,7 @@ module.exports = {
     ecmaVersion: 2020,
     sourceType: 'module'
   },
-  plugins: ['@typescript-eslint', 'prettierx'],
+  plugins: ['@typescript-eslint', 'promise', 'prettierx'],
   extends: [
     'standard',
     'eslint:recommended',
@@ -17,11 +17,9 @@ module.exports = {
     'plugin:import/errors',
     'plugin:import/warnings',
     'plugin:import/typescript',
+    'plugin:promise/recommended',
     'plugin:prettierx/standardx',
-    'plugin:prettierx/@typescript-eslint',
-    'prettier',
-    'prettier/standard',
-    'prettier/@typescript-eslint'
+    'plugin:prettierx/@typescript-eslint'
   ],
   rules: {
     'no-void': 'off',
@@ -45,16 +43,23 @@ module.exports = {
   overrides: [
     {
       files: ['*.js'],
+      extends: 'plugin:node/recommended',
       rules: {
         '@typescript-eslint/no-unused-vars': 'off',
         '@typescript-eslint/no-var-requires': 'off'
       }
     },
     {
-      files: ['examples/typescript/*.ts'],
+      files: ['examples/typescript/**/*.ts'],
       rules: {
         'import/no-unresolved': 'off'
       }
+    },
+    {
+      files: ['examples/**/*.js'],
+      rules: {
+        'node/no-missing-require': 'off'
+      }
     }
   ]
 }
index e0daca2d0aa84a85387f23414b884b1c57170f2d..017ac20be223e0fd26faba804e54c2329dcf931c 100644 (file)
@@ -1,18 +1,23 @@
 const Benchmark = require('benchmark')
 const suite = new Benchmark.Suite()
-const FixedThreadPool = require('../lib/fixed')
-const DynamicThreadPool = require('../lib/dynamic')
+const { FixedThreadPool } = require('../lib/index')
+const { DynamicThreadPool } = require('../lib/index')
 const size = 30
 const tasks = 1
 
+const LIST_FORMATTER = new Intl.ListFormat('en-US', {
+  style: 'long',
+  type: 'conjunction'
+})
+
 // pools
-const fixedPool = new FixedThreadPool(size, './yourWorker.js', {
+const fixedPool = new FixedThreadPool(size, './threadWorker.js', {
   maxTasks: 10000
 })
 const dynamicPool = new DynamicThreadPool(
   size / 2,
   size * 3,
-  './yourWorker.js',
+  './threadWorker.js',
   { maxTasks: 10000 }
 )
 const workerData = { proof: 'ok' }
@@ -27,12 +32,18 @@ async function fixedTest () {
   return new Promise((resolve, reject) => {
     let executions = 0
     for (let i = 0; i <= tasks; i++) {
-      fixedPool.execute(workerData).then(res => {
-        executions++
-        if (executions === tasks) {
-          resolve('FINISH')
-        }
-      })
+      fixedPool
+        .execute(workerData)
+        .then(res => {
+          executions++
+          if (executions === tasks) {
+            return resolve('FINISH')
+          }
+          return null
+        })
+        .catch(err => {
+          console.error(err)
+        })
     }
   })
 }
@@ -41,12 +52,16 @@ async function dynamicTest () {
   return new Promise((resolve, reject) => {
     let executions = 0
     for (let i = 0; i <= tasks; i++) {
-      dynamicPool.execute(workerData).then(res => {
-        executions++
-        if (executions === tasks) {
-          resolve('FINISH')
-        }
-      })
+      dynamicPool
+        .execute(workerData)
+        .then(res => {
+          executions++
+          if (executions === tasks) {
+            return resolve('FINISH')
+          }
+          return null
+        })
+        .catch(err => console.error(err))
     }
   })
 }
@@ -62,11 +77,15 @@ async function test () {
     })
     // add listeners
     .on('cycle', function (event) {
-      console.log(String(event.target))
+      console.log(event.target.toString())
     })
     .on('complete', function () {
-      this.filter('fastest').map('name')
-      console.log('Fastest is ' + this.filter('fastest').map('name'))
+      console.log(
+        'Fastest is ' +
+          LIST_FORMATTER.format(this.filter('fastest').map('name'))
+      )
+      // eslint-disable-next-line no-process-exit
+      process.exit()
     })
     // run async
     .run({ async: true })
index b714ab9e3a3b24cac366a7e6aecaa0d39662a26c..216b338e1777f9a74a9a825c4f53dbbd5be849db 100644 (file)
@@ -1,18 +1,24 @@
-const FixedThreadPool = require('../lib/fixed')
-const DynamicThreadPool = require('../lib/dynamic')
-const Pool = require('worker-threads-pool')
+const { FixedThreadPool } = require('../lib/index')
+const { DynamicThreadPool } = require('../lib/index')
+const WorkerThreadsPool = require('worker-threads-pool')
+const workerpool = require('workerpool')
 const tasks = 1000
 const size = 16
 
 // pools
-const externalPool = new Pool({ max: size })
-const fixedPool = new FixedThreadPool(size, './yourWorker.js', {
+const workerThreadsPool = new WorkerThreadsPool({ max: size })
+const workerPool = workerpool.pool('./workerpoolWorker.js', {
+  minWorkers: size / 2,
+  maxWorkers: size * 3,
+  workerType: 'thread'
+})
+const fixedPool = new FixedThreadPool(size, './threadWorker.js', {
   maxTasks: 10000
 })
 const dynamicPool = new DynamicThreadPool(
   size / 2,
   size * 3,
-  './yourWorker.js',
+  './threadWorker.js',
   { maxTasks: 10000 }
 )
 
@@ -24,14 +30,20 @@ async function fixedTest () {
   let executions = 0
   const time = Date.now()
   for (let i = 0; i <= tasks; i++) {
-    fixedPool.execute(workerData).then(res => {
-      executions++
-      if (executions === tasks) {
-        console.log(
-          `Fixed pool take ${Date.now() - time} to work on ${executions} tasks`
-        )
-      }
-    })
+    fixedPool
+      .execute(workerData)
+      .then(res => {
+        executions++
+        if (executions === tasks) {
+          return console.log(
+            `Fixed pool take ${
+              Date.now() - time
+            }ms to work on ${executions} tasks`
+          )
+        }
+        return null
+      })
+      .catch(err => console.error(err))
   }
 }
 
@@ -39,26 +51,30 @@ async function dynamicTest () {
   let executions = 0
   const time = Date.now()
   for (let i = 0; i <= tasks; i++) {
-    dynamicPool.execute(workerData).then(res => {
-      executions++
-      if (executions === tasks) {
-        console.log(
-          `Dynamic pool take ${
-            Date.now() - time
-          } to work on ${executions} tasks`
-        )
-      }
-    })
+    dynamicPool
+      .execute(workerData)
+      .then(res => {
+        executions++
+        if (executions === tasks) {
+          return console.log(
+            `Dynamic pool take ${
+              Date.now() - time
+            }ms to work on ${executions} tasks`
+          )
+        }
+        return null
+      })
+      .catch(err => console.error(err))
   }
 }
 
-async function externalPoolTest () {
+async function workerThreadsPoolTest () {
   let executions = 0
   const time = Date.now()
   for (let i = 0; i <= tasks; i++) {
     new Promise((resolve, reject) => {
-      externalPool.acquire(
-        './externalWorker.js',
+      workerThreadsPool.acquire(
+        './workerThreadsWorker.js',
         { workerData: workerData },
         (err, worker) => {
           if (err) {
@@ -71,22 +87,51 @@ async function externalPoolTest () {
           })
         }
       )
-    }).then(res => {
-      if (tasks === executions) {
-        console.log(
-          `External pool take ${
-            Date.now() - time
-          } to work  on ${executions} tasks`
-        )
-      }
     })
+      .then(res => {
+        if (tasks === executions) {
+          return console.log(
+            `worker threads pool take ${
+              Date.now() - time
+            }ms to work on ${executions} tasks`
+          )
+        }
+        return null
+      })
+      .catch(err => console.error(err))
+  }
+}
+
+async function workerpoolTest () {
+  let executions = 0
+  const time = Date.now()
+  for (let i = 0; i <= tasks; i++) {
+    workerPool
+      .exec('yourFunction', [workerData])
+      .then(res => {
+        executions++
+        return null
+      })
+      .catch(err => console.error(err))
+      .then(res => {
+        if (tasks === executions) {
+          return console.log(
+            `workerpool take ${
+              Date.now() - time
+            }ms to work on ${executions} tasks`
+          )
+        }
+        return null
+      })
+      .catch(err => console.error(err))
   }
 }
 
 async function test () {
-  fixedTest()
-  dynamicTest()
-  externalPoolTest()
+  await fixedTest()
+  await dynamicTest()
+  await workerThreadsPoolTest()
+  await workerpoolTest()
 }
 
 test()
similarity index 84%
rename from benchmarks/yourWorker.js
rename to benchmarks/threadWorker.js
index ac3959d121f1fcfa123f1118374a1c4b3d2bcf4f..b8f974d885218f5ff9b60a593111534a0f5df640 100644 (file)
@@ -1,5 +1,5 @@
 'use strict'
-const { ThreadWorker } = require('../lib/workers')
+const { ThreadWorker } = require('../lib/index')
 
 function yourFunction (data) {
   for (let i = 0; i <= 1000; i++) {
diff --git a/benchmarks/workerpoolWorker.js b/benchmarks/workerpoolWorker.js
new file mode 100644 (file)
index 0000000..c530257
--- /dev/null
@@ -0,0 +1,16 @@
+const workerpool = require('workerpool')
+
+function yourFunction (data) {
+  for (let i = 0; i <= 1000; i++) {
+    const o = {
+      a: i
+    }
+    JSON.stringify(o)
+  }
+  // console.log('This is the main thread ' + isMainThread)
+  return { ok: 1 }
+}
+
+workerpool.worker({
+  yourFunction: yourFunction
+})
index 22b4f19c4b7394c92b1b91b7d8ffad432e810e4c..4003c53462878652804fb8a051feeb792553c051 100644 (file)
@@ -10,11 +10,15 @@ pool.emitter.on('FullPool', () => maxReached++)
 const start = Date.now()
 const iterations = 1000
 for (let i = 0; i <= iterations; i++) {
-  pool.execute({}).then(res => {
-    resolved++
-    if (resolved === iterations) {
-      console.log('Time take is ' + (Date.now() - start))
-      console.log('The pool was full for ' + maxReached + ' times')
-    }
-  })
+  pool
+    .execute({})
+    .then(res => {
+      resolved++
+      if (resolved === iterations) {
+        console.log('Time take is ' + (Date.now() - start))
+        return console.log('The pool was full for ' + maxReached + ' times')
+      }
+      return null
+    })
+    .catch(err => console.error(err))
 }
index d1314aa94149fe7324fda3df0b91e240f9865532..0c4b749e6bbdabc5d542de614e47706a22ab7f09 100644 (file)
@@ -4,9 +4,13 @@ const pool = new FixedThreadPool(15, './multifunctionWorker.js', {
   onlineHandler: () => console.log('worker is online')
 })
 
-pool.execute({ fname: 'fn0', input: 'hello' }).then(res => console.log(res))
+pool
+  .execute({ fname: 'fn0', input: 'hello' })
+  .then(res => console.log(res))
+  .catch(err => console.error(err))
 pool
   .execute({ fname: 'fn1', input: 'multifunction' })
   .then(res => console.log(res))
+  .catch(err => console.error(err))
 
 setTimeout(pool.destroy.bind(pool), 3000)
index f125c477e20df4179c78984eb05f56fbb1a342ed..d81e3484e89946374cc3056208e4d888a0162722 100644 (file)
@@ -8,10 +8,14 @@ const pool = new FixedThreadPool(15, './yourWorker.js', {
 const start = Date.now()
 const iterations = 1000
 for (let i = 0; i <= iterations; i++) {
-  pool.execute({}).then(res => {
-    resolved++
-    if (resolved === iterations) {
-      console.log('Time take is ' + (Date.now() - start))
-    }
-  })
+  pool
+    .execute({})
+    .then(res => {
+      resolved++
+      if (resolved === iterations) {
+        return console.log('Time take is ' + (Date.now() - start))
+      }
+      return null
+    })
+    .catch(err => console.error(err))
 }
index 0e41df5bb9504ce28587b90379e49872f98e532c..30342d12c415306409b78a09d07c0fe0aaabb5a9 100644 (file)
       "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
       "dev": true
     },
+    "after-all": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/after-all/-/after-all-2.0.2.tgz",
+      "integrity": "sha1-IDACmO1glLTIXJjnyK1NymKPn3M=",
+      "dev": true,
+      "requires": {
+        "once": "^1.3.0"
+      }
+    },
     "aggregate-error": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
           "requires": {
             "has-flag": "^4.0.0"
           }
+        },
+        "workerpool": {
+          "version": "6.0.2",
+          "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz",
+          "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==",
+          "dev": true
         }
       }
     },
       "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
       "dev": true
     },
+    "worker-threads-pool": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/worker-threads-pool/-/worker-threads-pool-2.0.0.tgz",
+      "integrity": "sha512-5dtGbEucee6o5/kQgpyKIUoHGWf8488DP3ihZDJzDIVvH4V+NA6HdBl/I5ckI4yN1NwM68pdZDbrwac1M95mEA==",
+      "dev": true,
+      "requires": {
+        "after-all": "^2.0.2"
+      }
+    },
     "workerpool": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz",
-      "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==",
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz",
+      "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==",
       "dev": true
     },
     "wrap-ansi": {
index 02be7c8534b4b273cade17518bdb7b4eaf24c2ca..b7bedf1382d1473a49c61f75b1c031e66d37acfe 100644 (file)
@@ -6,6 +6,7 @@
   "scripts": {
     "build": "npm run build:clean && tsc",
     "build:clean": "rimraf lib",
+    "benchmark": "npm run build && node benchmarks/bench.js",
     "test": "npm run build && nyc mocha --exit --timeout 20000 tests/**/*.test.js",
     "test:debug": "mocha --inspect-brk --exit tests/**/*.test.js",
     "coverage": "nyc report --reporter=text-lcov | coveralls",
@@ -52,7 +53,6 @@
     "benchmark": "^2.1.4",
     "coveralls": "^3.1.0",
     "eslint": "^7.19.0",
-    "eslint-config-prettier": "^7.2.0",
     "eslint-config-standard": "^16.0.2",
     "eslint-plugin-import": "^2.22.1",
     "eslint-plugin-node": "^11.1.0",
@@ -66,7 +66,9 @@
     "prettier-plugin-organize-imports": "^1.1.1",
     "prettierx": "^0.17.0",
     "rimraf": "^3.0.2",
-    "typescript": "^4.1.3"
+    "typescript": "^4.1.4",
+    "worker-threads-pool": "^2.0.0",
+    "workerpool": "^6.1.0"
   },
   "engines": {
     "node": ">=12.11.0",
index 57f2fd95bef547161ca510078a5191dd6bd0ee9b..dc4f319419a7d2b31b9402e6c8e5ad59590355ca 100644 (file)
@@ -108,6 +108,7 @@ export class ThreadWorker<Data = any, Response = any> extends AsyncResource {
       .then(res => {
         this.parent?.postMessage({ data: res, id: value.id })
         this.lastTask = Date.now()
+        return null
       })
       .catch(e => {
         this.parent?.postMessage({ error: e, id: value.id })