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

feat: Basic model speedup #245

Merged
merged 12 commits into from
Mar 18, 2024
Merged

feat: Basic model speedup #245

merged 12 commits into from
Mar 18, 2024

Conversation

AlexeyChashchegorov
Copy link
Contributor

@AlexeyChashchegorov AlexeyChashchegorov commented Mar 5, 2024

Description

Brief: I have production case that uses big amount of simple policies (~100K) that need to have enforcement time < 50ms.
This use case have low performance at casbin-cpp. Casbin-cpp cashing not comes the solution - on uniform distribution of request. Cash filling is quite long.

I have implemented unordered_set storage for policies for "basic model with allow only" to be used on enforcement.

--==Solution have performance gain X185 times. ==--

This gain will not depend on policies count anymore - O(1) complexity of traverse on enforcement:

Before the modification:

RUNNING BENCHMARK
Benchmark                                                     Time                   CPU                           Iterations
BenchmarkBasicModelLargeSize                29929285 ns     29822588 ns           85

After the modification:

Benchmark                                                     Time                   CPU                           Iterations
BenchmarkBasicModelLargeSize                 161366 ns         161240 ns                  4454

Workaround:
Traversing of policies on enforcement at casbin-cpp implemented for vector. That quite long on using not first (but last ) element of the vector. It nice to reflect this on benchmark.

Solution:

  1. Instead of vectors use unordered_set to store the policies. It makes searching for specific policy complexity to be O(1) instead of O(n).
  2. Improve only "basic model with allow only" case
  3. Filter of policies ( stored in unordered_set ) if "basic model with allow only" used to get just one element for enforcement
  4. Put filtered collection to main enforce algorithm
  5. Use option of cmake to have storage of policies as vector or unordered_set ( to reduce memory footprint on specific use cases )

Provements:

  1. please come at build/ directory and run build_actions.sh
#!/usr/bin/env bash
echo INITIAL CLEANING
git checkout master
cd build
cmake ../
cmake --build . --target clean > /dev/null 2>&1
echo COMPILATION
cmake --build . -j 8 > /dev/null 2>&1
echo RUNNING BENCHMARK
./tests/benchmarks/casbin_benchmark 2> /dev/null | grep Benchmark

echo SECONDARY CLEANING
git checkout AlexeyChashchegorov:master
cmake ../
cmake --build . --target clean > /dev/null 2>&1
echo COMPILATION
cmake --build . -j 8 > /dev/null 2>&1
echo RUNNING BENCHMARK
./tests/benchmarks/casbin_benchmark 2> /dev/null | grep Benchmark

Results of running:

INITIAL CLEANING
SET BUILD ENV WITH BUILD_HASHED=OFF
COMPILATION
RUNNING BENCHMARK
Benchmark                                          Time             CPU   Iterations
BenchmarkRaw                                     165 ns          165 ns      4248888
BenchmarkBasicModel                           134863 ns       134836 ns         5390
BenchmarkBasicModelLargeSize                29929285 ns     29822588 ns           85
BenchmarkRBACModel                            205365 ns       204697 ns         4116
BenchmarkRBACModelSizesSmall                 2527201 ns      2526538 ns          649
BenchmarkRBACModelSmall                      4573460 ns      4571217 ns          276
BenchmarkRBACModelWithResourceRoles           148069 ns       148069 ns         4886
BenchmarkRBACModelWithDomains                 185278 ns       185208 ns         3848
BenchmarkKeyMatchModel                        162665 ns       162610 ns         4428
BenchmarkRBACModelWithDeny                    288470 ns       288407 ns         3060
BenchmarkPriorityModel                        145488 ns       145451 ns         4961
BenchmarkCachedRaw                               165 ns          165 ns      4232088
BenchmarkCachedBasicModel                        737 ns          721 ns      1004650
BenchmarkCachedRBACModel                         710 ns          703 ns       980626
BenchmarkCachedRBACModelSmall                    616 ns          615 ns      1101599
BenchmarkCachedRBACModelWithResourceRoles        692 ns          692 ns       995138
BenchmarkCachedRBACModelWithDomains              685 ns          679 ns      1021048
BenchmarkCachedKeyMatchModel                     628 ns          628 ns      1105758
BenchmarkCachedRBACModelWithDeny                 690 ns          690 ns       997137
BenchmarkCachedPriorityModel                     685 ns          685 ns      1013729
BenchmarkVectorOperations                        493 ns          493 ns      1425676
BenchmarkHasPolicySmall                        88897 ns        88870 ns         8001
BenchmarkAddPolicySmall                       452076 ns       451889 ns         2019
BenchmarkRemovePolicySmall                      1580 ns         1578 ns       442165
BenchmarkRoleManagerSmall                      96288 ns        96244 ns         7281
SECONDARY CLEANING
SET BUILD ENV WITH BUILD_HASHED=ON
COMPILATION
RUNNING BENCHMARK
Benchmark                                          Time             CPU   Iterations
BenchmarkRaw                                     200 ns          200 ns      3460704
BenchmarkBasicModel                           161802 ns       161720 ns         4865
BenchmarkBasicModelLargeSize                  161366 ns       161240 ns         4454
BenchmarkRBACModel                            173449 ns       173367 ns         4506
BenchmarkRBACModelSizesSmall                 2767169 ns      2765977 ns          650
BenchmarkRBACModelSmall                      4565711 ns      4564597 ns          278
BenchmarkRBACModelWithResourceRoles           212746 ns       212691 ns         3864
BenchmarkRBACModelWithDomains                 282814 ns       282422 ns         2806
BenchmarkKeyMatchModel                        304304 ns       304051 ns         2785
BenchmarkRBACModelWithDeny                    288726 ns       288308 ns         3057
BenchmarkPriorityModel                        291688 ns       291589 ns         3004
BenchmarkCachedRaw                               196 ns          196 ns      3597289
BenchmarkCachedBasicModel                        687 ns          687 ns       994912
BenchmarkCachedRBACModel                         691 ns          690 ns       996895
BenchmarkCachedRBACModelSmall                    636 ns          636 ns      1065336
BenchmarkCachedRBACModelWithResourceRoles        689 ns          688 ns      1007484
BenchmarkCachedRBACModelWithDomains              690 ns          690 ns      1004895
BenchmarkCachedKeyMatchModel                     636 ns          636 ns      1084700
BenchmarkCachedRBACModelWithDeny                 685 ns          685 ns      1012922
BenchmarkCachedPriorityModel                     687 ns          686 ns      1012717
BenchmarkVectorOperations                        493 ns          493 ns      1417093
BenchmarkHasPolicySmall                        93203 ns        90066 ns         7917
BenchmarkAddPolicySmall                       415859 ns       415684 ns         2125
BenchmarkRemovePolicySmall                      1633 ns         1632 ns       429664
BenchmarkRoleManagerSmall                      96992 ns        96944 ns         7241

Screenshots/Testimonials

Actions on the PR:

  • using of unordered_set at assertion_map to prototype the fast usage
  • implement compilation flag to select vector/unordered_set as storage type of policies at assertion_map
  • implement basic_model detector: to know when unordered_set can be used
  • to fix test failure on "priority model" - addition of abstract collection "PoliciesValues" that hides behavior of vector/unordered_set and unify the usage. "Priority model" only uses vector as policies storage and it can not been changed any possible way.
  • construction of collection depends on model: basic model -> PoliciesValues will be based on unordered_set
  • filtering of model policies at assertion map depends on PoliciesValues internal collection type: if "is_hash()" of the collection returns true - find at collection and hold just value that match.
  • moving out of the compilation flag ( automated selection of collection makes it possible )

@casbin-bot
Copy link
Member

@EmperorYP7 @sheny1xuan @cs1137195420 please review

@CLAassistant
Copy link

CLAassistant commented Mar 5, 2024

CLA assistant check
All committers have signed the CLA.

@hsluoyz
Copy link
Member

hsluoyz commented Mar 6, 2024

@AlexeyChashchegorov fix CI:

image

@AlexeyChashchegorov
Copy link
Contributor Author

I will figure out why it fails. My local build on osx passed with no problems.

@AlexeyChashchegorov
Copy link
Contributor Author

AlexeyChashchegorov commented Mar 13, 2024

PR description updated.
Code has significant update:

  • abstract collection PoliciesValues to make vector / unordered_set be threat uniform way.
  • all tests passed
  • Priority model is only blocker to do not use unordered_set directly at each of the models. Priority model relies on order user defined - it must be hold as vector
  • compilation option of cmake removed
  • automatic detection of unordered_set usage as container for policies at assertion_map implemented
  • filtering of policies collection at "enforcer.cpp" slightly polished for uniform usage of the PoliciesValues abstract collection

@AlexeyChashchegorov
Copy link
Contributor Author

several comments:

  • it is possible to use unordered_set for grouping policies as well ( speedup will be the same and not dependent of policies count)
  • it is possible to use deny effects for basic and rbac models as well with minimal limitations
  • I can and would like to make this changes as well but not in this PR.

@hsluoyz
Copy link
Member

hsluoyz commented Mar 14, 2024

@AlexeyChashchegorov plz fix CI errors:

image

@AlexeyChashchegorov
Copy link
Contributor Author

AlexeyChashchegorov commented Mar 15, 2024

I have just 20.04.6 and g++9.4 (not g++9.3 that used on CI).

But it pretty same to fix most of the bugs of build on Linux and to have unittests passed.

I have some problems on build of py bindings. But I believe it may be compiler specific problem.
Let's look.

@hsluoyz
Copy link
Member

hsluoyz commented Mar 15, 2024

@AlexeyChashchegorov plz fix:

image

@AlexeyChashchegorov
Copy link
Contributor Author

I will look at CI. Some time needed to get Win host available to compile.

@hsluoyz If you can direct me how to get proper Win host / pod with appropriate compiler to debug - I will be very appreciative. ( On my side it might consume more time )

@AlexeyChashchegorov
Copy link
Contributor Author

It should works for Windows as well now.

@hsluoyz hsluoyz merged commit cbeed65 into casbin:master Mar 18, 2024
14 checks passed
Copy link

🎉 This PR is included in version 1.55.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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

Successfully merging this pull request may close these issues.

None yet

4 participants