C++ London Meetup: Type Punning and Counting Nanoseconds

September’s C++ London Meetup featured two talks: Type Punning by Timur Doumler and Counting Nanoseconds by David Gross of Optiver. Optiver were the generous sponsors of this event, hosted at SkillsMatter near Moorgate.

Counting Nanoseconds

Optiver are based in Amsterdam and operate in the High-Frequency Trading sphere. Naturally, they need code to be fast in order to take trades more quickly than their competitors, but the benefits of fast code also extend to minimising energy costs in data centres and extending battery life on portable devices for other applications. David Gross spoke about micro-benchmarking – boiling code down to very small numbers of instructions to aid comparisons and improve performance. The caveat was that it’s always vital to measure performance in production using real data as well as doing this offline, low-level analysis against potted test cases. David warning against relying on the high-resolution clock which can be problematic (may not be steady and may not have the resolution you need). He often uses Intel Time Stamp Counter (TSC), although that’s not the original intention of that metric.

Type Punning

Timur is fascinated by the ability to take an object and treat it as another type in C++. Yet, in many cases, this results in undefined behaviour. For example:

class Gizmo {
  public:
  virtual void print(){ std::cout << "Gizmo\n"; }
};
class Widget {
  public:
  virtual void print(){ std::cout << "Widget\n"; }
};
Gizmo g;
Widget* w = (Widget*)(&g);
w->print();

The behaviour of this code is undefined, because Widget and Gizmo are unrelated, although both have a virtual print function, so the virtual function table is the same. The standard says that you can’t call a method on a reinterpret-cast’d object – you can only cast it back to the actual type of the object. But on most compilers tried by Timur, the code runs and prints “Gizmo”. He referenced the CppCon 2018 talk, Undefined Behaviour is Not an Error.

Other examples concerned how to share memory across multiple data types, such as treating 4 bytes as either a char array or as a float. Even using a union can result in undefined behaviour, because only one member is ‘alive’ at a time. In order to write portable code, you must consider aliasing rules, object lifetime rules and alignment rules. In same cases, you can use C++17’s std::memcpy to ensure alignment (but only if the types are the same size and trivially copyable). std::bit_cast is coming with C++20. And in C++23, there’s a proposal for std::start_lifetime_as(char[]) to implicitly create objects, for example when reading from a buffer.

The video for this talk is available on SkillsCast.

Leave a comment

Filed under C++, C++ Code, Meetup, Programming

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.