Awesome talk! Very informative! I've always wondered why threads aren't memory aware as far as the abstract machine is concerned as it means that std::thread is far too restrictive of an API limiting such things as priority or stack sizes.
Most important detail from this video. 27:05 the sequence of evaluations for parameters in a function call is not specified. Never rely on it. 39:40 greater than or smaller than between 2 pointers of different type is undefined behavior.
8:10 that goes counter to almost all implementation of modular compilers that divide the task in front-end and back-end and have effectively an IR representation, that is a sort of "virtual machine" instruction set, this is what I don't get about C++, they talk beautifully about abstract machines, about having the tooling for human centric design, about having control of performance (which is mostly an algorithmic and memory structuring problem ) . Then next they talk about not having "layers of abstraction" between it and the hardware. But the compiler infrastructure introduces a layer of abstraction as they transform from AST to SSA then to IR then finally emit the final machine code. This is a lot of layers of abstraction. I basically think that nowadays C++ is just a VM language like Java, and its VM is a LLVM IR. And Isn't the abstract machine by itself a layer of abstraction ? but you are talking about extra ones, its all implementation detail, isn't ? But I don't think this principle of not having layers between the language and hardware really is possible, you have compilers, operating systems, memory hierarchy, caches, all sorts of things, unless C++ specs go all the way down, but then that goes against supporting multiple architectures and environments and being scalable. I think one could pose some sort of modular stack of software like the OSI model they have on network engineering, C++ thinks its the TCP/IP, but it is really application level. I wish the C++ abstract machine would embrace layers on top of layers and just take care of their side and pose a final no-undefined behavior specification for a real abstract machine, like LLVM IR or something, not a make-pretend one that leave a lot open for the lower level to it do "wrong". If that causes performance issues, then its the problem of the bottom layers of the stack to take care of it, you are after all a abstract machine language that pretend the hardware is a PDP11, when none of the real hardware we run nowadays are PDP11. Aka, you stop pretending you own the machine, that's never going to happen, unless you are on embedded controllers, but they too are getting boot-loaders and operating systems, and MMUs and those things, there's always something between you as a language and the machine code and the hardware itself. Having a real abstract machine with no undefined behavior would help the hardware designers conform more closer to it, it would really be a good thing. Sorry about the rant.
I just wondering. In C++ (37:40) comparing pointers which are not from the same array is Undefined behavior. But that means that using std map /std::set with key being as pointer is also UB. I look at std::less implementation and see not magic for pointers, they fall directly to < operator. So... I googled a bit and found nothing about sets of pointers being dangerous. Everyone using them. Every project, literally. So... What I am missing? (ok, key itself is not UB, but it rarely objects from the same array).
Also if I need to find out whether a pointer points to an element within an array, would it be wrong to compare it with the addresses of the ends of the array?
As far as I know this goes back to some argument between Stepanov and some committee (C++ ‘s?) had back in the day. Stepanov wanted total order for pointers so he can use them as keys in std::map, but this was impossible in the C (C++?) abstract machine. So there was a compromise reached, less-than operator would keep being undefined in general for pointers but std::less function object was *mandated* to work for pointers. If this sounds like having your cake and eat it too is because it is. The idea I guess is that the standard library implementation would have to do whatever is necessary *in the corresponding platform* to make std::less be well defined for pointers, for example convert pointers to integers or bit patterns, or whatever the platform is allowed to do. I guess in some platforms this doesn’t require to do anything special and it just works as you saw. (Remember that the standard library implementation can do things that you can’t because it knows in which platform is running). I guess this compromise makes sense because std::less is like a comparison that doesn’t imply arithmetic reachability, i.e. std::less{}(a, b) == true doesn’t imply that a + 1 + 1 + … == b.
what about *inline static auto k = 0;* in a namespace scope in a header file? "inline" tells it's an external ODR linkage but "static" tells that it's an internal linkage with a static storage duration.
Why is the fact that there must be equivalent flows of execution at AM level and physical hardware level not obvious? I mean, provided that the implementation (compiler) is bug-free it is obviously true.
It is, but the problem is that the specification of the "abstract machine" is too lousy. Why do they do that, and then make the excuse "for performance", its not, it really isn't, its for the benefit of the compiler writers. aka, some sort of lazyness. If you read about the abstract/virtual machine specification of JVM or the CLI or the V8, they leave no space for undefined behavior, and they don't see to have performance problems with that. (no one is using exotic hardware architectures that aren't register machines either harvard or von-neuman) Or the fact that they leave aliasing totally out, modern hardware with memory management and direct memory memory access to the hardware HATES aliasing. But the C virtual machine, I mean, the C abstract machine has to pretend that aliasing can always occurs because of the way they define pointers. The specification is a just make-pretend PDP11 machine and they pretend they talk directly to hardware. This idea is holding the industry back, eventually the industry will break free of C++ for those reasons I think.
Because the implications of not having UB are costly. For instance, indexing past the end of an array is UB. So in a dynamic setting where the compiler doesn't know the size of the array, how could they get rid of this? It would cost something, because there would need to be a check/branch somewhere. The language pushes this responsibility into you, because it allows you to do things in a more efficient way. If I know that something in my program makes it impossible to index past the end of the array, I may not need the check at all. Therefore, UB is desired from a performance standpoint in this kind of condition.
10:15 - Wouldn't it be better to say that the abstract machine is used as a guide for what would run, rather than say that code is actually run on this abstract machine?
What's an abstract machine ? its a virtual machine, just like the JVM, or the CLI or the V8, but we use an AOT compiler instead of a JIT one, just to make people angry. I'm kidding.
this track was an very good idea, hope it continues in the years to come.
great talk!
will definitely share around
A very accessible 1 hour introduction to all the statements floating around in the standard that specify the c++ abstract machine.
Why I love "c++" ❤️
Best explanation I have seen so far about the abstract machine. Thanks.
Awesome talk! Very informative! I've always wondered why threads aren't memory aware as far as the abstract machine is concerned as it means that std::thread is far too restrictive of an API limiting such things as priority or stack sizes.
What a great talk!
Wonderful stuff.
Very informative! The talk would benefit from introducing concrete examples earlier - the first bit of example code snippet is introduced in 23:55 .
Most important detail from this video.
27:05 the sequence of evaluations for parameters in a function call is not specified. Never rely on it.
39:40 greater than or smaller than between 2 pointers of different type is undefined behavior.
Thankyou 💕💐🙏
I loved this. I would like to be a part of the C++ community.
8:10 that goes counter to almost all implementation of modular compilers that divide the task in front-end and back-end and have effectively an IR representation, that is a sort of "virtual machine" instruction set, this is what I don't get about C++, they talk beautifully about abstract machines, about having the tooling for human centric design, about having control of performance (which is mostly an algorithmic and memory structuring problem ) . Then next they talk about not having "layers of abstraction" between it and the hardware.
But the compiler infrastructure introduces a layer of abstraction as they transform from AST to SSA then to IR then finally emit the final machine code. This is a lot of layers of abstraction. I basically think that nowadays C++ is just a VM language like Java, and its VM is a LLVM IR.
And Isn't the abstract machine by itself a layer of abstraction ? but you are talking about extra ones, its all implementation detail, isn't ?
But I don't think this principle of not having layers between the language and hardware really is possible, you have compilers, operating systems, memory hierarchy, caches, all sorts of things, unless C++ specs go all the way down, but then that goes against supporting multiple architectures and environments and being scalable.
I think one could pose some sort of modular stack of software like the OSI model they have on network engineering, C++ thinks its the TCP/IP, but it is really application level. I wish the C++ abstract machine would embrace layers on top of layers and just take care of their side and pose a final no-undefined behavior specification for a real abstract machine, like LLVM IR or something, not a make-pretend one that leave a lot open for the lower level to it do "wrong".
If that causes performance issues, then its the problem of the bottom layers of the stack to take care of it, you are after all a abstract machine language that pretend the hardware is a PDP11, when none of the real hardware we run nowadays are PDP11.
Aka, you stop pretending you own the machine, that's never going to happen, unless you are on embedded controllers, but they too are getting boot-loaders and operating systems, and MMUs and those things, there's always something between you as a language and the machine code and the hardware itself.
Having a real abstract machine with no undefined behavior would help the hardware designers conform more closer to it, it would really be a good thing.
Sorry about the rant.
I just wondering. In C++ (37:40) comparing pointers which are not from the same array is Undefined behavior. But that means that using std map /std::set with key being as pointer is also UB. I look at std::less implementation and see not magic for pointers, they fall directly to < operator. So... I googled a bit and found nothing about sets of pointers being dangerous. Everyone using them. Every project, literally. So... What I am missing? (ok, key itself is not UB, but it rarely objects from the same array).
Also if I need to find out whether a pointer points to an element within an array, would it be wrong to compare it with the addresses of the ends of the array?
As far as I know this goes back to some argument between Stepanov and some committee (C++ ‘s?) had back in the day. Stepanov wanted total order for pointers so he can use them as keys in std::map, but this was impossible in the C (C++?) abstract machine. So there was a compromise reached, less-than operator would keep being undefined in general for pointers but std::less function object was *mandated* to work for pointers. If this sounds like having your cake and eat it too is because it is. The idea I guess is that the standard library implementation would have to do whatever is necessary *in the corresponding platform* to make std::less be well defined for pointers, for example convert pointers to integers or bit patterns, or whatever the platform is allowed to do. I guess in some platforms this doesn’t require to do anything special and it just works as you saw. (Remember that the standard library implementation can do things that you can’t because it knows in which platform is running). I guess this compromise makes sense because std::less is like a comparison that doesn’t imply arithmetic reachability, i.e. std::less{}(a, b) == true doesn’t imply that a + 1 + 1 + … == b.
what about *inline static auto k = 0;* in a namespace scope in a header file?
"inline" tells it's an external ODR linkage but "static" tells that it's an internal linkage with a static storage duration.
Why is the fact that there must be equivalent flows of execution at AM level and physical hardware level not obvious? I mean, provided that the implementation (compiler) is bug-free it is obviously true.
It is, but the problem is that the specification of the "abstract machine" is too lousy. Why do they do that, and then make the excuse "for performance", its not, it really isn't, its for the benefit of the compiler writers. aka, some sort of lazyness.
If you read about the abstract/virtual machine specification of JVM or the CLI or the V8, they leave no space for undefined behavior, and they don't see to have performance problems with that. (no one is using exotic hardware architectures that aren't register machines either harvard or von-neuman)
Or the fact that they leave aliasing totally out, modern hardware with memory management and direct memory memory access to the hardware HATES aliasing. But the C virtual machine, I mean, the C abstract machine has to pretend that aliasing can always occurs because of the way they define pointers.
The specification is a just make-pretend PDP11 machine and they pretend they talk directly to hardware. This idea is holding the industry back, eventually the industry will break free of C++ for those reasons I think.
When next video?
why isn't having no UB an explicit goal of CPP language designers?
Because the implications of not having UB are costly. For instance, indexing past the end of an array is UB. So in a dynamic setting where the compiler doesn't know the size of the array, how could they get rid of this? It would cost something, because there would need to be a check/branch somewhere. The language pushes this responsibility into you, because it allows you to do things in a more efficient way. If I know that something in my program makes it impossible to index past the end of the array, I may not need the check at all. Therefore, UB is desired from a performance standpoint in this kind of condition.
10:15 - Wouldn't it be better to say that the abstract machine is used as a guide for what would run, rather than say that code is actually run on this abstract machine?
using quantum mechanics as a metaphor will not ever make the thing you're talking about more intuitive, great rest of the talk though!
What's an abstract machine ? its a virtual machine, just like the JVM, or the CLI or the V8, but we use an AOT compiler instead of a JIT one, just to make people angry. I'm kidding.