The Inline Keyword in C.

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everybody today we're talking about the inline keyword in c [Music] today's video is inspired by adam who asked if i could do a brief video on the inline keyword and see what it's for how it works and how it should be used or can i recommend a good source for a clear explanation i've been reading up on it but i can't get a clear understanding myself so i was thinking today we just do a quick little dive into the inline keyword and see take a look around and see what it is how it works and what it's good for as always source code for this video is available through patreon thank you to all of you that support this channel all of your subscribers all of you that share these videos with your friends thank you for all your help thank you for supporting this work now let's get into the code now this example is going to be just a little bit exploratory i haven't done a lot of prep beforehand to see what's going to happen i just thought let's just jump into it and try it out but i did set up a few files and i just want to take you through them really quick so i've got this test.c it's a very simple program that basically just takes in so a simple program takes in an argument one argument presumably a numerical argument it has another ret return variable whatever you know result variable um let's change that to be a result i like that better than rat it's the great thing about these exploratory type stuff is that i can change it up as we go okay so i've got this and i have two fairly small functions here okay so these anytime you're talking about inlining things we're talking about small functions typically that's typically where we're going to see a difference i also have two more files nums.h and nums.c right now they don't have anything in them i just i know i'm going to need them later on and so i have them here just for when they come in handy and then i have a make file my makefile is going to basically go through this may look a little bit noisy to you all it's going to do is compile my test program using clang but it's going to compile it at different optimization levels at the default optimization level so no optimizations at 01 and o2 so that's basically what you're seeing right here it's also compiling both the executable binaries in these different levels but it's also generating assembly code so you can see these are the dash s options now i recently did a video on assembly code why it's handy to learn assembly language in 2020 and i mentioned that video that sometimes assembly comes in handy and today is that day we're going to talk about assembly we're going to use assembly to see what our compiler is generating and of course in here you notice i am linking with nums.o but like i mentioned before this has currently no code in it it's just an empty object file so this isn't currently changing anything i just wanted to have this in place because it's going to come in handy later on we also have our clean target down here which of course just blows everything away when we're done okay so let's go back into test.c and just look at what we've got here so one thing i want to make clear in this video is that there is a difference between what it means to inline a function and the inline keyword in c and that's what i want to tease apart today so inlining a function is simple all it means is that rather than actually making the function call we're going to take the code for the function and just stick it in line in the generated code so we're not actually making a function call we're not actually pushing anything on the stack we're not actually returning a value we're just inserting this code into the generated assembly code now let's take a quick look and see what happens in my example here if i compile it okay you can see i've got a bunch of files and we should probably talk about what this is actually going to produce if i run test zero so this is oh oops forgot i need an argument and i didn't do any argument checking so here our function is returning 2 up here and then here this is going to take the maximum of whatever the function 2 returns and the argument so let's do test 3 we get 3 test five test one we get two okay so this is working the way we expect it to no problems and right now if i run test one and test two i should get the same behaviors all the way around nothing really changes if we look at the assembly code you're gonna see that things are a bit different now i don't expect you to understand everything you're seeing here this isn't a deep dive into assembly and what everything is doing i just want to give you a rough idea of what you're seeing here so the first thing you're seeing here are our functions so here's the max function it's going to come down here to about here so this is basically this function for our max code up there that takes two integers and decides which is greater of the two and then down here you have the two function so this code basically just returns the number two and then down here below you have main so main basically is this whole thing down here it's a lot of code for something that doesn't do very much i know but you also notice down here that we have some function calls that we're making like our atoi call here our call to two our call to max so these function calls are actually happening so right now they're not being inlined at all now another thing to note is that we got about 86 lines of assembly code for this program now if we look at our optimized versions you're going to notice that with o1 we are looking at 64 lines of assembly code at 02 we're looking at 63 lines of assembly code so about the same not much difference there let's take a quick look at o1 and one thing you're going to notice is the max function is still there the 2 function is still there but if we come down to main you're going to notice that we're still calling max so that function call is still there but you notice that the call to 2 is gone basically it's been replaced by this line right here the compiler was smart enough to say you know this function always returns the same thing and it's a dumb function it returns the number two so why am i pushing stuff on the stack and actually calling this subroutine why don't i just stick two into the code and that's exactly what's happening right here and then if we jump over into opt2 you notice again we still have max we still have two so that code's still there in our binary and if we look down at main you can see that we're even doing a little more aggressive inlining here where basically we still are calling atoi but now there's no max and there's no two because these are both really simple functions and again the compiler just realized you know i don't need to actually call these functions i can just take the code from these functions and inject them into the generated assembly code which makes things faster and it makes things smaller and that's one thing that's interesting about this topic is that compilers are funny machines they run they use a lot of different heuristics there's also a lot of different semantics and policies in the different c standards and so behaviors may also vary like if i switch to gcc i may see slightly different behaviors because we're definitely getting into the part of the compiler world where things can vary a lot from one compiler to another so this gives you a rough idea of what inlining is but you also might be thinking you know jacob you haven't actually written inline anywhere in your code neither of these i they're getting inlined by the optimization engine but i didn't actually write inline anywhere so let's inline a function and let's just say inline into like this so the way people typically think about inline is that inline is a hint it's a it's a hint to the compiler saying hey you should inline this at least that's how we usually think about it but let's see what really happens on my machine when i do this if i compile my code you notice now i get an error which may seem a little weird i just basically gave you a hint to inline this function and one of the interesting things about hints is that hints the compiler is under no obligation to actually take this hint but would prefer if it would still compile my code now what's happening here is a little interesting is that inline has taken on a slightly different meaning what this inline keyword is actually saying in this case is hey this is the alternative implementation of this function to use if you want to inline this function okay so for this to work there's basically two things i can do first thing i could make it static in line let's try that first so static inline we've talked about static before a little bit is basically just saying hey this function only exists in this translation unit so don't go looking for some alternative implementation of two this is all you got so if i make it static inline now let's take a quick look at what we see here so if we go into our unoptimized version you notice we still have max we still have main two got bumped down to the bottom which is kind of fun and then up here in maine did we call the function yes so we're still calling the function so i told it to inline it and it ignored this hint altogether it just said heck with that i'm not going to inline your function thanks for the pointerman not really doing it but for some reason it did bump it down to the bottom probably because this function is now static and so maybe the compiler is organizing static functions down at the bottom for whatever reason this is just a good illustration of the fact that this is a hint it's a hint it's not actually forcing the compiler to do anything and the compiler is definitely asserting its independence here okay so what happens if instead if i remove this static let's say i don't want it to be static but i actually want to use this alternate behavior that's where this numbs.h and nums.c are going to come in to play so for this to work basically what we're going to do and i just want to show you really quick how this works is we need to have another function in another translation unit called two okay so this is the way you can think about this is that this is the official implementation so let's say i have some i don't know let's make a noticeably different implementation of two let's have it actually print something out okay and let's add some extra code in here i'm just messing around here there's nothing about this that's significant i'm just creating basically a new version of two that actually returns 30 and the reason is just that i want to be able to tell if it's calling one function or the other so we have this module here it's pretty simple but all it's doing is it's replacing this two function so now what you have is you have one translation unit this nums.o that has one implementation of two and you have test.c which has its own version of two and then i just wanna show you how this works when it when we actually compile so now if i compile this down now it doesn't give me an error anymore now it is willing to take just the inline into and what it's going to say is if the compiler decides to inline this to function it's going to use this version if it decides not to inline this function it's going to use the original version okay so let's first just try this out and see what happens let's pick a number between 2 and 30. if i run it with 01 and o2 everything is it's calling the original the broken version of 2 and it's returning 30. but you notice if i use the version that was optimized with o2 it is actually replacing it with my new implementation so it's actually switched implementations of the function and i guess this just gives you a little more flexibility in writing your code so if there's an actual different implementation that's going to be more friendly to inlining but maybe it's not quite as readable maybe it makes sense in your program to have two alternate implementations of the function so i don't usually use this in my own programs mostly because most things are fast enough often the compiler gets inlining right without me having to tell it to do anything and i really don't like having multiple implementations of the same function because that makes my code hard to read and hard to understand and hard to maintain and it just opens up opportunities for bugs which i'm usually trying to avoid at all costs but hopefully this gives you an idea of how the inline keyword actually works but one thing i do hope you can see here is that just adding inline to this function didn't actually increase its likelihood of getting in line which is kind of annoying let's say we actually do want this to always be in line and we do have one option here i'm going to show you and that is we can add an attribute to this function i believe this works on both clang and gcc oh i need two [Music] you know i've never understood why you need the two parentheses i need to dig into that one of these days if anyone knows please let me know down in the comments because that could save me a little bit of time appreciate it but so what we're doing here is we're tagging this function and saying hey uh no i really mean it always inline this function people who use this a lot will sometimes pound to find this to be something like force inline or something like that today i'm just going to leave the attribute open so you can see everything that's going on here but now if we compile it you notice it still compiles just fine but let's take a quick look at what's going on so if we look at our assembly code you notice we still have max we still have main down here in main though you notice that we have inlined this function okay so at least clang in this case is respecting the always inline attribute even if it's not respecting my inline hint keyword now if we jump over to our optimized level 1 version you notice again again the function has been inlined and if we jump over to optimization level 2 once again the function has been inlined now interestingly enough you notice that with this always inline thing it didn't bother with that original version that original broken version of two that return 30. none of that happens anymore now it gets my version my local version every time okay now when would you be interested in using this always inline functionality like the only place where i typically use always inline is in some of my embedded code so a few years ago i was working on a library that was handling gpio pins for an embedded microcontroller basically i just wanted a generic general purpose library for managing inputs and outputs and toggling pins high and low pretty simple stuff i was just trying to make my workflow and my code base a little easier to read the problem i was running into is that optimizations were changing timing a function call takes longer than an inline pin toggle and pin toggles are pretty simple not quite as simple as this two function but they're pretty simple so what was happening is code that would run just fine timing wise without optimizations was becoming much faster with optimizations and that was actually breaking my system because timing matters in a lot of these systems when you're when you have a microcontroller that's talking to a radio or a sensor or some other component sometimes timing being off by just a few microseconds can make the whole thing break and so that was the problem i was running into and so i definitely use this always inline attribute to say look just always inline the stupid thing because i need it to be consistent in that case i didn't really care if it was inlined or not i just couldn't tolerate the variability that came from having it sometimes inlined and sometimes not inlined now of course the trade-off when you inline functions you get faster code but you also lose some debug ability if you ever try to trace through this code all of a sudden gdb is going to get a little confused about what it's seeing now this isn't everything there is to know about the inline keyword or inlining functions but i hope it's helpful i hope it gives you a good idea of how inlining works and what the inline keyword actually does also if you really care about inlining your functions i hope this always inline attribute comes in handy just keep in mind that these things vary by c standard they can also vary by compiler but i hope this helps you understand what's going on i also hope this helped you see why assembly language can come in handy even in 2020 i always like to help you get under the hood because i think it helps you build a stronger foundation which in the end is going to make you a much stronger programmer if you like this video i've got others like these also subscribe to my channel if you don't want to miss the next one remember source code is available through patreon and until next time i'll see you later
Info
Channel: Jacob Sorber
Views: 20,922
Rating: 4.9487777 out of 5
Keywords: inline keyword in c, inline keyword, keyword, keywords, inline function, always inline, inlining functions in c, inline function in c, c programming, tutorial, programming tutorial, function attribute, gcc, clang, c tutorial, c keyword, inline c function, c keywords, compiler, compiler optimizations, compiler inline
Id: t6Jfrmdg5rk
Channel Id: undefined
Length: 16min 17sec (977 seconds)
Published: Tue Aug 18 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.