Skip to content

[PM-3426] Make cipher decryption faster by running in parallel#2685

Closed
quexten wants to merge 5 commits intobitwarden:mainfrom
quexten:parallel-cipher-decryption
Closed

[PM-3426] Make cipher decryption faster by running in parallel#2685
quexten wants to merge 5 commits intobitwarden:mainfrom
quexten:parallel-cipher-decryption

Conversation

@quexten
Copy link
Contributor

@quexten quexten commented Aug 13, 2023

Type of change

  • Bug fix
  • New feature development
  • Tech debt (refactoring, code cleanup, dependency upgrades, etc)
  • Build/deploy pipeline (DevOps)
  • Other

Objective

With a lot of ciphers in the vault, opening the vault (unlock/login) takes quite long (~25 seconds for my 10k cipher test vault). One of the causes seems to be that the cipher decryption runs concurrently (through async await) but not in parallel.

This PR rewrites the cipher decryption logic a bit to be both more readable and to run in parallel. On my emulator device, this brought the decryption time down to ~15 seconds. (An improvement, but there might still be a bottleneck somewhere else, since not all CPU resources were fully utilized. On the other hand, the emulator might also hinder further parallelization).

Related:
bitwarden/mobile#579

Code changes

Screenshots

Before you submit

  • Please check for formatting errors (dotnet format --verify-no-changes) (required)
  • Please add unit tests where it makes sense to do so (encouraged but not required)
  • If this change requires a documentation update - notify the documentation team
  • If this change has particular deployment requirements - notify the DevOps team

@bitwarden-bot
Copy link

Thank you for your contribution! We've added this to our internal Community PR board for review.
ID: PM-3426

@bitwarden-bot bitwarden-bot changed the title Make cipher decryption faster by running in parallel [PM-3426] Make cipher decryption faster by running in parallel Aug 13, 2023
Copy link
Member

@vvolkgang vvolkgang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Added one small note about the usage of .Result but, I believe we might have some additional quick wins here.

Before we get into that, we should take the opportunity and write a repeatable-ish performance test so we can have more confidence in our findings. For the time being this will probably be a new method / class in our Core.Tests and we can later look at an in-app test.

Checking with our devs and I'll circle back!

var decCiphers = parallelQuery
.Select(cipher =>
{
return cipher.DecryptAsync().Result;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I understand why the current approach led you here, we need to go Async All The Way, ie, not using .Result.

It's usually a great indicator that everything else needs to be refactored.

Copy link
Contributor Author

@quexten quexten Aug 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the article. I'm not too familiar with how c# handles async. I believe in this case it might not have been a problem, since every synchronous, blocking .Result is on a separate thread and thus has a different synchronization context, but I might also be wrong about this.

Either way, I've changed it now to use async only, so it should be fine now.

@quexten
Copy link
Contributor Author

quexten commented Aug 18, 2023

To get some more accurate numbers to this, I measured the time in emulator on my 10k cipher test account (using DateTime.Now). I think the numbers paint a pretty clear picture.

Time	Device Name	Type	PID	Tag	Message
08-18 23:06:32.668	New_Device_1_API_34	Verbose	17698	mono-stdout	Time taken new 11.434737
08-18 23:06:18.895	New_Device_1_API_34	Verbose	17698	mono-stdout	Time taken old 33.309191

(This is specifically only decryption time, getting the encrypted ciphers from storage using await GetAllAsync(); (which is not parallel) was excluded.)

This is a ~200% speedup in emulator, likely more on actual devices.

@quexten
Copy link
Contributor Author

quexten commented Jun 11, 2024

Closing. The native apps make this irrelevant.

@quexten quexten closed this Jun 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants