Giter Site home page Giter Site logo

Performance about guard HOT 5 CLOSED

safakgur avatar safakgur commented on May 10, 2024
Performance

from guard.

Comments (5)

safakgur avatar safakgur commented on May 10, 2024 2

I'm glad you liked the library!

You have a point; compiling expressions is expensive. Although I hinted in the readme that it is not the most efficient way to initialize an argument and gave some more details in the design decisions document, I see how using a member expression in the first example would leave a negative first impression about performance.

The use of member expressions is convenient unless used in hot paths. Yes, it would be heavy on the GC for server applications where we're validating thousands of arguments per second, but I thought (wrongly) that most applications wouldn't have that volume. So, I promoted this approach in the first example and tried to make it easy for people who don't want this overhead to find about the alternative method.

I now think that we should be emphasizing the faster approach as the default method and provide this one as a more convenient, albeit slower, alternative. I’ll be updating the readme accordingly.

Thank you for your insight.

Edit: I've just updated the readme and design docs.

from guard.

VictorBlomberg avatar VictorBlomberg commented on May 10, 2024 1

First of, this looks like a really nice library to use!

Regarding performance though: The real issue is the ArgumentInfo<T> Guard.Argument<T>(Expression<Func<T>>, bool) method, as shown in test case Test04GuardAssertArgumentDocs_x0_001. It compiles an Expression on every call. It makes a nice API, but at a very high cost. As long as that's a central part of the library it's not really high-performance. Compiling Expressions is really expensive.

from guard.

safakgur avatar safakgur commented on May 10, 2024 1

As long as you educate your users about the implications of the different approaches...

I agree. I also detailed performance considerations regarding member guards in the design docs since they too required creating expression trees. This issue made me realize that I should be warning Guard users about these costs if I am to label this library as high-performance.

I am impressed by your way of handling our criticism!

This is what OSS communities are for, right? :) I want this library as good as it can be, so I appreciate every comment, suggestion and criticism.

I'm closing this since it represents a subject that is too broad. I encourage anyone to open new issues for their specific problems/suggestions. Thank you both!

from guard.

safakgur avatar safakgur commented on May 10, 2024

Hi, thank you for the benchmark.

But I don't think it is fair to compare a fluent approach to a non-fluent one. Guard can never be as fast as a non-fluent approach. Because in order to get the usability provided by a fluent syntax, you'll have to allocate an object (albeit in stack, in our case) that stores the value to validate, its name, and maybe some other validation options. This is a trade-off between the usability and performance. I've touched upon the dilemma in the design decisions.

void Test(string s)
{
    // Vanilla - The only thing faster is not to validate.
    if (s == null)
        throw new ArgumentNullException(nameof(s), "S cannot be null.");

    if (s.Length == 0)
        throw new ArgumentException("S cannot be empty.", nameof(s));

    if (!s.Contains(':'))
        throw new ArgumentException("S must contain a colon.", nameof(s));

    // A hypothetical helper that accepts pre-evaluated conditions, closest to vanilla.
    Foo.NotNull(s, nameof(s));
    Foo.Require(s.Length != 0, nameof(s), "S cannot be empty");
    Foo.Require(s.Contains(':'), nameof(s), "S must contain a colon.");

    // A hypothetical non-fluent approach, won't be as fast as the above two.
    Bar.NotNull(s, nameof(s));
    Bar.NotEmpty(s, nameof(s));
    Bar.Contains(s, nameof(s), ':');

    // A fluent approach, cannot be as fast as a non-fluent one.
    Guard.Argument(s, nameof(s)).NotNull().NotEmpty().Contains(':');
}

The goal of this project is to provide an argument validation library that wouldn't require you to write all that boilerplate without being a burden for the GC. Performance considerations made for Guard are mostly focused on avoiding heap allocations. You can initialize a guarded argument (using Guard.Argument(p, nameof(p)) instead of Guard.Argument(() => p)) and chain (most) validations without making a single heap allocation.

Guard also makes extensive use of caching. An example would be the Contains validation for enumerables. When you call Guard.Argument(() => list).Contains(1) for the first time, the Contains validation checks if the argument type itself has a Contains method accepting an Int32. If it does, a lambda expression calling that is compiled; if it does not, a check that enumerates the collection is created. In either case, all the subsequent Contains validations for the same collection type use the cached delegate.

I appreciate the tests - performance is one of my top priorities for Guard and I'm always looking into more ways to improve it. It is not optimized for closer-to-metal programming, though. So if the nanoseconds between vanilla checks and Guard validations matter for a particular application, it probably shouldn't contain any runtime assertions at all.

from guard.

VictorBlomberg avatar VictorBlomberg commented on May 10, 2024

The updated readme and design doc is a great improvement.

I've inherited code with indiscriminate use of assertion methods using member expressions this way, and seen the real world cost of it. Therefore I think this is the right call, even though I fully understand your reasoning regarding the convenience of the expression variant. As long as you educate your users about the implications of the different approaches, exposing the member expression API is not really an issue.

I am impressed by your way of handling our criticism!

from guard.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.