PoW performance
Posted May 26, 2021 by Aravinth Manivannan ‐ 3 min read
Last Edited September 1, 2021
We are mCaptcha. We build kickass CAPTCHA systems that gives (DDoS) attackers a run for their money. And we do all of this without tracking your users. Oh and did I mention our UX is great?
mCaptcha uses a proof-of-work(PoW) mechanism to rate limit users or potential bots. In order for this to be effective, the PoW should be configured properly. The difficulty requirement can’t be too high, as it could cause accessibility issues on the client-side while at the same time, it shouldn’t be too low, as it wouldn’t offer proper protection against bots.
Malicious bots(the ones that wreak havoc), run native code which is capable of running in a multi-threaded context. This creates an unfair advantage for crackers using these bots over legitimate users, who usually use browsers to access a website.
I wanted to see how much of an advantage a native program would have over our WASM library.
Benchmark tools
So I wrote these to compare native and WASM performances:
- Browser benchmark: https://mCaptcha.github.io/benches
- Native benchmark: mCaptcha/cli managed by scripts/bench.sh
Results
The tests were run on my development machine featuring an Intel Core i7-9750h.
Native
Difficulty | Real | User | Sys |
---|---|---|---|
50000 | 0m0.040s | 0m0.040s | 0m0.000s |
300000 | 0m0.122s | 0m0.122s | 0m0.000s |
550000 | 0m0.124s | 0m0.119s | 0m0.003s |
800000 | 0m0.123s | 0m0.118s | 0m0.003s |
1050000 | 0m0.933s | 0m0.932s | 0m0.000s |
1300000 | 0m1.227s | - | 0m0.007s |
1550000 | 0m1.260s | 0m1.256s | 0m0.003s |
1800000 | 0m1.243s | 0m1.242s | 0m0.000s |
2050000 | 0m2.524s | 0m2.510s | 0m0.000s |
2300000 | 0m2.545s | 0m2.543s | 0m0.000s |
2550000 | 0m2.561s | 0m2.551s | 0m0.004s |
2800000 | 0m2.555s | 0m2.540s | 0m0.006s |
3050000 | 0m2.513s | 0m2.508s | 0m0.000s |
3300000 | 0m2.484s | 0m2.481s | 0m0.000s |
3550000 | 0m2.643s | 0m2.642s | 0m0.000s |
3800000 | 0m2.663s | 0m2.661s | 0m0.000s |
4050000 | 0m2.663s | 0m2.660s | 0m0.000s |
4300000 | 0m2.689s | 0m2.683s | 0m0.004s |
4550000 | 0m2.688s | 0m2.686s | 0m0.000s |
4800000 | 0m2.517s | 0m2.509s | 0m0.003s |
Browser
I ran the tests on both Firefox and Chromium to compare results
Firefox
- User Agent:
Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0
- Hardware concurrency: 12
Difficulty | Duration(in ms) |
---|---|
500000 | 401 |
1000000 | 413 |
1500000 | 398 |
2000000 | 394 |
2500000 | 1495 |
3000000 | 1556 |
3500000 | 3971 |
4000000 | 4235 |
4500000 | 4116 |
To be fair, my Firefox installation is loaded with a gazillion extensions while the Chromium instance is clean, as I don’t use it much
Chromium
- User Agent:
Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36
- Hardware concurrency: 12
Difficulty | Duration(in ms) |
---|---|
500000 | 399.40000000037253 |
1000000 | 354.6000000014901 |
1500000 | 351.19999999925494 |
2000000 | 353.80000000074506 |
2500000 | 1337.800000000745 |
3000000 | 1311.199999999255 |
3500000 | 3417.5999999996275 |
4000000 | 3488.800000000745 |
4500000 | 3458.2999999988824 |
Conclusion
At the highest difficulty factor, the native implementation was a almost second faster than the WASM library. But the fact that both of them were able to run to completion in under 5 seconds is impressive!
So, in my opinion, native implementation is only slightly faster than the WASM library and for all intents and purposes, this shouldn’t matter much.
P.S Work is underway to benchmark multiple platforms. A detailed report will be published when that data is available.
For this post, I asked some of my friends to run the tests on their computers. The results slightly varied but even the slowest case generated proof for 4500000 difficulty(the highest in this test), in under 15 seconds!