Daninet / hash-wasm

Lightning fast hash functions using hand-tuned WebAssembly binaries
https://npmjs.com/package/hash-wasm
Other
881 stars 49 forks source link

Performance issues on browser for pbkdf with sha512 #21

Closed StephenLynx closed 3 years ago

StephenLynx commented 3 years ago

This is my code:


var salt = 4;
var iterations = 16384;
var hashLenght = 256;
var newLabel = 'new time';
var oldLabel = 'old time';
var cachedHash = hashwasm.createSHA512();
var result = 'NXmIcqNIcO8iods77YNNtLk3PRlJ/KdCkhufUKbFr+d4w9pIyE8Kr/a87VZa8izvV6LLB35rIyEiK9HhKMRJcntx/AwKfzumBTAHuIdELIaMqXuKBv1dHhFTa+rL7KfMTp+F6LiLJY4LxYgEqmx5SgpRD2/s5qUaRJWWTGsAG19sY5aJ/2XhGRN1vhS55ziHwXx5xNfEj//77pGxU8lWMVYL/2eqbNN3DN8RC6KQPmfB+LXizfLfkJ6ZtYDLlvIul5ilz2o2IVFvibzaIpswwhoW1jVB+FL2GaZAXgW3+ce1AYrsW6TVZPbw1YH2VoCA6OTTOcbLONJhybZJdp95Nw==';

async function runOld(){

  console.log('starting old');
 var start = new Date();

  var textEncoder = new TextEncoder("utf-8");

  var passwordBuffer = textEncoder.encode(session);

  var importedKey = await
  crypto.subtle.importKey("raw", passwordBuffer, "PBKDF2", false,
      [ "deriveBits" ]);

  var params = {
    name : "PBKDF2",
    hash : "SHA-512",
    iterations : iterations
  };

  params.salt = textEncoder.encode(salt);

  console.log('old key: ', result === btoa(String.fromCharCode(...new Uint8Array(
      await crypto.subtle.deriveBits(params, importedKey, hashLenght * 8)))))

   var end = new Date();

  console.log((end - start) / 1000);

}

async function run(repeat) {

  console.log('starting new');

  var start = new Date();

  const key = await hashwasm.pbkdf2({
    password: session,
    salt: salt.toString(),
    iterations: iterations,
    hashLength: hashLenght,
    hashFunction: cachedHash,
    outputType: 'binary',
  });

  console.log('new key:', result === btoa(String.fromCharCode(...new Uint8Array(key))));
  var end = new Date();
  console.log((end - start) / 1000);

}

runOld();

run();```

I'm geting very slow times when it comes to the WASM implementation, while ff can get times of 0.009 seconds, WASM gets over 3 seconds. chromium gives me similar results on WASM, even if native is a bit slower than ff. I tried running it twice in sequence, but didn't change a thing. You can notice I have cached createSHA512 too. My friend ran this code and got similar results. ps: notice that behavior changes between firefox and chrome, ff will run wasm only when native is done, while chrome starts and finishes both at the same time more or less.
Daninet commented 3 years ago

Take care, there is a bug in your code. You should run the benchmarks one after another (await runOld(); await run()). Otherwise WASM execution might block the JS main thread and the native version will only get back the control after hash-wasm is finished. That's why you see inconsistent behavior.

You are comparing the native SubtleCrypto API with WASM. WASM libraries will probably never outperform the native implementations, because they need to be compiled at runtime and they doesn't have access to all specialized CPU instructions. So native implementations will be always faster, but they also have disadvantages (various levels of browser support, https requirement, complicated build processes, less forward-compatibility, etc.). For PBKDF2-SHA512 you might want to use SubtleCrypto if available and use hash-wasm as a fallback implementation where SubtleCrypto is not working.

StephenLynx commented 3 years ago

Yeah, I was aware of how they might run concurrently. But even when I ran wasm on it's own, it was quite slow. My issue here is that subtle doesn't let you run concurrently, even if you run it multiple times across different web workers, they will run sequentially, hence why I'm looking into wasm alternatives. I know it can't compare, but when it takes over 3 seconds and native takes 9 milliseconds, the gap is just way too large, I thought there was something wrong here. Are you SURE this is as good as it gets with WASM? There is nothing wrong with my code or with the library?

Daninet commented 3 years ago

Please note that having the DevTools open slows down the interpreter (certain optimizations are disabled).

On my computer I get the following measurements: Chrome SubtleCrypto + DevTools: 36ms Chrome hash-wasm + DevTools: 971ms (27x slower) Chrome SubtleCrypto: 35ms Chrome hash-wasm: 203ms (6x slower)

Firefox SubtleCrypto + DevTools: 93ms Firefox hash-wasm + DevTools: 1031ms (11x slower) Firefox SubtleCrypto: 94ms Firefox hash-wasm: 335ms (3.5x slower)

Also hash-wasm is not fully optimized for PBKDF2, because I wanted to keep the flexibility of supplying any hash algorithm without increasing the bundle size too much. But still, I believe that 3-6x slowdown is something acceptable when comparing to native implementations. WASM is still relatively young. We will probably see better figures in the future.

Daninet commented 3 years ago

One more thing I noticed: Firefox seems to cheat... It's caching the SubtleCrypto results between runs and even between page refreshes. When I randomize the password, the 1ms timings disappear and I get a consistent 95 ms.

StephenLynx commented 3 years ago

I tried not using the console, I tried disabling devtools on about:config using 2 settings and I never got a time better than 1.9s.

Daninet commented 3 years ago

What architecture do you use? I tested with Windows 10 Chrome v91 x64 Intel i7-7700k. I didn't change anything in about:config... I just refresh the page with dev tools closed and after some seconds I open it to see the results. Could you try running it in Node.js?

StephenLynx commented 3 years ago

i5-3337U @ 1.80GHz, centos 7, firefox 78.11.0esr 64 bits. On node I can just use node's pbkdf2 implementation, I only have a use for it on the browser.

Daninet commented 3 years ago

Seeing the node.js results would be useful because it behaves similarly to Chrome when DevTools is closed. In that way we could exclude the Chrome issues.

StephenLynx commented 3 years ago

Yeah, I can see what you mean. Chromium with the console closed managed to run on 0.7s, console open, over 4 seconds. So it points to something funny going on firefox.

Daninet commented 3 years ago

I think this issue can be closed.

StephenLynx commented 3 years ago

Can it? Did you manage to figure anything on firefox that could be causing it? Or are you just assuming there's nothing to be done on your side?

Daninet commented 3 years ago

For me the performance looks already good in Firefox: https://github.com/Daninet/hash-wasm/issues/21#issuecomment-866311497 I cannot reproduce those Firefox performance issues you mentioned. But tell me if you have a clue how can I do that.

Generally speaking, when I'm doing optimizations, I only check the raw WASM instructions of the different hash algorithms and try to make them the most efficiently executable on modern CPUs. It's the browser's job to do generate efficient native assembly from it.

I don't want to make any browser- or platform-specific optimizations. It would be a never ending job to keep track of what instructions are efficient on each platform / browser engine version and it would also make the bundles a lot larger if I start having N builds for the different environments.

Daninet commented 3 years ago

I've just seen that you mentioned that you're using firefox 78.11.0esr. That's a pretty old version. Could you try with the latest v89.0.2?

StephenLynx commented 3 years ago

If you are not having issues on a newer version then I suppose they already changed whatever was causing. I'm using that version because is what centos 7 ships.