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

Improve performance when using large amount policy #39

Merged
merged 1 commit into from Jun 15, 2020

Conversation

sagilio
Copy link
Member

@sagilio sagilio commented Jun 14, 2020

close #15

I created a benchmark project and here is a partial result.

BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19645
Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=5.0.100-preview.5.20279.10
  [Host]     : .NET Core 3.1.2 (CoreCLR 4.700.20.6602, CoreFX 4.700.20.6702), X64 RyuJIT
  Job-FLOMDT : .NET Core 3.1.2 (CoreCLR 4.700.20.6602, CoreFX 4.700.20.6702), X64 RyuJIT

IterationCount=3  RunStrategy=Throughput  

Before

Method Times PolicyCount Mean Error StdDev Min Max Median
HasPolicy 1000 500 10,247.42 μs 6,711.802 μs 367.897 μs 9,962.09 μs 10,662.64 μs 10,117.51 μs
AddPolicy 1000 500 21,597.31 μs 16,262.479 μs 891.401 μs 21,026.57 μs 22,624.49 μs 21,140.86 μs
HasPolicy 1000 5000 112,204.21 μs 41,008.798 μs 2,247.831 μs 109,609.56 μs 113,561.52 μs 113,441.54 μs
AddPolicy 1000 5000 133,846.18 μs 634,420.055 μs 34,774.704 μs 109,086.42 μs 173,602.98 μs 118,849.14 μs

Now

Method Times PolicyCount Mean Error StdDev Min Max Median
HasPolicy 1000 500 601.000 μs 102.551 μs 5.6212 μs 596.873 μs 607.402 μs 598.726 μs
AddPolicy 1000 500 632.119 μs 298.945 μs 16.3862 μs 615.599 μs 648.368 μs 632.391 μs
HasPolicy 1000 5000 645.874 μs 419.625 μs 23.0011 μs 621.002 μs 666.378 μs 650.243 μs
AddPolicy 1000 5000 627.886 μs 578.806 μs 31.7263 μs 605.511 μs 664.195 μs 613.952 μs

@hsluoyz
Copy link
Member

hsluoyz commented Jun 14, 2020

@arthuridea can you see if this PR solves your issue?

@hsluoyz
Copy link
Member

hsluoyz commented Jun 14, 2020

@huazhikui @nodece @xcaptain please review.

@arthuridea
Copy link

@arthuridea can you see if this PR solves your issue?

I wrote a simple dotnet core api project just now ,loading 9000 policies.The adapter was injected as Scoped service. It costs 2.05s in Waiting(TFFB) under version 1.2.7,while it successfully reduces to 72ms without any code changes. I didn't make any further testing,maybe this version works!Thanks very much for all of you.

Copy link

@xcaptain xcaptain left a comment

Choose a reason for hiding this comment

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

The HashSet<string> may cause some problems.

set => _policy = value;
get => _policy;
Policy = new List<List<string>>();
PolicyStringSet = new HashSet<string>();

Choose a reason for hiding this comment

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

The PolicyStringSet just used to store rule? what if a rule contains same string, like

p, alice, alice, read

Copy link
Member Author

@sagilio sagilio Jun 15, 2020

Choose a reason for hiding this comment

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

It stores the string which connected by rule List<string>, I think it will keep the same operation as before.
this is operation before :

for (var i = 0; i < a.Count; i++)
{
    if (!a[i].Equals(b[i]))
    {
        return false;
    }
}
return true;

If a rule contains the same string and the HasPolicy will can not return false at before too. Whether the HashSet<string> is safe enough need the rule parsing is complete.

Choose a reason for hiding this comment

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

Ok, understand, you turn an array into a string, so no need to worry about this.

Copy link
Member

Choose a reason for hiding this comment

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

Why we don't use HashSet<List<string>? somethings like:

 class ListPolicy<T> : List<T>
    {
        public override bool Equals(object obj)
        {
           // I'm not sure how to write.
            return obj is ListPolicy<T> policy &&
                   Capacity == policy.Capacity &&
                   Count == policy.Count;
        }

        public override int GetHashCode()
        {
            var hash = new HashCode();
            foreach (var item in this)
            {
                hash.Add(item);
            }
            return hash.ToHashCode();
        }
    }



    class MainClass
    {
        public static void Main(string[] args)
        {

            var policy = new HashSet<ListPolicy<string>>
            {
                new ListPolicy<string> { "user","data1","read" },
                new ListPolicy<string> { "user","data1","read" }
            };

            Console.WriteLine(policy.Count); // => 1
        }
    }

Copy link
Member Author

Choose a reason for hiding this comment

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

Why we don't use HashSet<List<string>? somethings like:

 class ListPolicy<T> : List<T>
    {
        public override bool Equals(object obj)
        {
           // I'm not sure how to write.
            return obj is ListPolicy<T> policy &&
                   Capacity == policy.Capacity &&
                   Count == policy.Count;
        }

        public override int GetHashCode()
        {
            var hash = new HashCode();
            foreach (var item in this)
            {
                hash.Add(item);
            }
            return hash.ToHashCode();
        }
    }



    class MainClass
    {
        public static void Main(string[] args)
        {

            var policy = new HashSet<ListPolicy<string>>
            {
                new ListPolicy<string> { "user","data1","read" },
                new ListPolicy<string> { "user","data1","read" }
            };

            Console.WriteLine(policy.Count); // => 1
        }
    }

I mentioned it in the next review. here is the content:

If changed List<List<string>> to Hash<Rule> (the Rule inherit List<string> and overwrite the Equals and GetHashCode), It will change almost all the public API and can not gain more performance. It will cost much time when Add the list because the Equal or Add has O(n) complexity.

Additionally, The HashSet can not get by index and will shuffle the list (But we can add the index property to handle it).

Copy link
Member Author

Choose a reason for hiding this comment

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

@nodece @xcaptain Here is the benchmark result of using HashSet<Rule>, the code is here change-to-hashset branch :

BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19645
Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=5.0.100-preview.5.20279.10
  [Host]     : .NET Core 3.1.2 (CoreCLR 4.700.20.6602, CoreFX 4.700.20.6702), X64 RyuJIT
  Job-WRAGMF : .NET Core 3.1.2 (CoreCLR 4.700.20.6602, CoreFX 4.700.20.6702), X64 RyuJIT

IterationCount=3  RunStrategy=Throughput  
Method Times PolicyCount Mean Error StdDev Min Max Median
HasPolicy 1000 500 511.904 μs 331.9158 μs 18.1934 μs 492.169 μs 528.009 μs 515.534 μs
AddPolicy 1000 500 10,286.494 μs 1,392.4007 μs 76.3222 μs 10,239.692 μs 10,374.566 μs 10,245.225 μs
HasPolicy 1000 5000 523.930 μs 411.9189 μs 22.5787 μs 498.292 μs 540.851 μs 532.646 μs
AddPolicy 1000 5000 10,284.017 μs 5,746.3454 μs 314.9766 μs 9,943.827 μs 10,565.527 μs 10,342.697 μs

return false;

Assertion assertion = Model[sec][ptype];
assertion.Policy.Add(rule);

Choose a reason for hiding this comment

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

Can we merge these 2 steps into 1? We can compare 2 List, why change to string

Copy link
Member Author

Choose a reason for hiding this comment

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

If changed List<List<string>> to Hash<Rule> (the Rule inherit List<string> and overwrite the Equals and GetHashCode), It will change almost all the public API and can not gain more performance. It will cost much time when Add the list because the Equal or Add has O(n) complexity.

Copy link

@xcaptain xcaptain Jun 15, 2020

Choose a reason for hiding this comment

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

If we have a lot of policies in memory, will this increase memory usage because of storing twice.

Copy link
Member Author

Choose a reason for hiding this comment

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

You are right, But it will not much increase memory and to file adapter it can be like a cache.
We can try to make it better, I think we can convert it to an array when using it.

Copy link
Member

Choose a reason for hiding this comment

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

the assertion.PolicyStringSet proptry does not need to be public.

  1. add assertion.AddPolicy(rule) method in Assertion Class replace these 2 steps:
assertion.Policy.Add(rule);
assertion.PolicyStringSet.Add(Utility.ArrayToString(rule));
  1. add assertion.Contains(rule) mehod in Assertion Class replace Model[sec][ptype].PolicyStringSet.Contains(Utility.ArrayToString(rule))

  2. add assertion.RemovePolicyAt(i) method replace these 2 steps:

assertion.Policy.RemoveAt(i);
assertion.PolicyStringSet.Remove(Utility.ArrayToString(rule));

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok, The assertion.PolicyStringSet is Internal now, I will add them at next commit.

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

Successfully merging this pull request may close these issues.

Helper.LoadPolicyLine causes poor performance
6 participants