3 pointsby akopicha month ago1 comment
  • akopicha month ago
    Author here. It started when I felt virtual functions and associated heap allocations are too expensive. Then I've realized std::any doesn't work for move-only types, let alone its SBO storage isn't configurable. Meanwhile microsoft/proxy does SBO only for trivial types. And there we go...

    Woid is an extremely customizable high-performance type-erasure header-only library. It provides containers like std::any, std::function and tools for non-intrusive polymorphism.

    Key features:

    - Performance

    - Value semantics

    - Move-only type support

    - Duck typing

    - Extreme customizability

    In my current benchmarks it outperforms std::any, std::function, inheritance-based polymorphism and some well-known libraries like function2, boost::te and microsoft/proxy.

    I want to make sure the comparison is fair, therefore, your advice on how to better tune the existing libraries is rather welcome.

      struct Square {
          double side;
          double area() const { return side * side; }
      };
      struct Shape : woid::InterfaceBuilder
                 ::Fun<"area", [](const auto& obj) -> double { return obj.area(); } >
                 ::Build {
          auto area() const { return call<"area">(); }
      };
      
      auto a = Shape{Square{1.5}}.area();
    And of course I'm here to answer your questions.
    • hebecker25 days ago
      Looks interesting.

      In my experience the typical implementations of std::function are slow-ish during construction/copying but calling an existing object is okay.

      Have you compared just the performance of calling type erased functions? Is there still a difference due to cache locality?

      • akopich25 days ago
        Yes! https://github.com/akopich/woid/blob/main/bench/plots/FunBen...

        Here I wrap std::less<int> into different wrappers (and of them manage to do SBO) and feed it into std::sort. Unless std::sort does a ton of copies, this is predominantly down to function invocation, not instantiation/lifetime management.

        There Woid is up to about 60% faster than std::function if it leverages the triviality of std::less<int>. The advantage goes to 30% if don't optimize for trivial types. And either of the wrappers considered are much slower than just using the lambda directly (see the Id "wrapper") as the type-erased wrappers inhibit inlining.

        I also do the same bench, but with a "big" less<int>, so that std::function fails the SBO, there the gains go up to 2-2.5x. https://github.com/akopich/woid/blob/main/bench/plots/FunBen...