Web Worker Developer Tools
Sat, 10/07/2017 - 22:28

If you have no idea what Web Workers are, go check out Using Web Workers page.

Let me start with a little backstory. I've created small project, that shows data from Prometheus timedata on map, and I though, there will be a lot of data, as I wanted to show data for past week or month (with scrape interval of 5 minutes, it's about 10k samples). OK, that's not so much, even multiplied by 30, it's just 30 x 10k items arrays. But I wanted to try how those Web Workers work and maybe improve performance of tool nobody else will use.

So I spent hours looking for the right module or plugin that would make it as easy as possible to use Web Workers and something that worked for me and vue.js. Finally I've found worker-loader and added it to webpack config:

{
  test: /workers.*\.js$/,
  loader: 'worker-loader',
  options: {
    inline: true
  }
}

I created worker that will take responses from API, process it and return objects. That's it. Function contains sorting and multiple iterations over arrays. Result was `processCars' running roughly ~100 ms, so even running this on main thread wouldn't be any issue. But you can see that whole Worker process took longer ~240 ms from receiving message to sending message with results back to main thread. I can just guess what's worker doing before and after actual working.

Web Worker process

One a bit unrelated culprit is sending data to worker, I had to stringify and again parse data, because some problem (more about this issue on stackowerflow). This is how I call worker and wait for response.

    return new Promise((resolve, reject) => {
      worker.onmessage = ({data}) => {
        return resolve(data)
      }
      worker.onerror = (error) => {
        reject(new Error(error))
      }
      worker.postMessage({action: 'processCars', results: JSON.parse(JSON.stringify(results))})
    })

Stringify took ~80 ms plus it took another ~120 ms until message was sent from Main thread to Worker. Another ~75 ms then worker.onmessage which just resolves data. Combining all together, 80 + 120 + 250 + 75 = 525 ms, that's quite a lot, when part that I wanted to run on Worker thread took just ~100ms and everything else (425ms) is sending messages between threads. I don't see any gain here.

Worker time

After some optimizations I found out, that really main problem is large dataset, after returning much smaller (from 10k to tens of items) arrays from Worker it took just ~1 ms compared to 75 ms in previous approach. What I learned from this is: sending large data between threads is expensive, if you want to not loose time, optimize payloads.

Just for comparison, that tiny bit on right is worker.onmessage, thanks to small payload.

Small payload

No let's look at what happens, when I move processCars to Main thread. Obvious part will be that time spent on sending messages will be eliminated. But execution itself was faster on Main (~100 -> ~65 ms) but can be coincidence or not. I've tested it only from few runs, no benchmark was used.

On the Main thread

Conclusion

I should have thought about data flow before introducing Web Worker to my code. And also check if it makes sense in my case. What would make better sense is to move fetching data to Worker, not just processing, this way I'd eliminate the expensive communication. Fetch+process and send just small payload back to Main. Now it is obvious to me.

Code references