Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance Improvements #135

Open
Flamifly opened this issue Dec 16, 2024 · 32 comments
Open

Performance Improvements #135

Flamifly opened this issue Dec 16, 2024 · 32 comments

Comments

@Flamifly
Copy link
Contributor

Issue for multiple small or big performance improvements

@Flamifly
Copy link
Contributor Author

using TryGetValue in AppSettings so you look for the value once instead 3 times
image
https://github.com/Flamifly/EasePass/tree/performance

@FrozenAssassine
Copy link
Owner

Oh I see what you mean, totally forgot about such functions, long time not visited those files 😂. Glad we have you ❤️

@Flamifly
Copy link
Contributor Author

just return 😂
image
Flamifly@c58efba

@Flamifly
Copy link
Contributor Author

Tomorrow i will implement the next performance improvement for GetAllDatabasePaths on the Database class
image

FrozenAssassine added a commit that referenced this issue Dec 17, 2024
Added Performance improvements for #135
@Flamifly
Copy link
Contributor Author

a little performance improvement
image

reduced allocation of ToLower and increased performance with that small change

@Flamifly
Copy link
Contributor Author

I noticed that I used contains and not equals so I changed it and it was even faster
image

@Flamifly
Copy link
Contributor Author

next Improvement
Linq is mostly easy to use but way slower than the stuff you can implement
image

@Flamifly
Copy link
Contributor Author

accesing Properties take longer than accesing a local variable
image

so i added a local variable length for the for loop

@Flamifly
Copy link
Contributor Author

Flamifly commented Dec 19, 2024

StringBuilder could be initialize once and not twice
image

Need to check if it would be faster if there would just a Clear instead of a new instalization before I implement that

@FrozenAssassine
Copy link
Owner

accesing Properties take longer than accesing a local variable image

so i added a local variable length for the for loop

Do you have some benchmark values for those, as I mentioned in #148, I am curious if it makes that much of a difference when using array.Lenght or list.Count over using a local variable?

@Flamifly
Copy link
Contributor Author

I got some but the result is not aspected in my opinion
image

it's almost pretty similar performance in this one the List.Count is faster but I did 4 Benchmarks and the local was usually faster at them but not much. Overall it was max 1-2 ms faster in 1 000 000 000 Iterations
In .Net it seems that it's just a little bit faster but not pretty much.
In other languages it is possible to be 25% faster with locals because of that I am used to do it always this way

@FrozenAssassine
Copy link
Owner

Well for me this would not be a reason to rewrite everything, but now since I already merged we have it like you did now.

It's not a big deal, but I was really curious about it, thanks for measuring ✌️

@Flamifly
Copy link
Contributor Author

Iterating over string is slightly slower than iterating over span
image

@Flamifly
Copy link
Contributor Author

Btw I will change the logic for is Secure alot to improve the overall performance for generating:

Tested with:
image

Before:
image

After:
image

@Flamifly
Copy link
Contributor Author

Still some room for improvemence here

@FrozenAssassine
Copy link
Owner

Yeah the allocated is pretty high in comparison, good thing to change that

@Flamifly
Copy link
Contributor Author

It wasn't the allocation at all. I had 1.89kb allocation with 6.8us less than the original implementation

The most Gain of Performance belongs to a change I would do on ContainsSequence
image

In the original Implementation the string was converted to lower case and even if you do that on a span it is slow.
Instead we can use StringComparison.OrdinalIgnoreCase which does a way faster job with probably the same result (Did not test the result yet)

@FrozenAssassine
Copy link
Owner

Oh true, I also think converting to lower case is the worst way for comparison. Since there are way faster ways. Finn is no one who thinks about speed 😂

@Flamifly
Copy link
Contributor Author

😂

Is there something else that I should focus on optimising?

@FrozenAssassine
Copy link
Owner

Hmm, I can not think about something rn

@Flamifly
Copy link
Contributor Author

Flamifly commented Dec 23, 2024

Next Performance Improvements:
PasswordHelper:

  • Change the Evaluate Method (Use one For loop instead of 4 Anys)
  • Change the Split in ReadIsPwnedData to Slices (Treat string as Span)

Database:

  • Replace the ToLower in RemoveDatabasePath to Equals with ignore case.
  • Do not use AddRage on the List at RemoveDatabasePath just add the remaining Paths to the List (Use the Array for iterating)
  • Replace Select with a For Loop in GetAllUnloadedDatabases

DatabaseItem:

  • Replace Path.GetFileNameWithoutExtension in MakeDatabaseName with Slice (Treat string as Span (probably faster need to be checked before))

RequestHelper:

  • Replace Regex with Sourcegenerated Regex in DownloadFileAsync | done

And More

@Flamifly
Copy link
Contributor Author

Flamifly commented Dec 27, 2024

Performance Improvement Generating Password:
Found a better way to generate a Password

(Length: 12)
image

(length: 20)
image

As you can see it is way more efficiant and will not be much slower even if you increase the length

I will later go into detail what I changed

@Flamifly
Copy link
Contributor Author

So now the Diffirences
Old Implementation:
GeneratePassword:
image

IsSecure:
image

GetMaxPoints:
image

New Implementation:
Generate:
image

GetAllowedIncludes:
image

IsSecure (similar to GetAllowedIncludes)
image

Performance:
image
I am not sure what exactly I changed at the old Logic that it performs that bad thought I did not touch it.
But that does not matter now

Differences:
What did I change and why?

  1. I implemented a Method that gets all includes of the allowed Chars. In my case an Include is if the Category of a Password is available or not (old logic maxpoints). I did this change because the Characters should not change while generating a new Password so it would perform much worse (need to change the IsSecure Method because we know which steps can be skipped)

Tomorrow I will end this
(wanted to add some Benchmarks for each Step and the result was better than my current implementation😂)
I will check if I can optimize them before I end this
Last Benchmark after first step:
image

@Flamifly
Copy link
Contributor Author

Just by changing the way the Password will be checked results is about 90% faster is very interesting in my opinion

@FrozenAssassine
Copy link
Owner

Pretty good job. I would've never figured this out 😂. You got som very good looking differences in performance waiting to get this merged and tested👍.

@Flamifly
Copy link
Contributor Author

Flamifly commented Dec 28, 2024

Ok now:
(length: 20)
image
image
image
image

I changed alot now.
First change is that I added a For Loop instead of checking always if I have reached the Length. (Was slightly faster)
Because of the For Loop the IsSecure Method will not be called every time a change on the StringBuilder has been made (reduced allocation (we do not call ToString always for the IsSecure Method) and reduced the amount of checks inside the Secure Method, if the char is a number etc.) (was about 1us faster)
I also moved the Random Instance to the class to avoid allocation every time the Method will be called (no need at all still slightly faster)
image
image

As you can see the allocations are lower and we overall do not need that much than yesterday
The Next step was to reduce the allocations even more.
Where the allocations happen is simple they happen at the ToString of the StringBuilder because our IsSecure Method need a String (ReadOnlySpan in our case).
How can we avoid the allocations there? It's pretty simple we create a new Span of chars on the Stack and copy everything from the StringBuilder to the Span in my case I did it at the IsSecure Method
image
By this we reduced the allocations and improved the performance slightly

If you ask where the other allocations happen:
We allocate the StringBuilder (because it is an Object and not a ValueType) and the ToString because the Method should return a String

You could reduce the allocations still by working only with a Stack allocated Span but it was somehow 0.6us slower than the StringBuilder implementation might be that it is faster to add something to the StringBuilder than changing something by index

If I forgot something to mention just tell me

@Flamifly
Copy link
Contributor Author

I will add this later to my Performance Branch and will create a Merge Request

@Flamifly
Copy link
Contributor Author

Should we really check if the Password contains Punctuation or should we check if the Password contains Special characters?

because if the Password contains "<>%$§" without ".:;," it will be seen as insecure should we change that?

@Flamifly
Copy link
Contributor Author

Performance Improvement for the RequestHelper:
Changing the Regex to a Generated Regex
image
image

By using a Generated Regex the Compiler will optimise it because of that it is in the most cases faster

@Flamifly
Copy link
Contributor Author

Performance Improvement for the DatabaseItem:
Change GetFileNameWithoutExtension to own implemented one -> is not worth to do that if you need to work with a String
image

If you do not need to work with a String and can work with a Span it would be worth it
image

Since that is not the case for the DatabaseItem I wont do that

@Flamifly
Copy link
Contributor Author

Performance Improvement for the Database:
chang the comparison from ToLower to Equals in RemoveDatavase:
image

It's way way way faster to use Equals instead of ToLower == ToLower
It's also faster to perform it on the string instead on a Span (probably the cast from a string to a span took that much time)

58ns to 0.03ns is a good improve to me

Flamifly pushed a commit to Flamifly/EasePass that referenced this issue Dec 29, 2024
Replace the ToLower in RemoveDatabasePath to Equals with ignore case. FrozenAssassine#135

Verknüpfte Arbeitselemente: #1
FrozenAssassine added a commit that referenced this issue Dec 29, 2024
Performance Improve for the Password Generation #135
@Flamifly
Copy link
Contributor Author

RemoveDatabasePath can be twice as fast:
image
image

We do not use a List for iterating (to iterate over a List is slower than an Array because the List can have it's Pointers everywhere and an Array have it's fixed block)
We removed the List completly and just use a StringBuilder to build our Paths (The List needs to be allocated and the Items need to be copied from the Array to the List)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants