Recently I found an old project that I did at faculty with my colleagues. It's written in C# and it detects edges on an image. 🖼
To know what I'm talking about, it looks like this:
I remembered how much fun was to code that thing, and all the challenges associated with it. It was one of the more interesting projects I worked on at faculty. 👨🎓
It uses Canny Edge detection algorithm, and I won't bore you with the theory behind it. But if that's the kind of geek you are 🤓, I totally respect that, so here's the link explaining everything:
https://en.wikipedia.org/wiki/Canny_edge_detector
So we implemented the algorithm from scratch, and it wasn't very complicated. But, it was inefficient. 🐢
When we tested our app, we found that it was very very slow, even for small images. I researched why that was, and quickly found out that the reason is C#'s automated memory management. See, our code was modifying the bytes of image data, which was held in a locked memory part. So each operation of writing to a byte of data was: unlock that memory address, write the byte in, lock the memory again. And so on and on for every write operation. So naturally the code was slow.
I quickly found a way to tell C# to exclude a part of code from the automated memory management, which was what we needed to make things faster. To tell C# not to lock that part of memory, that we know what we're doing. So we implemented that, and our app instantly became an order of magnitude faster. ⚡️
But next I thought, maybe there's a way to speed it up even more. So we discovered that C# had built in support for parallel programming. It had a parallel for loop, which was just a call to Parallel.for away. So we changed that, and again, our app became even faster, noticeably faster.
So that's the story behind it. With such fond memories, I decided it was only fair to rewrite it in JavaScript now. So I did that as a challenge to myself, and finished in one evening. 🙌 Few days later, I rewrote it in TypeScript, because why not!
Here's the repo with a demo, so you can play around with it:
https://petarjs.github.io/js-canny-edge-detector/
You can check out the console, I added timings for different operations on the image to see how fast it was doing the processing. ⏰
And of course, the app is not very fast, much slower than it worked on (optimized version of) C# code. 😢 There isn't parallel for, or direct memory access to speed things up in JavaScript.
But the most bothering thing was that the UI became blocked while the app was calculating all the pixel data, because JS is single threaded.
So to circumvent that, I transferred the code doing the calculations to Web Worker, and the UI stopped being blocked. That was an interesting experience, as I never worked with WebWorkers before. Learned a lot! 🌟
Next thing it to speed it up a bit.
Maybe rewrite it using WebAssembly? :D
Image taken from here