HNHacker News
TopNewBestAskShowJobs

GeorgeHaldane

47 karma · joined April 14, 2025

C++ developer. Working in the field of computational mathematics.
submissionscomments
GeorgeHaldane··on Show HN: Fast Random Library for C++17
Thank you for noticing! Turns out [0, 1] is an artifact of the old documentation carried back from the time where generic GCC / clang approach used to produce occasional 1's due to some issues in N4958 specification. This is fixed in a new commit.

For floats there are essentially 3 approaches that are selected based on the provided range & PRNG:

  1) Shift + multiply (like `(rng >> 11) * 0x1.0p-53`)
  2) "Low-high" from the paper by J. Doornik
  https://www.doornik.com/research/randomdouble.pdf
  3) Canonical GCC implementation
All of these generate values in [0, 1) range which is now reflected in the docs properly. For most actual cases 1st method is the one selected.
GeorgeHaldane··on Show HN: Fast Random Library for C++17
Thanks, that is indeed the case when list doesn't consist of literals, fixed in the new commit.
GeorgeHaldane··on Show HN: Single-Header Profiler for C++17
General rundown of the logic can be found in this comment on reddit: https://www.reddit.com/r/cpp/comments/1jy6ver/comment/mmze20...

About linear IDs: A call graph in general case is a tree of nodes, each node has a single parent and an arbitrary amount of children. Each node accumulates time spend in the "lower" branches. A neat property of the callgraph relative to a generic tree, is that every node can be associated with a callsite. For example, if a some function f() calls itself 3 recursively, there will be multiple nodes corresponding to it, but in terms of callsite there is still only one. So lets take some simple call graph as an example:

  Callgraph:         f() -> f() -> f() -> g()
                                       -> h()
  Node id:           0      1      2      3,4
Let's say f() has callsite id '0', g() has callsite id '1', h() has callsite id '2'. The callgraph will then consist of N=5 nodes with M=3 different callsites:

  Node id:         { 0   1   2   3   4 }
  Callsite id:     { 0   0   0   1   2 }
We can then encode all "prev."" nodes as a single N vector, and all "next" nodes as a MxN matrix, which has some kind of sentinel value (like -1) in places with no connection. For this example this will result in following:

  Node id:         { 0   1   2   3   4 }
  Prev. id:        { x   0   1   2   2 }
  Next id matrix:  [ 1   2   3   x   x ]
                   [ x   x   4   x   x ]
Every thread has a thread-local callgraph object that keeps track of all this graph traversal, it holds 'current_node_id'. Traversing backwards on the graph is a single array lookup:

  current_node_id = prev_node_ids[current_node_id];
Traversing forwards to an existing callgraph node is a lookup & branch:

  next_node_id = next_node_ids[callsite_id, current_node_id]
  if (next_node_id = x) create_node(next_node_id); // will be usually predicted
  else                  current_node_id = next_node_id;
New nodes can be created pretty cheaply too, but too verbose for a comment. The key to tracking the callsites and assigning them IDs is thread_local local variables generated by the macro:

https://github.com/DmitriBogdanov/UTL/blob/master/include/UT...

When callsite marker initializes (which only happens once), it gets a new ID. Timer then gets this 'callsite_id' an passes it to the forwards-traversal. The way we get function names is by simply remembering __FILE__, __func__, __LINE__ pointers in another array of the call graph, they get saved during the callsite marker initialization too. As far as performance goes everything we do is cheap & simple operations, at this point the main overhead is just from taking the timestamps.

GeorgeHaldane··on Show HN: Single-Header Profiler for C++17
Haven't worked with it, but based on initial look it's a quite different thing that stands closer to a frame-based profiler like Tracy (https://github.com/wolfpld/tracy).

As far as differences go:

Microprofile:

  - frame-based
  - needs a build system
  - memory usage starts at 2 MB per thread
  - runs 2 threads of its own
  - provides system-specific info
  - good support for GPU workloads
  - provides live view
  - seems like a good choice for gamedev / rendering
utl::profiler:

  - no specific pipeline
  - single include
  - memory usage starts at approx. nothing and would likely stay in kilobytes
  - doesn't run any additional threads
  - fully portable, nothing platform specific whatsoever, just standard C++
  - doesn't provide system-specific info, just pure timings
  - seems like a good choice for small projects or embedded (since the only thing it needs is a C++ compiler)
GeorgeHaldane··on Show HN: Single-Header Profiler for C++17
I've though about this but had yet to come up with a simple approach, perhaps something like a python script hooked to GCC-XML can do the trick, will look into that in the future.