CppCast - C++ Game Development at Ubisoft
Episode Date: December 8, 2016Rob 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)
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
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.
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++.
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,
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.
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.
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
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.
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.
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
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
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
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
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,
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
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,
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.
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
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.
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
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
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...
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.
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.
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.
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
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.
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?
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
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
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
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
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,
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
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,
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
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.
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
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.
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,
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,
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
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.
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,
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
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,
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
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
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?
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
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
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
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
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,
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.
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.
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.
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
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.
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
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.
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.
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
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
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?
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.
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
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.
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.
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
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.
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.
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
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.
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
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
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
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,
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
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,
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
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
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.
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.
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
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
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,
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
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
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
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.
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
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,
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.
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,
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
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.
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.
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
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,
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.
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.
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.
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.
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,
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
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.
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.
That importance
for performance didn't change.
But
yeah,
the importance
of good architecture and
good cold quality to
make people productive.
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
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.
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.
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,
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
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.
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.
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.
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
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,
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.
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.