Giter Site home page Giter Site logo

Comments (9)

nemequ avatar nemequ commented on July 30, 2024

You could look at GLib's Constructors for an idea of how to do this somewhat portably, but AFAIK there is just to guaranteed way to do this.

I think a better solution would be to just have each group compiled into a separate library which could then be dlopen()ed by uarch-bench. In addition to the ability to just drop a C file into uarch-bench and have it just work, it would also make it easy to load modules from other locations, allowing projects to keep uarch-bench tests in their trees. It would also make it easier to test different compilers since only the module in question would need to be recompiled.

from uarch-bench.

travisdowns avatar travisdowns commented on July 30, 2024

@nemequ - I'm not following the problem here. I've used this pattern many times (note I'm talking about C++ here not C). The portable approach is just to use straightforward C++, isn't it? An object with a constructor that registers the benchmarks (push instead of the current pull approach).

Perhaps I wasn't clear in the description, but I don't anticipate any issue here really, this is just a task so I remember to do the work.

I also like the idea of loading additional benchmarks at runtime from a shared object, but that's really a different, bigger feature (and using C++ in "plugin" interfaces is kind of a mess last time I looked). I added issue #43 to track that.

from uarch-bench.

nemequ avatar nemequ commented on July 30, 2024

Ah right, C++. Sorry, not used to thinking in C++. I don't know about it being a "portable" approach (IIRC there can be some issues if you use them in shared libraries), but yeah it should be safe for what you're talking about here.

from uarch-bench.

travisdowns avatar travisdowns commented on July 30, 2024

@nemequ - yeah there are definitely gotchas in C++, the big ones being that (a) the order between different compilation units isn't defined, so you can easily blow up if your globals refer to each other during initialization and (b) destruction is similarly messy, especially with shared libraries, and it's easy to accidentally access objects have they have been destroyed (the "easy out" here is often just to ensure the global object destructor never run at all: just "leak" them).

What I'm thinking of here is simple though and shouldn't run into those issues.

I didn't think much about C specifically, but yeah now that C benchmarks are supported it would be nice to come up with an approach there. One option would to be to have a build step that looks for .c files and picks out the benchmark registration method and adds it to an autogenerated file that registers all the C benchmarks, or ... dunno.

from uarch-bench.

nemequ avatar nemequ commented on July 30, 2024

I didn't think much about C specifically, but yeah now that C benchmarks are supported it would be nice to come up with an approach there. One option would to be to have a build step that looks for .c files and picks out the benchmark registration method and adds it to an autogenerated file that registers all the C benchmarks, or ... dunno.

AFAICT the registration code currently needs to be C++ anyways, even if the tests themselves are in C, so that doesn't really matter right now.

An API to register stuff in C would be very nice for #43, but if you're dlopen()ing a module you can just run a function too, so you don't need a constructor.

At some point it wouldn't be too difficult to add a macro which would support constructors on Windows, most GCC/clang/icc configurations, and suncc (would actually be a good idea for portable-snippets), plus C++, but if #43 happens I don't see why the test built in to uarch-bench couldn't use the same mechanism, then there is no need to maintain multiple paths.

from uarch-bench.

travisdowns avatar travisdowns commented on July 30, 2024

An API to register stuff in C would be very nice for #43, but if you're dlopen()ing a module you can just run a function too, so you don't need a constructor... but if #43 happens I don't see why the test built in to uarch-bench couldn't use the same mechanism, then there is no need to maintain multiple paths.

Right, but you could only run one function or perhaps a fixed list of functions after dlopen, right? I mean it solves the problem if you put each group of benchmarks into its own shared library, but if you don't do that you still have the same problem within the library of how to have N separate .cpp or .c files defining benchmarks without registering them in a separate spot. If you could enumerate the available functions in a shared object, and perhaps call all that met some pattern it would also solve this "inner" independence problem, but AFAIK there's not portable way to do that.

Just want to check that we're on the same page here. Of course, having different modules already makes the problem a lot better since you have at least one way to decouple things.

from uarch-bench.

nemequ avatar nemequ commented on July 30, 2024

I've been assuming one group per shared library, but if you'd prefer it would be fairly easy to do something like

typedef struct UarchCtx_ UarchCtx;
typedef struct UarchGroup_ UarchGroup;

void uarch_ctx_add_group(UarchCtx* ctx, UarchGroup* group);
UarchGroup* uarch_group_new(UarchCtx* ctx, const char* id, const char* description);
void uarch_group_add_bench(UarchGroup* group, const char* id, const char* description, long(* func)(uint64_t), int ops_per_iter);

I don't think it's too much to ask the modules to have a registration function which knows about all functions within that module.

If you dlopen() a module odds are decent a constructor won't be run automatically anyways, so the only thing I can really think of would be some build system magic to automatically generate a registration function using the file names to generate symbols (e.g., foo-bar.c would trigger a foo_bar_register() call to be generated).

Besides, I think the idea of just dropping a C/C++ file into a directory and having uarch-bench pick it up automatically is more attractive if you're modifying uarch-bench (as you currently have to) than if you're building something in your own source tree, especially when you consider that any build system magic would need to be rewritten, or at least customized, for your project.

from uarch-bench.

travisdowns avatar travisdowns commented on July 30, 2024

If you dlopen() a module odds are decent a constructor won't be run automatically anyways

Really, why? That would basically break any code that relies on this standard and widespread feature, which is probably most C++ code. It seems unlikely to me that this is a problem on almost any modern platform.

In any case, I really see the two things as orthogonal: there should be a way to load benchmark shared modules at runtime (or perhaps a way to embed uarch bench as a component inside your own application/module) and there should be a way to make the registration of benchmarks from C++ and C within uarch-bench or a module a bit more decoupled from having a master list.

I think each feature can more or less live or die on its own. I agree with you that for the modules approach it makes the most sense to call one known function after dlopen which registers all the benchmarks in a module. If there happens to be a constructor-based way to generate this list from separate .cpp files then it could be used to generate the list that this method returns, or it could be just an explicit list like we have today in main.cpp.

I don't think it's too much to ask the modules to have a registration function which knows about all functions within that module.

Yeah, that's fair.

from uarch-bench.

nemequ avatar nemequ commented on July 30, 2024

Really, why? That would basically break any code that relies on this standard and widespread feature, which is probably most C++ code. It seems unlikely to me that this is a problem on almost any modern platform.

You're probably right for this project. AFAIK ELF and PE both support constructors in the runtime linker. My understanding is that there are still some platforms where it isn't supported, but you're not likely to run into them on x86 anyways.

In any case, I really see the two things as orthogonal: there should be a way to load benchmark shared modules at runtime (or perhaps a way to embed uarch bench as a component inside your own application/module) and there should be a way to make the registration of benchmarks from C++ and C within uarch-bench or a module a bit more decoupled from having a master list.

I would think you would want a single code path since it's easier to maintain, but if you're comfortable with two I don't mind.

That said, I believe there is still an issue with static libraries you should watch out for. Last time I checked, the linker would skip any static libraries for which you don't actually use any symbols, and a constructor doesn't count as usage (https://ofekshilon.com/2013/04/06/forcing-construction-of-global-objects-in-static-libraries/ comes up from a quick search). So, if you're thinking about creating static libraries for each module to keep code duplication minimal while still embedding the built-in tests that may cause problems.

I think each feature can more or less live or die on its own. I agree with you that for the modules approach it makes the most sense to call one known function after dlopen which registers all the benchmarks in a module. If there happens to be a constructor-based way to generate this list from separate .cpp files then it could be used to generate the list that this method returns, or it could be just an explicit list like we have today in main.cpp.

I don't see any reason to complicated it by using a global constructor, but I guess that would be up to each module.

from uarch-bench.

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.