Yesterday I attended C++ Under The Sea, a conference here in the Netherlands - the country of negative elevation, windmills and kruidnoten. It was a short but great conference, featuring various national and international speakers.
In this blog post, I will share some highlights from the talks I attended and my experience at the conference as a volunteer.
Talks
Low-Overhead Logging for Low-latency Applications - Martijn Tersptra:
- Actually, we should master the art of not logging. Log levels are not a great solution, there can be a tendency to end up putting lots of logs in one level, and then when we enable that level, we also get a bunch of unnecessary junk that are unrelated to what we are interested in.
- An alternative or complementary solution to log levels is to use a path-based approach. Each logging statement can
record what file and line number it appeared in code, and we can at compile time create boolean flags for all log
statements whether they should be enabled or not by evaluating whether the log statement is within a specified set of
path ranges (e.g.
["/path/to/mycomponent/*", "/path/to/file.cpp:3200-3299"]
). - We can set these flags at the time of initializing the program, so that by the time we encounter a log statement whose flag is false, it takes only 2 machine instructions to check it and jump over the logging code.
- It would be also possible to do this check at compile time using
constexpr if
but then it can take longer to reconfigure what we want to see. - There was also a discussion about what to do with making the actual logging fast, which involved a large-enough ring buffer which was read and flushed to a file by a separate thread. An idea was also to only send the log message’s payload through the ring buffer, so that the actual formatting doesn’t have to be performed in the hot path but by the other thread.
- Koen Poppe also gave a talk earlier at Meeting C++ about a similar topic, check it out: Minimal Logging Framework - Koen Poppe - Meeting C++ 2024
Exploring Boost.Geometry: A Library Based on Concepts, Traits and Tag Dispatching - Barend Gehrels
This talk introduced a shockingly generic geometry library. It is storage agnostic (works with any vector/point type), coordinate system agnostic (e.g. cartesian/geographic), dimension agnostic, and topological dimension agnostic. The library celebrated its 30th birthday, and it evolved and changed a lot during its lifetime.
I think this could be a great example for testing Hylo’s generic programming and specialization features.
Cache-friendly data + functional + ranges = ❤️ - Björn Fahller
He is using struct of vectors storage mechanism to store different properties of the same kind of entities. Historically, a struct of vectors instead of a vector containing structs was quite cumbersome to work with, but it has great benefits for cache locality when we are not interested in all properties of the entity at once.
He wrote a library called Columnist that allows us to use this table-like data structure with maximum efficiency using ranges and views.
I am also researching cache-friendly and memory-efficient data structures for Hylo’s compiler, so I really enjoyed this talk.
Arrays, Fusion, CPU vs GPU - Conor Hoekstra
He compared algorithms and their composition in 15 different languages / technologies across CPU and GPU, analyzing convenience and the performance metrics on different dataset sizes.
At some point, he casually mentioned that the results of the average of 50 different computational problems, and he wrote a DSL for generating the proper code for all these technologies, which is quite impressive.
- C++ 17 algorithms are not composable efficiently because the free functions that work on iterators
- require allocation for intermediate results
- require multiple passes over the collection
- they are cumbersome to compose, and overly verbose
- A technology should be smart enough to fuse operations together into a single pass, e.g.
list.map(a->a%3).map(a -> a + 1)
should be equivalent tolist.map(a -> a % 3 + 1)
. Fusion-based technologies such as C++ ranges, and Swift lazy iterators seemed generally superior over doing something in multiple passes, but I’m actually unsure if this applies to much ‘deeper’ problems. Anyways, if it’s usually better to do the fusion, I would switch Swift’s defaults regarding the laziness of algorithms: I would have every algorithm lazy by default and have a.collect()
operation, or the past tense of the algorithm’s verb likelist.mapped(a -> a + 1)
- Swift seemed to be not as performant as C++ and Rust (sometimes 10-20% slower than C++ ranges). I didn’t see any particular reason for this, but it’s a shame because Swift advertises itself as a systems programming language.
- Flux was particularly praised for its high performance, with the correct naming of its algorithms also highlighted (e.g. its use of the term
map
, unlike C++ standard algorithms, which usetransform
. The fact that GPU-based Thrust only outperformed CPU-based Flux with datasets around 1 million elements is a significant testament to Flux’s efficiency. Additionally, Flux’s use of a cursor-based model instead of iterators provides a much safer API. For further insights, watch Tristan Brindle’s talk, A Safer Iteration Model for C++ - C++ on Sea 2023. - The c++ lambda syntax is very verbose, so it can be useful to have functions performing the partial application for us:
list.filter([b](auto a) { return a == b; }))
can be replaced withlist.filter(_eq(b))
. He wrote a nice library called Blackbird for this. - Swift and Rust doesn’t suffer from this too much because they have a more concise lambda syntax (
a.filter{ $0 == b }
,a.filter(|a| a == b)
). - See the slides at LINK
The C++ Execution Model - Bryce Adelstein Lelbach
- Defines the Happens Before and Synchronizes With relationships by going through simplified snippets from the standard.
- happens before != happened before != happening before
- My conclusions:
- Programming is hard.
- One shouldn’t trust their intuition when it comes to multithreading and reasoning about what happens before what before they watch this talk.
- I will have to rewatch this talk probably 2 more times to understand everything, but it was a very informative and well-structured presentation.
Introduction to C++ Reflection - Inbal Levi
- Introduces the history of static reflection ideas and the state of the Reflection proposals in C++.
- Static reflection is a way to query data about the program at compile time, and possibly synthesize new code based on that data.
- Hopefully, we will have static reflection in C++26. More library and language features can be added later, once people start using it and provide feedback.
- Use cases include synthesis of serialization code, enum to string conversion, generating visitors for efficient pattern matching, and it eliminates the need for many template meta-programming techniques and macros.
- Now for querying data about the program, the best approach seems to be a type-erased meta-object-based representation. We can get a
meta_info
object by reflecting on a code entity byconstexpr meta_info nyInfo = ^MyCodeEntity
, then we can query its members, function parameter names, and lots of other information that is part of the AST. See details here: P2996 - We can synthesize meta-types ourselves: P2996. This seems very promising for generating those visitors that I’ve been chasing for pattern matching.
- A meta type can be ‘converted back’ to a regular type, in a way similar to this:
constexpr meta_info myMetaInfo = get_member(^ParentType, "ChildStruct");
[:myMetaInfo:] myVar{2}; // Equivalent to ChildStruct myVar{2}
- A
meta_info
object is basically just a reference/handle to the representation of the object in the AST, so it has pointer semantics. This means we can declare a function, capture ameta_info myInfo
from it, then define the function, and then is_defined_function(myInfo) will return true.
My Experience
I arrived at the hotel the day before the conference. I only realized at 5pm that the hotel check-in closes at 7pm, and I live 2 hours away from Breda, so I packed my backpack in 3 minutes and biked to the train station, just to get informed that the train is not welcoming bikes until 7pm. I biked back home to drop my bike, then run to the station again. By the time I got to Breda centre, I had a list of essential things I left at home, so I planned a running route planned, hitting a store along the way. I love running, it is a viable competitor to public transport for under 5km in the Netherlands. Biking is even better, usually 1.5-2x faster than public transport. I arrived at the hotel at 6:45pm, in perfect timing. Tip: when you are running with two laptops and a full backpack, tighten the straps as much as you can, so the backpack doesn’t bounce around - this makes it much more comfortable.
Next day, I arrived to the venue at the venue at 7:45am, which was a beautiful 15-minute walk from the hotel in the sunrise.
I was a volunteer at the conference, which gave a unique experience interacting with many attendees and our great organizers. We helped at the registration desk with Floris Bob, after which we attended the talks and gave heads-up to the speakers whenever they had only 5-10 minutes left and when they were running out of time. There were a lot of things going wrong behind the scenes such as a room being too light and the presentation was illegible, or too dark and people were falling asleep. Nonetheless, we tried to react to issues as quickly as possible, and I think we also collected a great amount of lessons learned for next year.
I really enjoyed the talks, all of them got me thinking and opened my eyes to new possibilities, techniques and technologies. I also met and talked with many people, some of them I knew from my previous C++ On Sea, but I felt the 1 day to be too short to have a proper conversation with everyone I wanted to talk to. The conference sold out fully, and it was a great success, so I hope next year it will be 2-3 days.
I also had the opportunity to meet Conor and Bryce, the hosts of ADSP: The Podcast which I’ve been listening since I started university (2 years ago), and who brought me in to the C++ community, inspiring me to start watching conference talks, read C++ books, and eventually going to conferences in person.
After the conference, we went together with a group of people to a carefully chosen Indian restaurant for Bryce’s wish, then headed over to the hotel’s bar for talking about air travel, US credit cards and tornadoes for a surprisingly long time.
^ Jan Baart, Floris Bob, Conor Hoekstra, Jonathan Müller, Bryce Adelstein Lelbach and me.
Conclusion
Go to conferences! If you are a student, you can get a student ticket for a lower price or apply as a volunteer which usually comes with a free conference ticket. I would also recommend volunteering or giving a talk / lightning talk of some interesting topic you like.
~ Ambrus Tóth, 2024-10-12