Thanks for watching! ❤ To try everything Brilliant has to offer-free-for a full 30 days, visit brilliant.org/TheCherno. The first 200 of you will get 20% off Brilliant’s annual premium subscription!
Defining a function in the header (inline) makes it more likely to be inlined simply because it's definition is accessible in other translation units. If the compiler compiles a file that includes the header containing that function and the body is not in there, it can't inline it, unless you enable link time optimization (LTO), which is usually pretty slow and only enabled for release builds. But you can enable and should LTO for release builds, so while the remark makes sense, it's not very relevant.
Defining a member function in the header treats it as inline implicitly, according to at least Meyers' Effective C++ series books. Of course, the standard inline keyword itself is just a compiler hint and does not guarantee that the function will actually be inlined in the binary. At least in my own industry C++ work, we typically only write the definition of a member function inside the class definition in a header file if it is very short, like 1 or 2 statements.
@@metal571 Actually the inline keyword only was a hint historically, and any relatively recent C++ compiler completely ignores it for that purpose. It's actual meaning is completely unrelated (as with many keywords in C++). It really just means "multiple definitions are permitted", in that if you include the some inline function in multiple translation units, the linker doesn't get upset that the symbol is present multiple times.
@@driedurchin I suppose that it is indeed true that nowadays most compiler optimizers will make most of the inlining decisions on their own regardless of the keyword. You are certainly correct in that not only does inline allow for functions to be declared in header files without violating ODR, but in fact variables as well can now be inline as of C++17. Thanks for the clarification... I am far from being a compiler engineer myself
20:33 a little tip here: if you don't have visual assist, you can still create definition by pressing "Alt+Enter" (opens quick fix dialog with first option to create definiton) and pressing "Enter" again, but annoyingly it opens the definiton in a preview window instead of a new tab. If you want the definition to be opened in a new tab then go to "Tools>Options>Text Editor>C/C++>Advanced->Refactoring”, set option “Navigation after Create Declaration/Definition” to “Open Document”. And here you go:)
Thanks Cherno. I have learned c++ by your videos as a hobby. I am studying electronic engineering and next semester we are going to do a bit of c++ and I am sure I will be able to nail it because of all your videos that explain so much. Keep it up like it when you get deep ;)
When it's inlined in the header, the compiled has a chance to inline it. Otherwise the linker is the only component that can inline it. This is why the LTO is popular.
I don't think the linker can inline a function, only the compiler does. That's why inline header functions force the compiler to recompile the function, giving it a chance to actually inline. Please correct me if I'm wrong.
@Mark Tekfan A classical linker will only place functions/data. However, for example ARM'S compiler, will evaluate if the function is small enough it'll inline it. With LTO, the normal optimization that is done in the Compiler step, is now done in the Link step
@@marknn3 Throwing abbreviations at others seems to be a sport nowadays ... especially when EXPLAINING something to another person(here to you) which they (by definition!) can't know. (No offense @adama, but I had to LRHAT:))) ) I suggest you read at least Wikipedia and then the GCC section about (L)ink (T)ime (O)ptimization. It isn't portrayed as here(so simple), is mostly used for RELEASE builds and (like everywhere, hehehe:) every benefit also most of the time comes with some disadvantages. You may want to read 1. GCC Wiki "LinkTimeOptimization" and 2. Wikipedia, "Interprocedural_optimization" ... that you experience yourself, that LTO is not the whole picture. Interprocedural optimization (IPO), Whole program optimization (WPO) and the mentioned Link-time optimization (LTO) are all part of family of techniques. It may be an interesting read?!:)
1:33 I think part of the reason java can just do this is the built in javadoc support. Even if there is not a lot of actual 'documentation' written the generated documents can be a great way to overview classes.
Nowadays, in Visual Studio 2022, you can hover over a macro and it gives you the option to expand it out to its evaluated state. It is great for checking it is working as intended
yes and no. if its a complex macro that is maybe working with templates too, the tooltip will be to scuffed. for the most easy one liner its is very helpful i agree
Here are my 50 cent about inline functions and why they belong into the header file. Maybe this is already outdated, but that is what I learned about it - long time ago. Imagine you have two compilation units a.cpp and b.cpp and a.cpp contains the inline function. Both units are compiled independently, it's not that the compiler compiles all compilation units at the same time, it compiles each single file individually into the corresponding a.o and b.o (or for windows a.obj and b.obj). And by individually I mean it's a separate compiler execution for each compilation unit. So when compiling b.cpp the compiler does not know anything about a.obj, but the compiler needs to know the body of the function to decide if it is worth to inline it or if it is better to use a function call. That is way the inline function is forced to be in the header, so that each single compilation unit can know the function body. And just to mention it, this also brings disadvantages to put functions in the header. If they do not get inlined, you end up with the same function in every single object file, which is an automated way of code duplication and can end up increasing your binary size. Just to avoid anyone saying "Oh but then just put every function in the header.". If this is not right or no longer right, please let me know. Always willing to learn something new.
Inline functions definitions increase obj size but are marked by the compiler so the linker can remove duplicate instead of rising a name conflict error. Expanding inline functions can increase the binary size if compiler cant optimize it away.
6:20, 6:30 I heard all the same things. Because of all the ambiguity, if I REALLY want something "inlined" and it's relatively small (2-5 lines) I implement it as a macro so there's no doubt what the compiler is doing with it.
True, although certain things will be more annoying that way, type checking, for instance. But I suppose that's just a personal preference for me and others might not find that so annoying, and in cases where you can get away with not doing it, it'll make no difference.
My theory for the declared functions in the header having more chances to be inline is that it is visible in other translation units. if you define the function in a header file (and use something like __forceinline) and declare it on a cpp file, this inline function won't be visible on other cpp files.
This is correct. The compiler can do inline optimizations if the implementation is in the same translation unit. Otherwise, the linker has to do this at the linking step. It can still yield the same code in theory, but its almost guaranteed to take longer to do since linking can't be parallelised nearly as well as compilation can. Of course, attempting to inline something that is really big enough to be better as a function call can really cause the opposite problem during linking as well so its really case-by-case basis
Have you looked into C++20 modules ? would be interesting to see how we would change the way we write code (I know I know not fully supported yet but i saw that libfmt was able to be modularized for the major compilers, although i dunno if premake support them yet)
They’re awesome! I think they’re going to dramatically improve the c++ experience. However, GCC and Clang only have partial support, and so right now, you can really only use them in msvc.
In my personal project I'm using them and love them. The support on Visual Studios has improved a lot too so it isn't breaking intellisense as much or slowing the system too much. I still wonder what is a good way to utilize them. Should I leave the implementations and definitions in the same file? Should I use private modules for implementations? Is there still a reason to define in the module and separate implementations into the source file? I have been mixing around these details in my personal project and still don't really know the answers. I personally like the private module use because I like seeing what a file contains with the definitions and then the details in the same file just below elsewhere.
I tend not to use MSVC. I like GCC a lot. Seeing that module is not fully supported make it more trouble than it's worth. cause what if one day you find it need to be reverted in order to compile on other OS like linux? Linux do well with GCC guys.
Maybe in 5 years. Tools are far from useable with modules. Not working with XCode and Android NDK at all. Almost everything in C++20 is still barely useable, coroutines and ranges for example. But module are in the worst state.
Here's why the compiler is more likely to inline header code than source file code: If it's in the header, the compiler has access to the implementation, so it can be done at compile time. If it's in the source file, then it has to be done at link time. Unless you enable LTO, there is much less guarantee that this happens, especially on less mainstream platforms for your target toolchain. The inline function in methods is a weird one, I don't really see why you'd use it most of the time. In non-member functions in headers it's basically required (static works too, but then you have a duplicate implementation floating around for each source file that includes it, which may be what you want in some cases, but then you're probably doing black magic involving sacrificing babies).
One thing about private vs public members first... Unfortunately the order of member variables affects the class size in memory due to data alignment. So sometimes it can get a tiny bit more performance in exchange for separating private member variables from each other.
@@ohwow2074 Uh, yes, but that's most useful data members to be fair. Also it's not immediately obvious behavior, rust for example will by default rearrange members to try to improve alignment.
@@andrewf8366 I don't know if public and private affect the alignment but the order should be from the largest size to the smallest for better alignment.
Compile time can be a big deal. I've worked on projects that started as a few seconds to build, then a few minutes, eventually getting to several hours and for one particularly poor performing compiler a few days. The geometric growth of number of lines the compiler needs to parse can easily get out of hand. Modern compilers do a lot better job, especially with multiple cores. The standard practice is to use #include guards or the #pragma once. I tend to use #include guards these days. The second thing is to use forward declarations where possible. This works well if your header doesn't need to see the full class definition, such as if you have only a pointer or reference to a class. At one point I wrote an analysis tool that walked include paths and counted number of lines each header contributed to a build. In doing this I could then focus on eliminating the heavy hitters. The decrease in build time that was realized was more than worth the time. These days I tend not to work on such large applications and compilers tend to handle things better. For you discussion on in header definition of functions. A compiler can't inline what it hasn't seen. Depending on the compiler, that usually means that a function fully defined in the header is a candidate for inlining. However, if a compiler uses some sort of compilation database, which I believe Visual Studio does, then it may be able to inline functions that are not defined in a header. One tactic that I have used is to always put every function definition into the CPP file until I find that performance can be improved by putting it in the header and marking it inline. Most of the time inline functions don't buy you much performance gain. The main exception is cases that are very trivial like accessing a member variable. The first thought I had when you showed the header is that I would use the using statement to simplify those complicated container declarations. A simple example might be if I have a vector of std::unique. I might declare using Ptr = std::unique: followed by using MyClassVector = std::vector; This pattern also makes it a lot easier to swap out the container later on. It's basically the same as typedef, but with a much easier to remember syntax. I also prefer putting the member data and private functions at the end like you demonstrated and for most of the same reasons. The public interface should be all you need to use a class. I apply that idea to documenting code as well. If I can't document a class such that anyone using it won't need to see the header or other source, then I probably need to improve the API to make it simpler.
6:48 it used to be that "inline" keyword was about actually inlining functions, but nowadays the compilers decide what to inline according to various metrics. The "inline" keyword affects this metric, but a very small amount. The real effect of "inline" is related to ODR. But you need LTO / LTCG enabled in order for functions to get inlined across translation units - in that regard defining functions in a header helps, but not the inline keyword itself. And you should always mark functions with headers "inline" because otherwise you're looking at ODR violations and linker errors about "multiple definitions".
It's always been a hint, and not a requirement, as per the standard, but compilers generally try to inline things if you suggest it. Of course, with a high enough optimization level, things not marked inline will get inlined if the compiler thinks they should.
@@anon_y_mousse, my point is that the impact of this hint is extremely small. Use __forceinline (or the GCC's analog) if you really want something inlined.
It's not a myth and you did not get it right Cherno. Functions that defined in header CAN be inlined by compiler because definition of this function would be visible in each translation unit that includes that header and functions that only declared in the header can't be inlined because translation unit do not know body of the function. Linker can inline functions across translation units if LTO is enabled but that is incredibly slow. That's why unity builds rule XD
At work we use a lot of forward declarations reducing the amount includes. It is a pain having a code style change in a header file that triggers a waterfall of compilations.
I believe it is actually impossible to inline the function when it is in a different compilation unit. It would HAVE TO be done by the linker. So unless linker optimizations can do inlining, it is impossible. Not sure if linker does inlining or not.
You probably won't read this comment, but I'd love to share the reason why some methods are implemented in the header file. Unreal Engine is a great example. The code is divided into modules that are dynamically loaded, such as DLL files. Modules that depend on other modules can access their header files, but the implementation (the CPP file) is only available during the compilation of the module that owns the header file. If a header file contains only the declaration for method M and another module includes M, it won't be able to inline this method because it lacks the definition. The definition is provided through dynamic linking. However, if the definition is in the header, the compiler can inline this method for the dependent modules. More commonly used and smaller methods will likely have their definitions in the header file. Deciding to inline every possible method could cause your libraries to consume more memory since methods will not be shared. Thus, this represents a tradeoff between performance and memory usage.
Cherno with proper support coming for modules soon(TM) for C++, what are your thoughts on it? It claims it will reduce the messiness of inclues(which just copy paste whole files) from what I heard.
Thank you for your nice video and helpfull tipps. One word about defining functions in the header file: it's not only a matter of code size. IMHO i don't want not leak too many implementation details by my header files, especially if I'm writing library code. Implementation details in my header files can create unnecessary dependencies between my modules and make the use and refactoring of my library code much harder.
Little question: I'm wanting to build my own GUI-app with a 3D rendering scene and a menu on the side. I've seen the ImGUI video from Cherno. Is it still one of the best options, for a beginner?
What will be first - private section or public is topic for holy war. Because in some cases I can open header file for see internal stuff (private) or in other cases it can be external interface (public). I my opinion if this sections have big bunch of fields and methods (like in this example) it just need separate on few classes. If class will be simple it don't different what section will be first or second I will see all header file at on single screen.
This kind of stuff is exactly why I've migrated to C# instead of C++. Not only are the standard libraries better but you have a lot less compile debt due to header files and can enjoy not dynamically allocating memory if you don't want to. While remaining very close to the hardware. JIT'd even. Not to mention the C++ standard library is garbage in comparison. I also am relieved to not have to deal with compiler flags et al. I also enjoy hotloading code, so I can live edit stuff as I am doing things. Instead of recompile and suffer going through to the point of where I need to test.
Well "better header files" would be using modules instead of header files buuuut clang and gcc support for them is still experimental and so is cmake support
Generally, if the variable is expensive to copy (large amount of data or expensive copy constructor) yes. But if you have something that can fit in a register, there is usually no benefit and passing by value would just be simpler.
I've been playing with Unreal engine including trying to learn a bit of C++ and adding classes in code. I wonder if I did what you just did if it would list all the expansions in the actual unreal engine part, which seems to be pretty big. Maybe I'll try it. It takes me 40 minutes to compile the engine from source. This is very useful video because I was trying to learn stuff from a book and due to a small error was getting gaziliions of the same error and I had no idea why. Now I do. Thanks. Hmm, the properties page for my project doesn't include the same as yours. There is no C++ entry so I can't try the preprocessor thing.
Inline's original intention of helping the optimizer isn't really the same meaning these days, it's more just about having a the same definition specified in multiple translation units (aka multiple definition are permitted). The optimizer without link time optimization is going to have a reasonable chance of optimizing these if they are small and it thinks they are suitable, if they are not visible then it requires link time optimization / link time code generation. Member functions defined in the class definition are automatically marked as inline, the inline keyword is meaningless. You should typically avoid force inline it's nearly always the wrong thing, and you just hinder compile times and very rarely do you actually help the optimizer.
Nearly always is maybe a stretch. I feel like you should use it about as often as most compiler directives - when you're quite sure you should. Not sure it needs that qualifier
@@kippers12isOG the trouble is many people think they need it, but in virtually all cases they do not really know. I've worked on several engines where people thought they were smart and used always inline and when disabling always inline it had no noticable difference in runtime performance.
Thank you for the high quality content as always! I'm wondering what's your take on static code analysis tools. I've seen your sponsored video about PVS studio a while ago, but haven't seen it in use in any of your videos since. The question why I'm asking is because a lot of the topics in the code reviews can be automatically detected (and often even corrected) by these tools. Personally I'm using clang-tidy which is free and has a great number of checks available.
Wondering of what you think about Jonathan Blow’s new language Jai? His approach to create a successor of C++ as well as his general concepts for a programming language - for example with no header files, the amazing compiling speed, etc… .
I think "the compiler more likely inlines functions in a header file" is just a myth. The actual compiler, after the pre-processor, doesn't even know which part is from the .h file and which is from the .cpp, it's just a huge post-processed text blob. But most header file function definitions are so simple that they get inlined; if they get too complex the linker forces the removal from the header file (double functions found) - so it's inverse logic that "header file definitions get inlined". Also, any public consumer of a class would pull class logic into its own code, which is just a bad design. In our company, any function definition (even the simplest) is not allowed. And we also don't stick to "every .cpp must have a .h file" -- mostly we have 1 header file for 1 class definition, and the class is split among 10-15 .cpp files, each small and focused on a single class functionality group.
"The actual compiler, after the pre-processor, doesn't even know which part is from the .h file and which is from the .cpp, it's just a huge post-processed text blob"
@@voytechj this is done with a #line marker that states the original file and a reference line number; the following lines are then determined by a delta and a lot of empty lines (because it uses less characters than repeating the #line marker); nonetheless the compiler doesn't "understand" what it means, it's text and a number that is printed when compiler errors are found; old compilers didn't print code locations, just the current error line and that was it
I particularly hate CMake for many reasons: poor syntax, silent syntax errors, complex logic function names ... and the worst for me: need the same number of lines than for a Makefile ... to generate a Makefile :) I only use Makefiles. I made some macros that I included in all my projects (git submodules) then my main Makefile is tiny and is just made of a list of files to compile, path (to search them) and include dir (to search headers) and include libraries that can be found by the pkg-config command. So more compact than CMake. No more. I'm agree than Makefile syntax and rules are sometimes painful (when using code source generation since Makefile shall know your files not yet existing). Else you have scons a build system using python language I used it once, I liked it (the drawback is the have .o next to your .c files).
The correct word for headers is "abstraction". Headers are an abstraction of the class. In Java, you have interfaces (abstraction). That is the same as headers. However, requiring it for every class is a pain in the ass.
When you declare a function but don't define it in a header file then the compiler _can't_ inline it. When you include that header file in another source file, the compiler doesn't have the definition of that function available for inlining. You can't expect the compiler to inline a function that it doesn't have an implementation for! The compiler has no choice but to call the function. Only the linker, once it pieces all the object files together, could optimize that any better.
Interesting video. 1/ I hate classes with hundreds of public methods. Can I use destroy() method at runtime ? Can I call add_background() twice ? What happens if I call close_all() or set_init_false() before calling init() ? I prefer letting max 4 public methods to not let choice the user to call the wrong method (follow my white path). Need to split code into several classes. 1 Class = 1 idea. And to be sure the class does a single thing is to comment it. If you start writing this class does this and this and this ... this means something wrong in the design and start to split it. 2/ For cleaning my includes (reducing cycles) I use doxygen with the option for rendering graph of includes. I then remove manually cycles until having a spamming tree. I know some people does not like this because you can remove includes such string, map, vector in a file that need them because included in another header file but if you modify the last file removing an include, your current file may stop compiling.
Newer subscriber, so sorry if you've already talked about this in another video, but what about pimpl to keep your headers cleaner? I know, less chance of inlining, but curious about your thoughts. And to your commenters talking about modules: my opinion is wait unless you are Microsoft only. gcc and clang aren't there yet.
Im programming on C++ almost 4 years and still can't get used to writing variables at the end of the class, regardless of the level of protection, so I write them from above as it is easier for me. Oh that Java make so many damage....
No, this isn't for me. I want explanations, not opinions. When you are reading the public part of a class, you are reading an "API" ("application programming interface" in the old sense), it is a "contract" (in a human sense): the documentation directed towards any programmer *_using_* your class. The public part of the class should be extensively documented: purpose, what value its call returns and how it changes the state of the class, the purpose and semantics of the values of each and every parameter, as well as exceptions. The private parts contain notes by the programmer who made the class: if that programmers have to put the class aside and return to it after 4 months: _"where was I, what is the purpose of this loop?"_ When you reduce the size of a class by refactoring it into multiple dependent classes, you do it in order to reduce its size to a *screen-full.* That is: if your font becomes unreadable when you zoom out of the class/function/method, then it cannot be viewed screen-full. Somebody is viewing the screen, somebody reads the code. All explanations rest on the fact that *a programmer* is trying to orient himself/herself through the code. Programmers need to understand psychology.
As long as the file thats including the other file (imagine you include game.h) has access to the implementation of the code, it will inline it (at least in c). So if you include a header file with code, that code is always inlined. As simple as that.
That's not true. Not in C++ or C. The inline keyword has implementation defined impact on how likely it is your function will be inlined. In practice, it's effectively ignored for this purpose, because the compiler (at least thinks that it) knows better
A decent approach to clean up the mess in that game code and you explained very well why you would do it this way. Also thumbs up for showing the processor output ;-) I have a few addtiotional tips if you may be interested: You could move all "Game" fields one or mulitple state classes/structs and let relevant codes in the Game class referencing it by the function arguments only. This way you easily see which functions requires, what type of states and therefore can maybe extract it further out. Last thing regarding compile times, i have another tip i can share - but most people dont like it, because it requires good header inclusion handling: The entire game can be compiled from one single translation unit, instead of compiling different translation units. In the main.cpp or you main entry point cpp file, simply include all the required files like this: // Sometimes you need to define some header beforehand #include "game.h" #include "math.cpp" // Entire math #include "assets.cpp" // All assets data, fixed fonts, tile-coordinates, etc. #include "game.cpp" // Entire game #include "box2d.cpp" // Entire physics engine int main(int argc, char*[] argv) { // entry code, platform initialization game loop, etc. } This way the compile time can be almost instant, even on projects with 100k+ lines of code! I do this since 5 years in all my C/C++ projects and it works great and safes me a ton of time. Sometimes to make a library compile this way, it requires a bit of fiddling and some source libraries can´t work at all in this behavior, but thats okay.
So, I'm going to say the obvious that you should never, never, never, never include a .cpp especially for library code. A lot of code with use anonymous namespaces(or the static keyword in the c style way of doing things) to prevent dependency leakage for various implementation files. This is a very common and standard good practice for reducing collisions with other translation units and helping to mitigate ODR issues. If you do this however, you can get a crazy amount of dependency leakage. Be *very* careful using this unless you know *for sure* that the .cpp files won't leak dependencies. It is a real nightmare to debug, especially for things with simple names like "tmp" or "first" or "obj" which where meant to be private to an implementation file's own translation unit. Finding and tracing these is a nightmare, especially if your code analysis bugs out. Especially in bigger projects, I recomend you just don't do this. And thanks to how guards work on macros, you might not even catch some of these until you target a specific platform, and then you may have to dig through a large amount of code that you didn't add or that hasn't been modified in forever to trace the dependency chain and find the issue. If you do need to do this though, first I pity you and good luck, and second the -i flag in gcc is a lifesaver for seeing how the includes get generated for each translation unit.
@@Firestar-rm8df I never had any problems with my approach, as i mostly include single-file-header libraries - exceptions are box2d (50 cpp files) and imgui (5 cpp files), which worked after i transformed the original header file (box2d.h and imgui.h) to be "single-header-file" with an optional implementation block, that includes all .cpp directly in the correct order. But you are correct, sometimes there are such libraries which are so bad designed that, when you include the cpp translation directly, you are screwed. Those libraries needs its own translation unit(s) unfortunatly :-( assimp, is a good example of a library that can´t by included directly as a source with my method - but i dont care, if i need assimp then it would be a offline processing tool and i am done.
Your Java example makes no sense. The thing that makes more sense is that on Java you were writing code for yourself and not other people. I know for a fact the compiler can inline functions in the CPP. My reason for not putting function bodies in headers is to speed up compilation.
This whole inlining thing is somewhat overmarketed, I would say. And I even think that it hurts unexperienced developers, because they start to think too much about whether to inline each and every function or not, and actually end up inlining almost everything. This is premature optimization. And in fact, in most of the cases, inlining is not going to give you any sensible performance boost. Just like using "++i" instead of "i++" will not speed up you program, eventhough "++i" is in theory faster than "i++". Remember that fundamentally in order to be inlined, a function has to be a hot and short piece of code in order for inlining to be sensible. For example, by default Java JIT compiler only considers a specific method for inlining if its both "short" (meaning that it runs fast, for example a function that prints to the console or loads an image is not "short", despite it potentially being one line of code) and has been called over 10k times. And despite all that, remeber 80/20 rule. 80% of performance bottlenecks usually sits in your choice of algorithms and datastructes that you use to implement the task. And remaining 20% are these tiny and specific details, like hot code inlining, data structure padding for cache alignment, and other stuff. As an example, watch the recent video on this channel named "How I Made My Game Engine MUCH Faster...". The Cherno managed to double the fps by implementing a better algorithm for managing descriptor sets, no method inlining could ever give you that performance boost. This is a perfect example of how you should optimize. So, to sum up, if you find yourself thinking too much about whether or not you should inline some method, then take a step back and realize that this should not be your main concern. Instead, delegate this choice to the compiler, and focus on your main task - implementing a unit of functionality. Later, after you finish your task, you can do perfomance analysis and perfomance tuning of that finished code, and it will give you the hints about these minor optimizations.
In the case of pre- versus post- increment, it depends on whether you're using an iterator class or a simple integer type. In the case of an iterator class, pre-increment will necessarily be faster because it'll not make an unnecessary copy of the iterator that'll merely be discarded anyway. With higher optimization levels, hopefully such things can be caught, but that requires link time analysis often because the iterator may be defined in another file and swapping post for pre won't be something the compiler can decide on its own here. In the case of Java, an indirect function call won't be noticeably, if at all, slower than a direct function call because it's a virtual machine, not a directly compiled program, but we should be discussing C++ in which not inlining the trampoline will cause a second function call to be emitted in the generated code and that does have some overhead which if looped can cause problems in more than one way. For instance, cache misses when that extra function call overfills the cache, or segfaults when it smashes the stack, which is also a security concern. While algorithmic and structural designs are the most important part of designing your application, you should realize that they are also a part of the optimization process. It takes practice and knowledge of the underlying architecture, but you can prevent certain slowdowns by being aware of where they might occur and thinking of how to avoid them as you write. I've heard various statements on optimization and "premature" optimization being bad, but none of the arguments hold up as some optimizations quite simply can't be made with a mere change of the structure or algorithm used. My take is that you should always think of optimization, from the start, while you're coding, and even once you're done.
@@anon_y_mousse as for pre/post increment, there is an empirical evidence that shows absolutely no performance difference between pre/post increment when using STL Iterators actually without LTO enabled, and this debate can go forever, but there is really no much difference between the two, and this is only a matter of preference. Usually your program spends about 0.0000001% of its runtime doing pre/post increments, so it’s not going have much effect. As per Java, nowadays it is actually compiled to machine code at runtime by JIT and is recompiled every now and then while the program runs to optimize for the specific scenarios in which the program runs. And in some cases java programs run even faster than corresponding c++ programs. Why? Because compiler knows how to optimize better than even most experienced programmers. And about inlining, I agree that you should inline some very simple functions like accessors/mutators when you write them, but again, compilers are very very aggressive at inlining, and they will do it for you anyway when you build for distribution
Also please please please if you're noticing you're not very consistent with your formatting; just use clang formatter. Nothing worse than poorly formatted code you have to read all day.
Thanks for watching! ❤
To try everything Brilliant has to offer-free-for a full 30 days, visit brilliant.org/TheCherno. The first 200 of you will get 20% off Brilliant’s annual premium subscription!
I would like to see a video where you talk about settings in the properties, what you use, when to use, etc.
Hi
You have a nice Visual Studio design, is there somewhere to download your settings?
Hey cherno, i like your theme, what's its name?
Defining a function in the header (inline) makes it more likely to be inlined simply because it's definition is accessible in other translation units. If the compiler compiles a file that includes the header containing that function and the body is not in there, it can't inline it, unless you enable link time optimization (LTO), which is usually pretty slow and only enabled for release builds. But you can enable and should LTO for release builds, so while the remark makes sense, it's not very relevant.
^that, also LTO hasn't been available back in the days, so inlining whole body was more meaningful back then for the reasons you mention.
Defining a member function in the header treats it as inline implicitly, according to at least Meyers' Effective C++ series books. Of course, the standard inline keyword itself is just a compiler hint and does not guarantee that the function will actually be inlined in the binary.
At least in my own industry C++ work, we typically only write the definition of a member function inside the class definition in a header file if it is very short, like 1 or 2 statements.
@@metal571 oh hi metal, lol
@@metal571 Actually the inline keyword only was a hint historically, and any relatively recent C++ compiler completely ignores it for that purpose. It's actual meaning is completely unrelated (as with many keywords in C++). It really just means "multiple definitions are permitted", in that if you include the some inline function in multiple translation units, the linker doesn't get upset that the symbol is present multiple times.
@@driedurchin I suppose that it is indeed true that nowadays most compiler optimizers will make most of the inlining decisions on their own regardless of the keyword.
You are certainly correct in that not only does inline allow for functions to be declared in header files without violating ODR, but in fact variables as well can now be inline as of C++17. Thanks for the clarification... I am far from being a compiler engineer myself
20:33 a little tip here: if you don't have visual assist, you can still create definition by pressing "Alt+Enter" (opens quick fix dialog with first option to create definiton) and pressing "Enter" again, but annoyingly it opens the definiton in a preview window instead of a new tab. If you want the definition to be opened in a new tab then go to "Tools>Options>Text Editor>C/C++>Advanced->Refactoring”, set option “Navigation after Create Declaration/Definition” to “Open Document”. And here you go:)
Thanks!! Very helpfull!
Thanks Cherno. I have learned c++ by your videos as a hobby. I am studying electronic engineering and next semester we are going to do a bit of c++ and I am sure I will be able to nail it because of all your videos that explain so much. Keep it up like it when you get deep ;)
When it's inlined in the header, the compiled has a chance to inline it. Otherwise the linker is the only component that can inline it.
This is why the LTO is popular.
I don't think the linker can inline a function, only the compiler does. That's why inline header functions force the compiler to recompile the function, giving it a chance to actually inline. Please correct me if I'm wrong.
@Mark Tekfan A classical linker will only place functions/data. However, for example ARM'S compiler, will evaluate if the function is small enough it'll inline it.
With LTO, the normal optimization that is done in the Compiler step, is now done in the Link step
@@adama7752 Good to know. Thanks for this additional information!
@@marknn3 Throwing abbreviations at others seems to be a sport nowadays ... especially when EXPLAINING something to another person(here to you) which they (by definition!) can't know. (No offense @adama, but I had to LRHAT:))) )
I suggest you read at least Wikipedia and then the GCC section about (L)ink (T)ime (O)ptimization. It isn't portrayed as here(so simple), is mostly used for RELEASE builds and (like everywhere, hehehe:) every benefit also most of the time comes with some disadvantages.
You may want to read 1. GCC Wiki "LinkTimeOptimization" and 2. Wikipedia, "Interprocedural_optimization" ... that you experience yourself, that LTO is not the whole picture. Interprocedural optimization (IPO), Whole program optimization (WPO) and the mentioned Link-time optimization (LTO) are all part of family of techniques. It may be an interesting read?!:)
1:33 I think part of the reason java can just do this is the built in javadoc support. Even if there is not a lot of actual 'documentation' written the generated documents can be a great way to overview classes.
Nowadays, in Visual Studio 2022, you can hover over a macro and it gives you the option to expand it out to its evaluated state. It is great for checking it is working as intended
yes and no. if its a complex macro that is maybe working with templates too, the tooltip will be to scuffed. for the most easy one liner its is very helpful i agree
@@Zvend VS can "Expand Inline", means it can replace the macro with the actual code. Quite recent feature tho
I believe it's a preview feature
not only that if you put a comment above your code sometimes the tooltips marked with your comment are the only information it has.
Here are my 50 cent about inline functions and why they belong into the header file. Maybe this is already outdated, but that is what I learned about it - long time ago. Imagine you have two compilation units a.cpp and b.cpp and a.cpp contains the inline function. Both units are compiled independently, it's not that the compiler compiles all compilation units at the same time, it compiles each single file individually into the corresponding a.o and b.o (or for windows a.obj and b.obj). And by individually I mean it's a separate compiler execution for each compilation unit. So when compiling b.cpp the compiler does not know anything about a.obj, but the compiler needs to know the body of the function to decide if it is worth to inline it or if it is better to use a function call. That is way the inline function is forced to be in the header, so that each single compilation unit can know the function body.
And just to mention it, this also brings disadvantages to put functions in the header. If they do not get inlined, you end up with the same function in every single object file, which is an automated way of code duplication and can end up increasing your binary size. Just to avoid anyone saying "Oh but then just put every function in the header.".
If this is not right or no longer right, please let me know. Always willing to learn something new.
Inline functions definitions increase obj size but are marked by the compiler so the linker can remove duplicate instead of rising a name conflict error. Expanding inline functions can increase the binary size if compiler cant optimize it away.
6:20, 6:30 I heard all the same things. Because of all the ambiguity, if I REALLY want something "inlined" and it's relatively small (2-5 lines) I implement it as a macro so there's no doubt what the compiler is doing with it.
True, although certain things will be more annoying that way, type checking, for instance. But I suppose that's just a personal preference for me and others might not find that so annoying, and in cases where you can get away with not doing it, it'll make no difference.
My theory for the declared functions in the header having more chances to be inline is that it is visible in other translation units. if you define the function in a header file (and use something like __forceinline) and declare it on a cpp file, this inline function won't be visible on other cpp files.
This is correct. The compiler can do inline optimizations if the implementation is in the same translation unit. Otherwise, the linker has to do this at the linking step.
It can still yield the same code in theory, but its almost guaranteed to take longer to do since linking can't be parallelised nearly as well as compilation can.
Of course, attempting to inline something that is really big enough to be better as a function call can really cause the opposite problem during linking as well so its really case-by-case basis
@@AURORAFIELDS Thank you for the extra knowledge =)
Have you looked into C++20 modules ? would be interesting to see how we would change the way we write code (I know I know not fully supported yet but i saw that libfmt was able to be modularized for the major compilers, although i dunno if premake support them yet)
@@h..h yeah and it's possible to do make it a module and a header at the same time by setting a prepocessor flag :D !
They’re awesome! I think they’re going to dramatically improve the c++ experience. However, GCC and Clang only have partial support, and so right now, you can really only use them in msvc.
In my personal project I'm using them and love them. The support on Visual Studios has improved a lot too so it isn't breaking intellisense as much or slowing the system too much.
I still wonder what is a good way to utilize them. Should I leave the implementations and definitions in the same file? Should I use private modules for implementations? Is there still a reason to define in the module and separate implementations into the source file? I have been mixing around these details in my personal project and still don't really know the answers. I personally like the private module use because I like seeing what a file contains with the definitions and then the details in the same file just below elsewhere.
I tend not to use MSVC. I like GCC a lot.
Seeing that module is not fully supported make it more trouble than it's worth.
cause what if one day you find it need to be reverted in order to compile on other OS like linux?
Linux do well with GCC guys.
Maybe in 5 years. Tools are far from useable with modules. Not working with XCode and Android NDK at all.
Almost everything in C++20 is still barely useable, coroutines and ranges for example. But module are in the worst state.
Here's why the compiler is more likely to inline header code than source file code:
If it's in the header, the compiler has access to the implementation, so it can be done at compile time.
If it's in the source file, then it has to be done at link time. Unless you enable LTO, there is much less guarantee that this happens, especially on less mainstream platforms for your target toolchain.
The inline function in methods is a weird one, I don't really see why you'd use it most of the time. In non-member functions in headers it's basically required (static works too, but then you have a duplicate implementation floating around for each source file that includes it, which may be what you want in some cases, but then you're probably doing black magic involving sacrificing babies).
Dude, I love you and appreciate these videos so much. Thank you for creating and sharing the series!
One thing about private vs public members first...
Unfortunately the order of member variables affects the class size in memory due to data alignment. So sometimes it can get a tiny bit more performance in exchange for separating private member variables from each other.
This is only true for non static member variables.
@@ohwow2074 Uh, yes, but that's most useful data members to be fair.
Also it's not immediately obvious behavior, rust for example will by default rearrange members to try to improve alignment.
@@andrewf8366 I don't know if public and private affect the alignment but the order should be from the largest size to the smallest for better alignment.
I love this new way of titling, where you put the topic your covering instead of like an ep #
2:30 There is good analogy for that. Epigraph and content page in books. It comes first to tell you what you can get from it.
Compile time can be a big deal. I've worked on projects that started as a few seconds to build, then a few minutes, eventually getting to several hours and for one particularly poor performing compiler a few days. The geometric growth of number of lines the compiler needs to parse can easily get out of hand. Modern compilers do a lot better job, especially with multiple cores. The standard practice is to use #include guards or the #pragma once. I tend to use #include guards these days. The second thing is to use forward declarations where possible. This works well if your header doesn't need to see the full class definition, such as if you have only a pointer or reference to a class. At one point I wrote an analysis tool that walked include paths and counted number of lines each header contributed to a build. In doing this I could then focus on eliminating the heavy hitters. The decrease in build time that was realized was more than worth the time. These days I tend not to work on such large applications and compilers tend to handle things better.
For you discussion on in header definition of functions. A compiler can't inline what it hasn't seen. Depending on the compiler, that usually means that a function fully defined in the header is a candidate for inlining. However, if a compiler uses some sort of compilation database, which I believe Visual Studio does, then it may be able to inline functions that are not defined in a header.
One tactic that I have used is to always put every function definition into the CPP file until I find that performance can be improved by putting it in the header and marking it inline. Most of the time inline functions don't buy you much performance gain. The main exception is cases that are very trivial like accessing a member variable.
The first thought I had when you showed the header is that I would use the using statement to simplify those complicated container declarations. A simple example might be if I have a vector of std::unique. I might declare using Ptr = std::unique: followed by using MyClassVector = std::vector; This pattern also makes it a lot easier to swap out the container later on. It's basically the same as typedef, but with a much easier to remember syntax.
I also prefer putting the member data and private functions at the end like you demonstrated and for most of the same reasons. The public interface should be all you need to use a class. I apply that idea to documenting code as well. If I can't document a class such that anyone using it won't need to see the header or other source, then I probably need to improve the API to make it simpler.
The worst performance killer is to use one class per file convention. It's the easiest way to speed up things without using C++20 modules.
6:48 it used to be that "inline" keyword was about actually inlining functions, but nowadays the compilers decide what to inline according to various metrics. The "inline" keyword affects this metric, but a very small amount. The real effect of "inline" is related to ODR. But you need LTO / LTCG enabled in order for functions to get inlined across translation units - in that regard defining functions in a header helps, but not the inline keyword itself. And you should always mark functions with headers "inline" because otherwise you're looking at ODR violations and linker errors about "multiple definitions".
It's always been a hint, and not a requirement, as per the standard, but compilers generally try to inline things if you suggest it. Of course, with a high enough optimization level, things not marked inline will get inlined if the compiler thinks they should.
@@anon_y_mousse, my point is that the impact of this hint is extremely small. Use __forceinline (or the GCC's analog) if you really want something inlined.
It's not a myth and you did not get it right Cherno. Functions that defined in header CAN be inlined by compiler because definition of this function would be visible in each translation unit that includes that header and functions that only declared in the header can't be inlined because translation unit do not know body of the function. Linker can inline functions across translation units if LTO is enabled but that is incredibly slow. That's why unity builds rule XD
At work we use a lot of forward declarations reducing the amount includes. It is a pain having a code style change in a header file that triggers a waterfall of compilations.
that's what coffee and porches are for
I believe it is actually impossible to inline the function when it is in a different compilation unit. It would HAVE TO be done by the linker. So unless linker optimizations can do inlining, it is impossible. Not sure if linker does inlining or not.
Apparently, the linker can do it if you explicitly enable it, but it's slower.
@@theRPGmaster Slower, yes, but LTO is most definitely worth it if you're writing something performant.
How your visual studio look soo good , which theme is this
You probably won't read this comment, but I'd love to share the reason why some methods are implemented in the header file. Unreal Engine is a great example. The code is divided into modules that are dynamically loaded, such as DLL files. Modules that depend on other modules can access their header files, but the implementation (the CPP file) is only available during the compilation of the module that owns the header file. If a header file contains only the declaration for method M and another module includes M, it won't be able to inline this method because it lacks the definition. The definition is provided through dynamic linking. However, if the definition is in the header, the compiler can inline this method for the dependent modules.
More commonly used and smaller methods will likely have their definitions in the header file. Deciding to inline every possible method could cause your libraries to consume more memory since methods will not be shared. Thus, this represents a tradeoff between performance and memory usage.
He just keeps going and going and going and nothing will stop him until he got to the ground level lol. Love it
The curse of most freelance C++ developers, we're all hard headed and don't know when to quit
The Cherno is the GOAT
Const should always be used when possible. The compiler will often use it to further optimize the code.
Love this series, thanks much.
Bless you, Cherno, for being so innocent and using "Team Deep" so nonchalantly
Cherno with proper support coming for modules soon(TM) for C++, what are your thoughts on it? It claims it will reduce the messiness of inclues(which just copy paste whole files) from what I heard.
I do find myself wondering what happens if you inline a function which returns a private variable from a class
I sir I want all basic videos link
Really weird place to put the sponsor-segment, but I learned some usefull things today!
Thank you for your nice video and helpfull tipps. One word about defining functions in the header file: it's not only a matter of code size. IMHO i don't want not leak too many implementation details by my header files, especially if I'm writing library code. Implementation details in my header files can create unnecessary dependencies between my modules and make the use and refactoring of my library code much harder.
also, forward declaration in header files, so you don't include headers inside other headers, you avoid cycles and your compile times also get lesser
Holly bananas man! I did not about the mutable keyword in C++ and I have used for about 6 years.
Little question: I'm wanting to build my own GUI-app with a 3D rendering scene and a menu on the side. I've seen the ImGUI video from Cherno. Is it still one of the best options, for a beginner?
This kinda videos give me a lot of useful information. I mean this is better than all those C++ samples that are out there.
What will be first - private section or public is topic for holy war. Because in some cases I can open header file for see internal stuff (private) or in other cases it can be external interface (public). I my opinion if this sections have big bunch of fields and methods (like in this example) it just need separate on few classes. If class will be simple it don't different what section will be first or second I will see all header file at on single screen.
This kind of stuff is exactly why I've migrated to C# instead of C++. Not only are the standard libraries better but you have a lot less compile debt due to header files and can enjoy not dynamically allocating memory if you don't want to. While remaining very close to the hardware. JIT'd even. Not to mention the C++ standard library is garbage in comparison. I also am relieved to not have to deal with compiler flags et al. I also enjoy hotloading code, so I can live edit stuff as I am doing things. Instead of recompile and suffer going through to the point of where I need to test.
Well "better header files" would be using modules instead of header files buuuut clang and gcc support for them is still experimental and so is cmake support
So, what headers are used for in C++?
hey man, what font do you use? I really liked it!
Wait, isn't it better to pass variables by const reference if their values aren't going to be changed?
Generally, if the variable is expensive to copy (large amount of data or expensive copy constructor) yes. But if you have something that can fit in a register, there is usually no benefit and passing by value would just be simpler.
I've been playing with Unreal engine including trying to learn a bit of C++ and adding classes in code. I wonder if I did what you just did if it would list all the expansions in the actual unreal engine part, which seems to be pretty big. Maybe I'll try it.
It takes me 40 minutes to compile the engine from source.
This is very useful video because I was trying to learn stuff from a book and due to a small error was getting gaziliions of the same error and I had no idea why. Now I do. Thanks.
Hmm, the properties page for my project doesn't include the same as yours. There is no C++ entry so I can't try the preprocessor thing.
why getters such initialized() are non const?
Inline's original intention of helping the optimizer isn't really the same meaning these days, it's more just about having a the same definition specified in multiple translation units (aka multiple definition are permitted). The optimizer without link time optimization is going to have a reasonable chance of optimizing these if they are small and it thinks they are suitable, if they are not visible then it requires link time optimization / link time code generation.
Member functions defined in the class definition are automatically marked as inline, the inline keyword is meaningless.
You should typically avoid force inline it's nearly always the wrong thing, and you just hinder compile times and very rarely do you actually help the optimizer.
Nearly always is maybe a stretch. I feel like you should use it about as often as most compiler directives - when you're quite sure you should. Not sure it needs that qualifier
@@kippers12isOG the trouble is many people think they need it, but in virtually all cases they do not really know. I've worked on several engines where people thought they were smart and used always inline and when disabling always inline it had no noticable difference in runtime performance.
Do you have an opinion of STB libraries?
i love it. very good!
Do you know if its possible to output the preprocessed files for Unreal code ? I can't find the option
Thank you for the high quality content as always! I'm wondering what's your take on static code analysis tools. I've seen your sponsored video about PVS studio a while ago, but haven't seen it in use in any of your videos since. The question why I'm asking is because a lot of the topics in the code reviews can be automatically detected (and often even corrected) by these tools. Personally I'm using clang-tidy which is free and has a great number of checks available.
Pvs studio is absolutely horrendous.
It introduces more bugs than actual fixes for your code.
Wondering of what you think about Jonathan Blow’s new language Jai? His approach to create a successor of C++ as well as his general concepts for a programming language - for example with no header files, the amazing compiling speed, etc… .
Please point me to a video where he says its a successor of C++. I really doubt that. Its a different C/D, maybe Rust like compiler
Which font do you use?
"Team deep" We all know cherno is not talking about his table tennis skills dang
You need to make a video about const and mutable stuff that you did at the end.
With gcc without -Os the 'inline' functions in the header file are most likely inlined. It can speed-up the code.
Also it reduces the stack usage which can be important with embedded programming.
@@ArjanvanVught but increases program memory
does anyone know what vs theme is in this video?
For C++ it is better reducing the usage of preprocessor macros. In modern C++ we have more elegant alternatives.
why theres a unity icons on an obj files? hmmm
I think "the compiler more likely inlines functions in a header file" is just a myth. The actual compiler, after the pre-processor, doesn't even know which part is from the .h file and which is from the .cpp, it's just a huge post-processed text blob. But most header file function definitions are so simple that they get inlined; if they get too complex the linker forces the removal from the header file (double functions found) - so it's inverse logic that "header file definitions get inlined". Also, any public consumer of a class would pull class logic into its own code, which is just a bad design. In our company, any function definition (even the simplest) is not allowed. And we also don't stick to "every .cpp must have a .h file" -- mostly we have 1 header file for 1 class definition, and the class is split among 10-15 .cpp files, each small and focused on a single class functionality group.
"The actual compiler, after the pre-processor, doesn't even know which part is from the .h file and which is from the .cpp, it's just a huge post-processed text blob"
@@voytechj this is done with a #line marker that states the original file and a reference line number; the following lines are then determined by a delta and a lot of empty lines (because it uses less characters than repeating the #line marker); nonetheless the compiler doesn't "understand" what it means, it's text and a number that is printed when compiler errors are found; old compilers didn't print code locations, just the current error line and that was it
let me bring some pop corns, your every video is like a treat
theme?
Hm thanks for an inspiration. I've got some cool ideas.
What do you think cherno about CMake or Makefile?
I particularly hate CMake for many reasons: poor syntax, silent syntax errors, complex logic function names ... and the worst for me: need the same number of lines than for a Makefile ... to generate a Makefile :) I only use Makefiles. I made some macros that I included in all my projects (git submodules) then my main Makefile is tiny and is just made of a list of files to compile, path (to search them) and include dir (to search headers) and include libraries that can be found by the pkg-config command. So more compact than CMake. No more. I'm agree than Makefile syntax and rules are sometimes painful (when using code source generation since Makefile shall know your files not yet existing). Else you have scons a build system using python language I used it once, I liked it (the drawback is the have .o next to your .c files).
The correct word for headers is "abstraction". Headers are an abstraction of the class. In Java, you have interfaces (abstraction). That is the same as headers. However, requiring it for every class is a pain in the ass.
Hi cherno!
What about a series on Vulkan like you did for OpenGL? Anyways love your contents!!
vulkan is just a batching math lib for openGL... what do you want to see it wrap openGL?
When you declare a function but don't define it in a header file then the compiler _can't_ inline it. When you include that header file in another source file, the compiler doesn't have the definition of that function available for inlining. You can't expect the compiler to inline a function that it doesn't have an implementation for!
The compiler has no choice but to call the function. Only the linker, once it pieces all the object files together, could optimize that any better.
Interesting video. 1/ I hate classes with hundreds of public methods. Can I use destroy() method at runtime ? Can I call add_background() twice ? What happens if I call close_all() or set_init_false() before calling init() ? I prefer letting max 4 public methods to not let choice the user to call the wrong method (follow my white path). Need to split code into several classes. 1 Class = 1 idea. And to be sure the class does a single thing is to comment it. If you start writing this class does this and this and this ... this means something wrong in the design and start to split it. 2/ For cleaning my includes (reducing cycles) I use doxygen with the option for rendering graph of includes. I then remove manually cycles until having a spamming tree. I know some people does not like this because you can remove includes such string, map, vector in a file that need them because included in another header file but if you modify the last file removing an include, your current file may stop compiling.
Newer subscriber, so sorry if you've already talked about this in another video, but what about pimpl to keep your headers cleaner? I know, less chance of inlining, but curious about your thoughts. And to your commenters talking about modules: my opinion is wait unless you are Microsoft only. gcc and clang aren't there yet.
Im programming on C++ almost 4 years and still can't get used to writing variables at the end of the class, regardless of the level of protection, so I write them from above as it is easier for me. Oh that Java make so many damage....
Hi Cherno. Can i copy ur layer system?
21:15 so c++23 modules
It's not a myth, it's a C++ spec that implementations in header file are always inlined automatically.
No, this isn't for me. I want explanations, not opinions. When you are reading the public part of a class, you are reading an "API" ("application programming interface" in the old sense), it is a "contract" (in a human sense): the documentation directed towards any programmer *_using_* your class. The public part of the class should be extensively documented: purpose, what value its call returns and how it changes the state of the class, the purpose and semantics of the values of each and every parameter, as well as exceptions. The private parts contain notes by the programmer who made the class: if that programmers have to put the class aside and return to it after 4 months: _"where was I, what is the purpose of this loop?"_ When you reduce the size of a class by refactoring it into multiple dependent classes, you do it in order to reduce its size to a *screen-full.* That is: if your font becomes unreadable when you zoom out of the class/function/method, then it cannot be viewed screen-full. Somebody is viewing the screen, somebody reads the code. All explanations rest on the fact that *a programmer* is trying to orient himself/herself through the code. Programmers need to understand psychology.
As long as the file thats including the other file (imagine you include game.h) has access to the implementation of the code, it will inline it (at least in c). So if you include a header file with code, that code is always inlined. As simple as that.
That's not true. Not in C++ or C. The inline keyword has implementation defined impact on how likely it is your function will be inlined. In practice, it's effectively ignored for this purpose, because the compiler (at least thinks that it) knows better
inline keyword almost guarantees inlining. You can view the source of gcc or clang and inline dramatically increases the inline "score".
I hope when you look at the variables, you talk about the packing of them. I also wish you replied to comments :(.
Oh Lord.... if you ever saw my code, you'd probably leap through my monitor screen and strangle me.
A decent approach to clean up the mess in that game code and you explained very well why you would do it this way.
Also thumbs up for showing the processor output ;-)
I have a few addtiotional tips if you may be interested:
You could move all "Game" fields one or mulitple state classes/structs and let relevant codes in the Game class referencing it by the function arguments only. This way you easily see which functions requires, what type of states and therefore can maybe extract it further out.
Last thing regarding compile times, i have another tip i can share - but most people dont like it, because it requires good header inclusion handling: The entire game can be compiled from one single translation unit, instead of compiling different translation units. In the main.cpp or you main entry point cpp file, simply include all the required files like this:
// Sometimes you need to define some header beforehand
#include "game.h"
#include "math.cpp" // Entire math
#include "assets.cpp" // All assets data, fixed fonts, tile-coordinates, etc.
#include "game.cpp" // Entire game
#include "box2d.cpp" // Entire physics engine
int main(int argc, char*[] argv) {
// entry code, platform initialization game loop, etc.
}
This way the compile time can be almost instant, even on projects with 100k+ lines of code!
I do this since 5 years in all my C/C++ projects and it works great and safes me a ton of time.
Sometimes to make a library compile this way, it requires a bit of fiddling and some source libraries can´t work at all in this behavior, but thats okay.
So, I'm going to say the obvious that you should never, never, never, never include a .cpp especially for library code. A lot of code with use anonymous namespaces(or the static keyword in the c style way of doing things) to prevent dependency leakage for various implementation files. This is a very common and standard good practice for reducing collisions with other translation units and helping to mitigate ODR issues. If you do this however, you can get a crazy amount of dependency leakage. Be *very* careful using this unless you know *for sure* that the .cpp files won't leak dependencies. It is a real nightmare to debug, especially for things with simple names like "tmp" or "first" or "obj" which where meant to be private to an implementation file's own translation unit. Finding and tracing these is a nightmare, especially if your code analysis bugs out. Especially in bigger projects, I recomend you just don't do this. And thanks to how guards work on macros, you might not even catch some of these until you target a specific platform, and then you may have to dig through a large amount of code that you didn't add or that hasn't been modified in forever to trace the dependency chain and find the issue. If you do need to do this though, first I pity you and good luck, and second the -i flag in gcc is a lifesaver for seeing how the includes get generated for each translation unit.
@@Firestar-rm8df I never had any problems with my approach, as i mostly include single-file-header libraries - exceptions are box2d (50 cpp files) and imgui (5 cpp files), which worked after i transformed the original header file (box2d.h and imgui.h) to be "single-header-file" with an optional implementation block, that includes all .cpp directly in the correct order.
But you are correct, sometimes there are such libraries which are so bad designed that, when you include the cpp translation directly, you are screwed. Those libraries needs its own translation unit(s) unfortunatly :-( assimp, is a good example of a library that can´t by included directly as a source with my method - but i dont care, if i need assimp then it would be a offline processing tool and i am done.
Give us more raytracing!!!
Team Deep sounds a bit suss tbh 😅
the main problem is that headers exist in the first place
He is a Handsome man 😊
Better header file is the file that is not a header file.
Hello ! Here after 58 seconds of uploading 😂🙌🏻
Your Java example makes no sense. The thing that makes more sense is that on Java you were writing code for yourself and not other people.
I know for a fact the compiler can inline functions in the CPP. My reason for not putting function bodies in headers is to speed up compilation.
cool
This whole inlining thing is somewhat overmarketed, I would say. And I even think that it hurts unexperienced developers, because they start to think too much about whether to inline each and every function or not, and actually end up inlining almost everything. This is premature optimization. And in fact, in most of the cases, inlining is not going to give you any sensible performance boost. Just like using "++i" instead of "i++" will not speed up you program, eventhough "++i" is in theory faster than "i++". Remember that fundamentally in order to be inlined, a function has to be a hot and short piece of code in order for inlining to be sensible. For example, by default Java JIT compiler only considers a specific method for inlining if its both "short" (meaning that it runs fast, for example a function that prints to the console or loads an image is not "short", despite it potentially being one line of code) and has been called over 10k times. And despite all that, remeber 80/20 rule. 80% of performance bottlenecks usually sits in your choice of algorithms and datastructes that you use to implement the task. And remaining 20% are these tiny and specific details, like hot code inlining, data structure padding for cache alignment, and other stuff.
As an example, watch the recent video on this channel named "How I Made My Game Engine MUCH Faster...". The Cherno managed to double the fps by implementing a better algorithm for managing descriptor sets, no method inlining could ever give you that performance boost. This is a perfect example of how you should optimize.
So, to sum up, if you find yourself thinking too much about whether or not you should inline some method, then take a step back and realize that this should not be your main concern. Instead, delegate this choice to the compiler, and focus on your main task - implementing a unit of functionality. Later, after you finish your task, you can do perfomance analysis and perfomance tuning of that finished code, and it will give you the hints about these minor optimizations.
In the case of pre- versus post- increment, it depends on whether you're using an iterator class or a simple integer type. In the case of an iterator class, pre-increment will necessarily be faster because it'll not make an unnecessary copy of the iterator that'll merely be discarded anyway. With higher optimization levels, hopefully such things can be caught, but that requires link time analysis often because the iterator may be defined in another file and swapping post for pre won't be something the compiler can decide on its own here.
In the case of Java, an indirect function call won't be noticeably, if at all, slower than a direct function call because it's a virtual machine, not a directly compiled program, but we should be discussing C++ in which not inlining the trampoline will cause a second function call to be emitted in the generated code and that does have some overhead which if looped can cause problems in more than one way. For instance, cache misses when that extra function call overfills the cache, or segfaults when it smashes the stack, which is also a security concern.
While algorithmic and structural designs are the most important part of designing your application, you should realize that they are also a part of the optimization process. It takes practice and knowledge of the underlying architecture, but you can prevent certain slowdowns by being aware of where they might occur and thinking of how to avoid them as you write. I've heard various statements on optimization and "premature" optimization being bad, but none of the arguments hold up as some optimizations quite simply can't be made with a mere change of the structure or algorithm used. My take is that you should always think of optimization, from the start, while you're coding, and even once you're done.
@@anon_y_mousse as for pre/post increment, there is an empirical evidence that shows absolutely no performance difference between pre/post increment when using STL Iterators actually without LTO enabled, and this debate can go forever, but there is really no much difference between the two, and this is only a matter of preference. Usually your program spends about 0.0000001% of its runtime doing pre/post increments, so it’s not going have much effect.
As per Java, nowadays it is actually compiled to machine code at runtime by JIT and is recompiled every now and then while the program runs to optimize for the specific scenarios in which the program runs. And in some cases java programs run even faster than corresponding c++ programs. Why? Because compiler knows how to optimize better than even most experienced programmers.
And about inlining, I agree that you should inline some very simple functions like accessors/mutators when you write them, but again, compilers are very very aggressive at inlining, and they will do it for you anyway when you build for distribution
lol
;)
Also please please please if you're noticing you're not very consistent with your formatting; just use clang formatter. Nothing worse than poorly formatted code you have to read all day.
When it comes to inline, you can reference it with cppreference, following this link: ` w/cpp/language/inline `