CppCast - C++ Game Development at Ubisoft

Episode Date: December 8, 2016

Rob and Jason are joined by Nicolas Fleury, Technical Architect at Ubisoft Montreal, to talk about the development and performance tuning techniques used at Ubisoft on games like Rainbow Six Siege. ... Nicolas has 13 years of experience in the video game industry, more years in the software industry in telecoms, in speech recognition and in computer assisted surgery. Technical Architect on Tom Clancy's: Rainbow Six Siege, he is one of the key Architects behind some collaboration initiatives at Ubisoft and was also Technical Architect on games like Prince of Persia. He presented at CppCon 2014 "C++ in Huge AAA Games". News Bjarne Stroustrup - Keynote Meeting C++ 2016 Investigating Radix Sort How to use PVS-Studio for Free Nicolas Fleury Nicolas Fleury Links Ubisoft Montreal CppCon 2014: Nicolas Fleury "C++ in Huge AAA Games" CppCon 2016: Nicolas Fleury "Rainbow Six Siege: Quest for Performance" SG14 Group CppCon 2014: Mike Acton "Data-Oriented Design and C++" CppCon 2014: Jeff Preshing "How Ubisoft Develops Games for Multicore - Before and After C++11" CppCon 2016: Nicholas Ormrod "The strange details of std::string at Facebook" Sponsor JetBrains

Transcript
Discussion (0)
Starting point is 00:00:00 This episode of CppCast is sponsored by JetBrains, maker of excellent C++ developer tools, including CLion, ReSharper for C++, and AppCode. Start your free evaluation today at jetbrains.com slash cppcast dash cpp. And by Meeting C++, the leading European C++ event for everyone in the programming community. Meeting C++ offers five tracks with seven sessions and two great keynotes. This year, the conference is on the 18th and 19th of November in Berlin. Episode 81 of CppCast with guest Nicholas Fleury recorded December 7th, 2016. in this episode we discussed the performance of radix sort then we talked to nicholas flurry technical architect at ubisoft montreal
Starting point is 00:00:59 nicholas talks to us about some of the game dev techniques used by his team at Ubisoft. Welcome to episode 81 of CppCast, the only podcast for C++ developers by C++ developers. I'm your host, Rob Irving, joined by my co-host, Jason Turner. Jason, how are you doing today? All right, Rob, how about you? Doing good. Getting ready for the holiday season. You? Well, yeah, there's that. And we got our second snow of the year and lots of cold weather tonight. I think I still got a ways to go before I see anything resembling snow.
Starting point is 00:01:50 It's supposed to be negative four when we get up in the morning. Oh, wow. That's chilly. That sounds chilly. Okay, well, at the top of every episode, I'd like to read a piece of feedback. This week, Bell wrote us an email saying, Hi, Rob and Jason. Thanks for the great podcast. It's really opened my eyes to what's out there in the world of C++.
Starting point is 00:02:12 I'm a university student hoping to work full-time on C++, preferably on embedded systems. Having listened to the podcast with Dan Sachs and Odin Holmes, it seems this is very much possible. However, everywhere I've looked so far always ends up being some C++ pasted on top of C. Would you or any of your listeners know of or know of how I could approach this open source libraries, courses, development boards, etc. As I feel like I might just have to settle for C. I'm really sold on C++, especially after Jason's talk at CppCon. It would be a shame to not get to practice any of it professionally. What do you think about this, Jason? I actually had someone ask me on Slack if I had any advice for getting started in embedded C++ kind of stuff,
Starting point is 00:02:56 and I didn't have any direct answer for that, so I posted it on Twitter and ended up with actually a pretty long thread of people discussing this on Twitter for what development boards are good to get started with. So maybe I could find a link for that, and that's something we could share in the show notes. Yeah, that's a good idea. I'm sure Odin and also maybe Jackie from a couple episodes back probably have some pretty good advice about getting started with embedded or robotics.
Starting point is 00:03:22 Yeah, and there's a few other guests we've had on, too, that do embedded stuff in their real jobs, and they responded to that Twitter thread also. Yeah. Well, we'd love to hear your thoughts about the show. You can always reach out to us on Facebook, Twitter, or email us at feedback at cpcast.com. And don't forget to leave us reviews on iTunes as well.
Starting point is 00:03:41 Joining us today is Nicholas Fleury. Nicholas has 13 years of experience in the video games industry, more years in the software industry and telecoms and speech recognition and in computer assisted surgery. Technical architect on Tom Clancy's Rainbow Six Siege. He is one of the key architects behind some
Starting point is 00:03:58 collaboration initiatives at Ubisoft and was also a technical architect on games like Prince of Persia. He presented at CppCon 2014 C++ in huge AAA games. Nicholas, welcome to the show. Yeah, thank you. You know, I've got a question about working on something like this, like Tom Clancy's Rainbow Six Siege.
Starting point is 00:04:18 How involved is the author in making sure the game meets his vision or something like that? Well, Tom Clancy is dead now, so he's still working. Yeah, he is. I did not realize he was dead. That's unfortunate. When did he pass away? I can confirm that. Yeah, I have to look that up.
Starting point is 00:04:44 I'm supposed to be sure, but I'm pretty sure. Yeah, in 2013, October 1st. So he was not involved in the development of the game then? Of course not. But he wrote
Starting point is 00:04:59 the Rainbow Six idea and books. So the game is kind of going back to part of his vision of what Rainbow Six is supposed to be. So really exploring the ideas of all these special
Starting point is 00:05:15 units that we have across the world and different countries and the different mentalities of each of these units. So it is fair to say there was someone involved in Tom Clancy's work who was also making sure
Starting point is 00:05:32 that the game kind of met the vision, I guess you will say, of the author? Well, I guess at that point it's owned by Ubisoft, the Tom Clancy brand. So it's really up to us to respect that legacy
Starting point is 00:05:47 and what the brand is standing for. And it's always tricky because different people have different visions of what the brand is. But one year later, after releasing Rainbow Six Siege,
Starting point is 00:06:03 a game is very popular, we have over 1 million players each day. These are never-seen numbers ever for a Ubisoft game. One year after release, we're really happy to have that in our hands. That's great. I have some questions about your background, but I might save them for later in the interview, actually. So let's get into the news. We have a couple articles here. And feel
Starting point is 00:06:32 free to comment on any of these, Nick, and then we'll start talking to you about C++ game development. Okay? Perfect. Okay, so this first one is the Meeting C++ conference just happened. Was this a week or two ago? I can't remember the date off the top of my head, Jason. I think it was more than two weeks ago, but I'm sorry, I can't remember either. Okay, well, it looks like they are posting some of the videos. They posted the keynote from Bjarne Stroustrup,
Starting point is 00:07:00 and I think they'll probably be posting more. I can't remember if they're like CppCon, whether they post every single video, but I think they'll probably be posting more. I can't remember if they're like CBP Con, whether they post every single video, but I think they do. Oh, yes. He's posted a lot of videos so far. There's a lot of lightning talks up. Many of them are very good. There's some fun ones. Yeah, definitely.
Starting point is 00:07:18 Like Michael Case basically doing a poetic reading of the standard. Yeah, I need to watch that one still i heard good things about it though yes well uh this keynote from bjarne it's always uh worth watching and seeing his latest thoughts about uh the development and progress of c++ so i'd highly recommend this one and i'll put the link in the show notes did you have a chance to watch it jason about half of it okay it seemed uh fairly optimistic sounding um and i there's you know i called out one thing which i'm guessing bjarne has put on a slide before but like 11 minutes in he lays out exactly what the zero overhead principles of c++ are on one slide and i'm like i need to go back to that
Starting point is 00:08:00 slide and make sure i take no note of that. How about you, Nick? Did you have a chance to watch any of these Meaning C++ videos? I watched the one from Bjorn, but it was similar to what he showed at CppCon, but the Q&A was interesting. It was like 40 minutes just for a Q&A. Yeah, they had a good amount of Q&A at the end, and a couple of good questions did come up, I think.
Starting point is 00:08:23 Yeah, I didn't make it that far, unfortunately. The next one is Investigating RadixSort. And this is on the ProbablyDance.com blog, which is an interesting blog talking about C++ programming. But he's going into a
Starting point is 00:08:39 comparison between RadixSort and StoodSort and putting out the claim that radix sort is significantly better with larger amounts of elements of an array, of a stud vector, I guess. Yeah, I mean, I've never heard of this type of sort before, and I wouldn't even be the right person to try to describe it, but it was an interesting article. Yeah, I definitely don't think I had heard of radix sort prior to reading this article. Well, considering his base case here is how to sort the enemies by distance from the player
Starting point is 00:09:14 with the enemies that are currently engaged in battle with the player coming first, I'm wondering if Nicholas has dealt with anything like this. Well, I'll be honest, The article that was really interesting to me because I really learned about Reddix sort. I'm sure that some people on my team know about it. But yeah, to be honest, we use a steady sort a lot. But yeah, it was a really interesting article. Well, I had already...
Starting point is 00:09:43 I mean, it's a novel sorting technique to me, but I had already heard that there were some options for, like, taking, you know, the fast sorts, like quick sort, and if you actually, like, subdivide it into other things and then do insertion sorts on the smaller sets and whatever, then you can get better performance that way also.
Starting point is 00:10:00 And I didn't know if Stood's sort did any of those tricks. I still don't know. I mean, I don't remember, I don't remember him mentioning that at all in this article, but so there's also that, I guess. Yeah.
Starting point is 00:10:11 And one of the things that he shows at the end is instead of just using, you know, just radix sort or just std sort, you could have your own sort where if it's a very small group of elements, then you could use something like insertion sort, a medium number, use std, and a larger group use radix sort.
Starting point is 00:10:28 Okay. That seems like a pretty good technique. Yeah, and it was also important to note that the radix sort is not great if what you have is almost sorted. And in a real-life situation, that might be your case. That you are just
Starting point is 00:10:43 adding stuff and you want that is and something that is already sorted so but it's really interesting tool to to know about yeah yeah okay so this next one uh is another article from pvs studio we've covered quite a couple articles from them in the past and this one's kind of interesting. They're trying to come out with a new free licensing model that they're aiming at small teams, indie teams. And the way they're going about this is kind of interesting. I'm not sure if I've seen this before, where they're basically requiring you to add comments to the top of your source code if you want to use the free version of the license. Have you seen this before, Jason? I mean, I've seen the article before. I haven't seen this method before.
Starting point is 00:11:31 I mean, they're essentially asking you to put a tiny little advertisement at the top of every file if you're an open source developer. Two-line comment. Yeah. And, you know, I think I'm okay with that. I'm probably going to do this in ChaiScript. I definitely understand their motivation because they kind of point out, well, we could have just done an open source license, but there are certain companies that work on open source projects full time, but they're like large development teams. I'm thinking of things like QT, where there's companies that work on QT for a living and they could afford to buy a license. Why should they get something like this for free?
Starting point is 00:12:08 So they're not targeting just open source. They're targeting smaller projects. Those smaller projects can be open source or it could be a small, maybe you're just a single person making an app and trying to sell it. You could make use of this license too. Yeah. Yeah. So that's pretty cool. We'll put the link to that in the show notes as well um yeah so nick let's
Starting point is 00:12:32 start talking about uh video game development um do you want to talk a little bit about some of the performance techniques that you have developed at ubisoft that might make sense for C++ developers in general, not just game developers? Yeah, but let me first, I can start to explain some key differences. We have a lot of programmers. Ubisoft Montreal is the biggest studio in the world. We are like 3,000 people. In a project, we have a lot of people. Right now, if I'm sending an email, if I send an email to all the programmers on my team, it's almost 200 people. Of course, not everybody
Starting point is 00:13:12 is a programmer, so let's say 150. So, we tend to prefer performance to ease of use in different situations. So, maybe some simple example or Array class, for example, by default
Starting point is 00:13:28 Array class is like our SEVector class. It's not, by default, keeping the order of elements if you remove an element at the middle. By default, we take the last one and put it there. But if you want to preserve your order, then you need to opt in and say, okay, that array, I care about the order or you call a different function to remove that element to say okay removeAt. It's called removeAtSlow in our case. Another example is our smart pointers. We noticed in the past we had no
Starting point is 00:14:02 arrow operator like you have for a typical smart pointer. But people were using the arrow all over the place in a single function. But the problem is that the smart pointer was very similar to a share pointer or a weak pointer. You have a bucket somewhere with both the soft ref count and the hard ref count. So it doesn an indirection. And each call to that arrow operator was doing the indirection and reloading that bucket to get the pointer. So we remove that and say,
Starting point is 00:14:35 okay, you just need to call get object, call get object function, and then you use that object. So it's a minor inconvenience, but this is the kind of choice that we make. We make these kind of choices that we make we make these kind of choices that you will not make let's say in the standard library
Starting point is 00:14:49 and I don't understand why but in our case there's that preference to choose performance so that's one difference I think if we use in the game industry there's been a talk, a keynote from Mike Dacton a few years ago,
Starting point is 00:15:06 which was maybe a bit controversial in its approach, but it's data-driven programming. I'll be honest, data-driven programming is maybe the work of a minority of programmers in the game industry, maybe mostly graphic programmers, maybe physics programmers. So it's really useful when you work with a lot of data. So the idea is really to structure your data in a way that is optimal according to what you do with that data. So it can be counterintuitive
Starting point is 00:15:38 because you are writing your classes much more like C-struts and not in a way that is human will perceive the data, but really in a way that is fine according to your algorithms. It can run over 10 times faster just by doing that. So there's no debate when you care about the performance. You just have numbers and then there's no debate that When you care about the performance, you just have numbers, and then there's no debate.
Starting point is 00:16:05 That's what you should do because it's so much faster. So the binary curve programmers is doing that, but this is definitely a technique that is very important. So the 10% of our code that is running 99% of the time, it needs to do this. Otherwise, we're not shipping a game like rainbow six at 60 FPS another thing we do is trying to avoid the heap and then I feel like what I'm saying is pretty common and I'm repeating the obvious but even today if I look at the
Starting point is 00:16:39 standard library components and new ones there's often it's often not designed to avoid using the heap. So, very interestingly, I use, I think, daytime functions and was surprised that they were using the heap. And often people from the SG14 group, so people caring about performance, of course the gaming industry also are frequently trading these kind of domains. We often get the same answer, you can use your own locator if you want. But it's often not true. In the case of std function, the allocator was even decided to be removed recently.
Starting point is 00:17:21 And even there it was, I tried to use it and it was really messy and that's why I'm a court of a proposal be removed recently. And even there it was, I tried to use it and it was really messy and that's why I'm a court of a proposal to add in place function inside the standard library. So in place function has the exact same interface as SD function but instead of using the heap it will not compile so then you can just you have an additional template argument to control the buffer that is inside it. And that small object buffer that you have, typically in an std function implementation,
Starting point is 00:17:55 this is not in standard, but every implementation is doing that. You have the same thing in std string. And there was a great talk at CppCon, a guy from Facebook. His name was Nicholas Ormond. Ormond. Ormond. Ormond. A great talk, and he was discussing the implementation of the string class in Facebook,
Starting point is 00:18:19 and also in the end, the std string implementation that we have today in GCC. But again you will want an ill-placed string. What about when I never want to use the Ipsh? Then I should have a subclass to expand the small string optimization buffer. So all these kind of things I think the SG14 group will probably help to add in the standard library. And the most obvious example is of course to have something like a small vector or small array like Schindler presented at CppCon and I presented also two years ago at CppCon called in place array so these kind of things you want but you want that
Starting point is 00:19:12 small vector to derive from a more like std vector like class but we cannot do that with the current std vector So I don't know if it will happen. Do we add two classes and SDD vector becomes kind of deprecated because you have another class competing with it? I don't know. But there are a lot of things around all of that to do in the future in the standard library, I think. So you talked a little bit about SG14 there.
Starting point is 00:19:45 Do you want to talk a little bit more about what your involvement has been with that group? Well, I was involved with call cook as a quarter for the place function proposal, and I want to write something to add explicit init lists in the standard library, this is five lines of code. It's just that the integer list right now,
Starting point is 00:20:10 it's very greedy, and there are issues where basically right now we have to ban it in the code. You cannot add... If you add a constructor with taking an integer list, it's pretty much compatible with a templated class, with a template class that will have any constructor other than this one taking pretty much any simple types. So if you have a
Starting point is 00:20:40 constructor taking a boolean and you add a constructor with initials or lists, if someone is passing false using brace in isolation, well, the false will be converted to a float if this class is templated with a float, and it will just call, yeah, it will not call the constructor you expect. So you add a constructor that will be called in unexpected situations. And the fix is very simple. It's just to add a wrapper over initial list, call it, let's say, explicit initial list,
Starting point is 00:21:16 and you just use this one instead, and it will be chosen in the reverse order. It will be chosen last instead of first. So yeah, all that to say a five-line code class that we could add in the standard library. That's interesting. I had a conversation recently on Slack with some other developers
Starting point is 00:21:38 that perhaps explicit construction should be the default and implicit be an option instead of the way it is today for single parameter constructors. And I hadn't really thought about how initializer list plays with that, but you're kind of saying initializer list
Starting point is 00:21:56 is kind of like the ultimate worst case for implicit construction. Yeah, I was very disappointed when I realized how it was. I contacted Patrice Roy, which is on the committee, because I was like, what the hell is this?
Starting point is 00:22:14 We did that again. Again, implicit winning by default and explicit. So, yeah, I was a bit disappointed. Yeah, I do think explicit is better than implicit. so yeah I was a bit disappointed yeah I do think explicit is better than implicit this is something I really
Starting point is 00:22:30 like in Python I think it's part of the language design philosophy but yeah we have to live with what we have yeah I assume it was done this way because they want to well actually I'm not maturifying or really but I guess it was done this way because they want to... Well, actually, I'm not maturifying or really, but I guess it's to be able to use a shortlist
Starting point is 00:22:51 to be able to pass anything and forward a bunch of arguments to something else you're calling. It's just that, to me, the use case of just synthesizing a constructor sounds to me like more it's more frequent I don't know but yeah in that case we have something that is very very implicit that's for sure
Starting point is 00:23:14 so you are using this wrapper that you're referring to I haven't started to use it to be honest but yeah I will introduce it at the same time as I will say to people that they can use brace
Starting point is 00:23:29 installation, because to be honest, nobody's using brace contractors yet. We just switched to Jaws 2 2015 or 2012 before. But yeah, so my I'm sending a tip of the week inside Ubisoft each week, and there will be one on that, so I'm sending a tip of the week inside Ubisoft each week,
Starting point is 00:23:46 and there will be one on that, and I will talk about insertor release at the same time, yeah. Yeah, that's, well, I mean, it's water under the bridge at this point, I guess, but the Visual Studio 2013 braced initializer list had some interesting bugs that I was getting memory corruption stuff with when I first started utilizing it, but you skipped that.
Starting point is 00:24:08 So you're fine. There's, there's something really funny about social list constructors is that the way they were introduced, they were introduced in some classes in ways that there's no way I can do it in my code. Take for example, std string. If you pass std string and you use the brace in it, you pass tree and then a character.
Starting point is 00:24:34 If they will have introduced brace constructors at the same time, well, let's say before introducing the initialized constructor, that way they will have break existing code. So they kind of, it's like the standard library cheated by introducing feature and using it at the same time. Well, two features at the same time and using them at the exact same time. While in my case, you know, some people could use my string class, pass tree in the character, and I cannot add a general disk constructor. Otherwise, I'm changing the behavior of my current code.
Starting point is 00:25:18 So this is also something interesting, that the Stanley Book Library is doing something I cannot do in my code. Interesting. So your talk at CppCon this year was about some of the performance techniques you developed while working on the latest Ubisoft game. Do you want to talk a little bit about some of that performance tuning work? Yeah, so I presented a bunch of tools. I discussed that we typically would profile with profiling tags that would be clear manually in the code. We have our own systems to dump that as efficient as possible. I didn't want to detail into the implementation of that system. That could be for next time I guess. One tool where
Starting point is 00:25:55 I've shown I think everything needed to implement it is our array analyzer. So the idea of the array analyzer is to know for all the arrays, which is our stdVector-like class, we can know for all of them, the entire code base, all the statistics about them. And the idea is to identify them by something that is very close to their declaration and the code so that is to say okay if I have one SD vector now one array declaration I want to have one bunch of statistic for that specific declaration and I was showing I was looking at the address the caller inside the array constructor to get something that is very close to that, to an ID that is 1-1 with the declaration and the code.
Starting point is 00:26:53 And that, yeah, with these statistics, then you can put proper reserve size or add in place array or small vector, whatever you call it, and different places. So we're able to make optimization to all our arrays in the entire codebase in a very efficient way, very interesting way. So that was one tool I thought was interesting. I discussed other techniques and I've shown two very simple lock-free containers and the point of these of what I was showing is that the two lock-free containers were really simple one of the one was a lock-free queue and oh it was basically fitting on one page of code if you put it all together and and you search Log3Q on the web
Starting point is 00:27:46 you'll find something much more complex but the truth is that we're able with something that simple to ship a game like Rainbow Six Siege and then I presented the the Log3 pool because typically in the game industry I know actually we use Wait3Q combined with a buffer so I wanted to show another approach that is that has no memory overhead that is more like a typical pool but is at the same time not free so I showed that as well with again pretty much the entire implementation on the slides and it's not that complex as well.
Starting point is 00:28:25 So that's kind of the kind of thing that I've shown in my talk. So lock-free containers are notoriously difficult to get correct. I was wondering if you have any tools that you use to validate or verify that your lock-free queues are doing the right thing.
Starting point is 00:28:42 No, I don't have any tools. I think what we do is we write simple code. So that's pretty much the point of what I've shown. The lock-free queue is simple. The lock-free pool is simple. Then I wrote actually
Starting point is 00:28:57 lock-free equivalent of shell pointer and weak pointers over that. even these smart pointers shell pointer and weak pointers over that. Even the smart pointers, that was a bit tricky. We had actually one crash per day
Starting point is 00:29:14 with that. It's not much, one per day with one million players. We look at the code, me and another guy, we look at the code for half an hour, like, man, my head hurts. What is wrong?
Starting point is 00:29:29 And at some point, we say, these two lines of code were stretching our mind, thinking art could be wrong. We just inverted them, and we never had the crash anymore. So that was it, but I don't know. I don't know what it was exactly. The thing is that also, to be honest, we work on x64 on all platforms now. And x64 is not taking that much liberty with the liberty it takes between Lord and stores.
Starting point is 00:30:02 So I'm pretty sure that we have some large free code. Well, I would not be surprised that we have some large free code that if we run it on a different hardware, we could have some issues that we need to fix. My friend Jeff Prishing has a blog, and I know I think you've written
Starting point is 00:30:19 one of his posts that some libraries, when they started to be ported to ARM, were a bunch of bugs being ported in some lock-free libraries because they were finding bugs only on that specific hardware. So I'm pretty sure that we have a lot of code that we'll need to adapt if we go back to a platform like PowerPC like we had in the past, and even more if we run it on ARM.
Starting point is 00:30:53 Well, you kind of have an advantage at the moment that all pretty basic... Well, correct me if I'm wrong, but basically all of the gaming platforms are 64-bit Intel architecture today. Yes, it's all x64, yeah. So it's simplifying things a lot, yeah. So back when we were supporting... You mentioned... Oh, yeah, we're just saying that.
Starting point is 00:31:12 Go ahead. Back in the day, we were supporting Xbox, for example, 360. And so we were, the previous console generation, were supporting PowerPC platforms. And we had a few issues. Jeff Rushing, in his CppCon talks two years ago, mentioned a case where he actually had to fix
Starting point is 00:31:30 an example using Atomics only for the Xbox. Interesting. You mentioned tagging a little bit with the performance tuning. Could you go into that a little bit more? I'm not too familiar with the concept of tagging for performance. Yeah, I don't know if that name is official in any way.
Starting point is 00:31:53 So basically, we declare profiling points in code. We declare them manually. And then we have a system to register them in a file. So it's pretty much working with Treadlock Hold data as much as possible, but do not affect performance. So that way we can record all the time in a profile build. And the profile build has pretty much the exact same performance as the final build, the one we ship to players.
Starting point is 00:32:24 So that way we're recording all the time the different tasks. We will not record we'll avoid, let's say, putting a profiling point in code. It's one line of code to declare the point. We'll avoid putting
Starting point is 00:32:40 a profiling point for something that is taking, let's say, something as low as one microsecond to run. You want something bigger than that. So then we can record all the time. For example, let's say it's the last five frames. But it might not even be frames. It might be just we allocate, let's say, 50 megs for recording.
Starting point is 00:33:04 And it always records. And that way, when there's a frame rate drop, we're sure to be able to dump all of this. And then we can see exactly what happened, what actually took time. So most of our profiling is done with that. Of course, we'll use tools like VTunes and stuff like that to make profiling with
Starting point is 00:33:27 a granularity that is much smaller but the reality is that most profiling is done with something a bit more macro where we can see what took time and if at some point you see something that is taking a lot of time and you don't have enough detail
Starting point is 00:33:43 then we will just add then a profiling point in the code and that's it. So this is what we tend to use most of the time because it's light enough to be deployed on the entire production floor. So a tester is playing the game, the game drops to 30 frames per second, it's going to automatically then make a dump of the profiling data that it currently has in memory so you can go back and look at it. Exactly. So the idea is that we add what we call
Starting point is 00:34:14 unified poetry across the production floor. Well, I say production floor, but I'm including multiple countries inside Ubisoft. So the idea is that if you have a drop of frame rate, we don't want you to need to reproduce anything. For everything related to performance,
Starting point is 00:34:32 the dump should be enough to have all the information you need to fix the issue right away and not having to reproduce it. Okay. So what are your thoughts on the difference between micro-benchmarking or whole program analysis that we're talking about with the profiling tools? each of them like 2-3% gain some of them were compilation flags or link time optimization these kind of things, the optimization of the arrays
Starting point is 00:35:13 making some systems lock free so for all these kind of things you want to test the entire game because even if I start introducing some lock free solutions I may change the type of contention there in the program. So I really need to test the real game to have the reality of if I'm proving performance or not. And the workflow I've been using to test performance, I mentioned it in my CPConc talk of this year,
Starting point is 00:35:46 is that sometimes it was preventing also bad optimization to come in, because sometimes you think it will make things faster, but then you test, and actually what you think is happening a minority of the time is actually more often. And when it's happening, the effect is terrible. And then you realize, okay, now I cannot put in it's not it's not actually improving things um so and doing micro optimization to me it's more if i if you optimize a very specific systems that um that is sitting on the top of others and yeah it and it's pretty much otherwise self-contained so then you can test that specific change
Starting point is 00:36:26 if you write a container or these kind of things. But we don't do that much of these things, so graphic programmers can optimize their stuff, let's say, in isolation in some cases, other programmers as well. But I think most of the time we do all programming analysis, or we analyze actually our own stuff but still inside the old programs with very macro profiling points as i've described before i'd like to interrupt the discussion for just a moment to bring you a word from our sponsors
Starting point is 00:36:57 c-line is a cross-platform ide for c and c++ from jetbrains it It relies on the well-known CMake build system and offers lots of goodies and smartness that can make your life a lot easier. C-Line natively supports C and C++, including C++11 standard, libc++, and boost. You can instantly navigate to a symbols declaration or usages too. And whenever you use C-Line's code refactorings, you can be sure your changes are applied safely throughout the whole code base. Perform unit testing with ease as CLion integrates with Google Test, one of the most popular C++ testing frameworks. And install one of the dozens of plugins like Vim Emulation Mode or Go Language Support. Download the trial version and learn more at jb.gg slash cppcast dash cline.
Starting point is 00:37:48 I have a question going back a bit. You mentioned that you have strings that basically completely avoid the heap, if I understood correctly, which is kind of like the ultimate conclusion of the small string optimization. Did I catch that right? Actually, I don't add it yet. This is something I want to try. We don't have that much strings.
Starting point is 00:38:11 But I'm just saying that every time you have small something optimization, small string optimization, small buffer optimization, yeah, every time you have a small string optimization buffer, whatever similar concept, I think Laplace should be designed to have a subclass
Starting point is 00:38:28 to be able to avoid a heap completely. So std string should be like that. Well, right now we're suggesting to add a new class called in play string instead. But my point is just that it shows that we're not thinking about people that never want to use a heap in the way some standard library
Starting point is 00:38:50 containers or components are written right now. Just adding something called allocator and say, oh, you can just use that. It's not that simple. Sometimes you want the same class to handle it. If there's already a buffer inside the implementation,
Starting point is 00:39:07 then, for example, suppose I want to make an allocator to use std function and make an in-place function with it, then I will lose the buffer inside it, and I will add another buffer that will be used instead. So I don't have an in- string right now I want you I want to test it well actually I haven't placed it's just right now is that deriving publicly from certain acts but I haven't placed now what I think about it so yeah
Starting point is 00:39:41 I did optimization having in in-place string. What I want to change is the base class of the string. What I want to change is our string implementation, so this in-place string is deriving publicly from the string class. So right now, it's deriving protectedly, so it's a bit messy, but I did
Starting point is 00:40:00 that before shipping last year. So what I would want is an in-place string deriving publicly from the string class, so that it can be passed around as a normal string. And to do that, you want the base class to need some minimal knowledge about
Starting point is 00:40:16 a few things. You need at least one bit to tell if it has the ownership of the buffer or not. So this is something I will need to do if I want to support it. So you need to sacrifice a few bits in the base class to be able to support that. And yeah, that's it. Then you have it. You have a subclass, you can't expand the small string buffer in the optimization buffer in the base class. Actually right now our string base class doesn't have a small string optimization buffer.
Starting point is 00:40:59 So what I will want is to do something very similar to what Nicholas Amrod presented at CppCon, so have something very similar to the GCC implementation with the small string optimization buffer, but the subclass will expand it. So right now I I have an in-place string, but it's easy to do because the base class doesn't have any buffer in it at all. So I want to have
Starting point is 00:41:34 both advantages, have the small buffer optimization, and at the same time, be able to have an in-place class to avoid the heap in any case where I see I can avoid it. That was a very messy response. What would, in your hypothetical class,
Starting point is 00:41:52 what would happen if the user expanded past the string? I mean, since your goal is to avoid the heap, would that be a crash? Would that be an exception? Or would it still grow like the small string optimization does today? I'm guessing not exception. It will still grow like the small string optimization does today? I'm guessing not exception. It will still grow. So you need something in the base class to know the size of the buffer as well.
Starting point is 00:42:15 So it's exactly as what Chandler showed for the small array or small vector. Yeah, so you want still to be still able to grow. So that way you're not... Because when you introduce in place array, in place string, whatever, you'll want to introduce a crash. You're just doing that as an optimization. So yeah,
Starting point is 00:42:38 and then it can grow more in a certain percentage of cases. Sometimes it won't be 100% of the time. I feel like I can imagine a world, in your world, where your optimization tools that you have that analyze the size of vectors and help you set the default reserve size
Starting point is 00:42:57 could then automatically feed back in and set the small optimization buffer size or whatever of everything that you have that might hit the heap and like kind of self-optimize your game to some extent uh that's that's for sure i could tweak that number and see the results that for sure this is yeah something interesting um one advantage i have in my case as well is that I can tweak the maximum size I can afford for strings. So I can decide that 2 bytes is enough for the size of a string. I could decide that it's a power of 2 for the buffer size, and that's it.
Starting point is 00:43:37 Fuck it. It's a power of 2. So then I can save some bits to make the string as small as possible because if I grow it too much, then I'm introducing side effects, adding cache misses in some places. So that's a tricky art. Yeah. I'll have to make sure to put a link to the Facebook Strings talk in the show notes this week.
Starting point is 00:44:05 Yes. So one question I had is you mentioned that you went from using Visual Studio 2012 to 2015. What C++ 11 and 14 features are you using at Ubisoft? Are you using a majority? Yeah, I think C++11 did a major cleanup and there's a bunch of things that were really interesting. If I just look at the in-place function implementation that we did with call cook
Starting point is 00:44:36 based on implementation of a lot of other people, actually, it's pretty neat when you look at the code that it's like 200 lines or something. If you compare back in the days of when Boost function was introduced, there's no comparison in the amount of code that we needed back in the day to do the same feature. So yeah, there's a lot of things that were created that were introduced. Move semantics I'm still discovering new ways of using them on a regular basis. Just this week I was optimizing our multimap implementation and our map class and our set class,
Starting point is 00:45:26 we are actually using, I don't know if you know about that, it's called Judy arrays. So Judy arrays are a C, it's a container written in C, with pretty much amazing performance. It's not an hash table, and it's not a binary tree either. It's its own thing. It's a Judy tree. And Judy arrays, they will only support keys that are 32 bits or 64 bits.
Starting point is 00:45:57 So they don't support complex types with constructors and all of this. So we are using them, and we wrap them in the C++ class. And basically, we require types being used to be mem-copyable. And this week, when I made the multimap, I decided that the value for the multimap, which is storing the value, would be actually a map of set. So the values will be a set that is implemented with Judy as well.
Starting point is 00:46:31 And I realized that our set class, I didn't need it to make it copyable. That didn't make sense to support a copy of a set, but it could make sense to make it movable. So I was able to use a very modern C++ feature to wrap something that is very old-style C code. I found that really interesting. So, actually, that's one example I have in mind. Also, for range loops, they became very popular very quickly at Ubisoft. We use a dotnet nomenclature, or begin and end functions, they start with an uppercase.
Starting point is 00:47:15 But we needed to add lowercase begin, lowercase end to support 4-range loops. And I thought that would be a great occasion to actually use a different iterator. Because we'll only use begin and end with lowercase inside 4-range loops. So what I did is I added inside the arrow class an atomic reading count. And I did a special iterator that will increase the count in the constructor and decrease it in the destructor.
Starting point is 00:47:49 That iterator was only movable. It was compiling fine. So that way, I'm just touching the atomic value twice. And that way, then I could assert a modification of the array inside the hourglass. And we found a bunch of bugs this way in the weeks after introducing the feature because then we could find some mold shredding issues when someone is modifying an array while another shred is iterating on it. So these new tools that we have in our hands,
Starting point is 00:48:23 we find new ways of using them on a regular basis. That's interesting. So I had a question calling back to your bio. You mentioned how you previously worked in the telecom, speech recognition, and computer-assisted surgery industries. How do you make the jump from an industry like that into game development? Because I know there's probably a lot of C++ developers out there who maybe dream about working on AAA video games
Starting point is 00:48:50 like you guys work on at Ubisoft. It's a good question. I think I always wanted to be a video game, so I made the jump and then I just adapted. But at first, to be honest, I was working more on tool side. So the transition was a bit less rough. Then I started to move more on C++ side in the engine. Back in the day, the philosophy in the game industry was much more accurate than today.
Starting point is 00:49:23 Today with multi-threading and all of this, I think the quality of code we're searching, it's much higher than let's say 10 or 15 or 20 years ago. So I think it made it easier for guys like me to find their place. But still in day eight, at the same time, I think I started to embrace that cultural difference. Something didn't change.
Starting point is 00:49:50 That importance for performance didn't change. But yeah, the importance of good architecture and good cold quality to make people productive.
Starting point is 00:50:06 This is something that is still very present. And the size of the code base has increased so much in the last 10 years in the game industry that we need good software design principles. So the challenge is to meet these requirements of performance at the same time where we make people as productive as possible. So
Starting point is 00:50:33 being a good C++ developer with a solid foundation in architecture and knowing how to write performing code is maybe just as important as having experience actually writing video games? Would you say? I think there's a place for everyone. We never have enough good programmers.
Starting point is 00:50:58 Even a game that comes from a 6-inch actually our player versus AI is not running at 60 FPS right now. So it's really for the game that is five versus five that really wants 60 FPS.
Starting point is 00:51:15 But my point is this, this place for tools, this place for good C++ tools, good C Sharp tools as well, to be honest. This place for good programmers everywhere, and if you're smart, and you're willing to learn, you know, a guy like me tells you you know this, and the best idea,
Starting point is 00:51:32 blah blah blah, and you just embrace, I think there's, I think that will be not a tough adaptation. I think it's much, yeah, I think it's harder for less good programmers to adapt
Starting point is 00:51:48 than a good C++ programmer if he's willing to embrace some sacrifices that we accept to make in the game. It's true. Jason, you have any other questions today? Yeah, I'm curious.
Starting point is 00:52:04 Since we've talked about, you know, you've moved to C++11 and 14, is there anything from C++17, maybe C++20, that you're really looking forward to that you think you could help out with your development? I'm really looking forward to coroutines. I don't know when we'll see them, but they can change all the right part of our code. It's pretty unclear, to be honest, at the moment.
Starting point is 00:52:31 But I'm thinking AI, behavior trees, or even if you look at network code, everything that you could write stuff using async features. There's definitely interesting ideas there. So to me coroutines is the most interesting. Of course it's hard to not think about the future without thinking about modules but I will say I'm still skeptical. I want them but at the same time I don't want to lose any performance, compilation performance that we have today.
Starting point is 00:53:10 We use FastBuild for fast compilation, and FastBuild to me is really, really precious. I think everybody should use it. Yeah, so with FastBuild, I like everybody should compile any code base under four minutes so to me that should never be higher than that so I
Starting point is 00:53:35 think it will be tough to be to choose modules and and yeah and get that performance from the first try because we cannot switch if there's a regression with compilation time. But yeah, if it works, that will be great. Okay, well, Nick, is there any links or anything or Twitter handle you want to share so people can find you online? Well, if people want to contact me,
Starting point is 00:54:03 I guess private reply on the google group of hg14 is probably the best way i don't have any twitter accounts whatever um so yeah i guess that would be the best way to contact me okay well thank you so much for your time today uh you're welcome thanks for joining us cool thank you thanks so much for listening in as we chat about C++. I'd love to hear what you think of the podcast. Please let me know if we're discussing the stuff you're interested in. Or if you have a suggestion for a topic, I'd love to hear about that too. You can email all your thoughts to feedback at cppcast.com.
Starting point is 00:54:39 I'd also appreciate if you like CppCast on Facebook and follow CppCast on Twitter. You can also follow me at Rob W. Irving and Jason at Leftkiss on Twitter. And of course, you can find all that info and the show notes on the podcast website at cppcast.com. Theme music for this episode is provided by podcastthemes.com.

There aren't comments yet for this episode. Click on any sentence in the transcript to leave a comment.