Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » [C/D/Rust/etc] Enjoyably writing perfomant, cross-platform native code...?

This thread is locked; no one can reply to it. rss feed Print
 1   2 
[C/D/Rust/etc] Enjoyably writing perfomant, cross-platform native code...?
Erin Maus
Member #7,537
July 2006
avatar

Hi! I'm trying to implement some means of self-therapy to prevent me from spiraling into mental decay. The critical bit is keeping occupied and focused, so I'm thinking of working on finally rewriting my vector graphics library in a native, portable language.

By portable, I mean:

  • Can compile without tremendous effort on just about any non-esoteric platform combination (hardware and software). This means x86 desktop platforms, such as modern Windows (anything beyond XP), Linux distributions, BSD (especially BSD, because I'm going to be switching to FreeBSD shortly for philosophical and personal reasons). It also means popular ARM-based mobile platforms (iOS, Android, Windows Phone...) and various embedded platforms (consoles namely, such as Wii U/PowerPC and its x86 counterparts, PS4/Xbox One).

  • Can interface with any competent language by exposing a C-style interface, enabling efficient usage from other environments (i.e., using LuaJIT's FFI bits, or Java's JNI, or using it in a C++ application, or .NET using P/Invoke).

And by native, I mean:

  • Low-level memory management when necessary. If the user of my library has their own memory allocation needs, my library should be able to make use of it. Very important for mobile and embedded targets, as well as high-performance desktop needs (e.g., rendering engines in games).

  • No hidden performance penalties.

But most importantly, I'd like minimal antagonistic language philosophies :). I don't want to become overly stressed because of poor language choices that rear their heads at inconvenient times. Verbosity isn't a major issue if it has a purpose, either--I actually prefer a more rigid and explicit syntax, especially when reading code by others ("others" includes me after looking at months-old, or years-old, code!).

The library aims to be performant enough to enable real-time vector graphics in user interfaces and games. Allowing integration into existing projects is also crucial. I actually won't be implementing rendering code in the library itself; instead, it would spit out resources for various APIs and hardware (i.e., optimized meshes and textures). Efficiently marshaling these resources (essentially binary blobs at the end of the day) is very important, as well as being able to clearly define ownership. A solid threading model could be beneficial, depending on future developments (e.g., developing parallel-friendly logic to take advantage of multi-core systems).

I've previously attempted writing a vector graphics library in C++. A simple C shim would enable the library being used in other projects and some subset of C++ is extremely portable (this subset generally eliminates C++11 and beyond, though). It fits the memory management requirements somewhat--except when you want to use custom allocators with the standard library, at which point all the data structures are off-limits. Any C++ code that doesn't play nice with custom allocators (a large amount of libraries) also becomes difficult or impossible to work with. Not to mention managing ownership and all is left to the developer and isn't integrated into the language, making it much too easy to make a mistake that won't blow up for some time. And language quirks pop up at the most inopportune times. Verbosity is needlessly terrible and useless, especially with modern computers (namely the archaic headers/source distinction; I shouldn't have to write so much boilerplate code that doesn't offer any incentives). And threading is a joke.

I've skimmed over Rust and find it very refreshing. Looking at larger projects written in Rust isn't an obstacle (I spent the better half of a day looking at Mozilla's Servo browser engine). I am very fond of the memory features (especially if many memory errors are found during compile time, which seems to be a major point/philosophy?), and it seems I can have fine control of allocation strategies, all the while benefiting from a solid ownership model. Creating a C shim seems well supported (judging by Servo and documentation I found otherwise). It seems Rust uses LLVM as a back-end, which should ensure the portability requirements. I know a few users here have used Rust to varying degrees, so advice and anecdotes would be nice! It also seems to have a good threading model.

I've used D in the past. I'm very fond of the power and syntax D brings. For this project in particular though, it seems counterproductive despite D being a native language because of the memory requirements. As far as I can tell, D's garbage collector, despite being optional, is pretty much necessary when using the standard library and some language features. I'm also not sure how well supported D is on less common platforms (mobile and embedded). No idea about threading, having never worked with threads in D. edit: I also can't find any documentation on making a C shim for a D library, which is absurd. I found a snippet about using Haskell's FFI with a D library, but nothing on marshaling or potential ABI issues.

And there's probably other options that I haven't discussed... So any advice?

---
ItsyRealm, a quirky 2D/3D RPG where you fight, skill, and explore in a medieval world with horrors unimaginable.
they / she

SiegeLord
Member #7,827
October 2006
avatar

I used both D and Rust (and still use Rust). I think D, once you get over its constant hurdles (abhorrent tooling, even on the level of compilers) is a pretty nice to program with in the sense of giving you the tools to put your ideas into code without much trouble. D's templates are very free-form and powerful, so you can express some pretty interesting semantics. It is indeed possible to export a C interface to a D library (I've done it), but keep in mind that you'll probably need to lug around the runtime (if you want to use the standard library). The custom allocator support actually got a lot of attention in D recently, but most of the standard library doesn't work with it, so you practically need to use a GC at some point. Moreover, if you do abandon the GC, D loses all the tools it had to provide memory safety, so you end up in just as much danger of segfaults as you were in C++. If you use the runtime, you're hamstrung by needing it ported to every platform you want to use; I think it works on BSD and some ARMs, but it'll definitely be second tier support because you'll have to use the two alternative compilers, which lag in features and stability. I think the built-in threading support is terrible and barely better than C++'s, I don't think it's a selling point of D at all despite their claims to the contrary. I think D is okay to program in on Linux if you don't mind the GC, but that's about it. I don't think this is changing any time soon.

Rust doesn't have a runtime and has pretty powerful tools for ensuring memory safety. If you have an allergy for heap allocations, Rust provides tools to construct the most Byzantine allocation-free architecture that I'd be terrified to try in C++ or D, fearing blowing my foot off. Rust doesn't have a very good custom allocator support, so you will have to abandon the standard data structures (perhaps there's a 3rd party library for this though). Still, it's static analysis tools will still work even if you do your own allocations (as I said above, a big advantage of Rust is being able to not allocate in the first place). Since there's no runtime, using other platforms should be pretty easy. I know Android is a first tier platform, but BSD is a second tier platform (that said, they do run tests on it, but I see right now that they seem to be failing... hah). I think for BSD you might find a bit more trouble with it. I think the threading support is pretty good, although I haven't really pushed it that hard.

The biggest issue with Rust is that it's really opinionated about what designs are easily expressible in its language semantics. If you write code that follows the design principles that are checkable, then you won't have trouble. If you run into the limitations of the static checker, then you'll start to have a lot of trouble. In Rust, very much unlike D and C++, you really must come up with a design with Rust's semantics in mind. You can't just pick a random idea and implement it, you'll have a lot of trouble and it will be super frustrating if you try to do it. Rust requires a lot of upfront thinking and then being open to completely gut your design if it doesn't mesh with what Rust considers proper. Many things that are completely memory safe are not allowed by Rust, and while it has an escape hatch, once you take that route it won't be holding your hand (and in many ways, the unchecked part of Rust is even more dangerous than what C++ and D offer you). It's not practical or enjoyable to try to go against the Rust way.

That said, I've used it for a few years and after a bit of time you learn what works and what doesn't, so with experience those things don't hamper you too much. It is something you will have to deal with if you're starting with it however. If you can clear that hurdle and be okay with somewhat iffy BSD support, I do think Rust is probably a good language for your use case. I enjoy the language and do all of my non-Allegro development in it, and I've written a few Allegro games in it as well.

Lastly... it's surprising to me that you think that you'll need to abandon C++11 and on for portability... I was under the impression that both Clang and GCC support its features across the board. I program a lot in C++11 (and 14) at work, and I think there's a really nice subset of the language that you could restrict yourself too. Yes headers are a pain, but when you look at what what D and Rust have to deal with because they don't have them (neither has good separate compilation), you start to appreciate them a bit (I still am on the no-headers side of the fence though :P).

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

GullRaDriel
Member #3,861
September 2003
avatar

Portability ? C.

"Code is like shit - it only smells if it is not yours"
Allegro Wiki, full of examples and articles !!

jmasterx
Member #11,410
October 2009

Write it in ANSI C and everyone will be happy.

Even if u write it in C++, you can make a C binding fairly easily.

I use C++11 and C++14 a lot now... it's not that scary, very useful, and yes, every modern compiler supports it.

Erin Maus
Member #7,537
July 2006
avatar

The verbosity of C is worse than C++, especially math code. Considering my library would be very dependent on matrices and vectors, I'd rather not. Not to mention Microsoft's C compiler has no support for C99.

And regarding C++ portability: there's many useful niches of this library where only a subset of C++ would be used where C++11, if supported, isn't amazingly useful. For example, consoles developers, or a console SDK itself (i.e., PS4), often disables support for exceptions (making the C++ standard library counter-productive). Unreal Engine 4 disables exceptions, as well, on the platforms it supports. C++ containers have terrible support for custom allocators (even in C++11), and the language itself makes using custom allocation strategies a nightmare if using any other libraries.

And thank you for your advice, SiegeLord :)! I think I'm going to try Rust for this project. Ownership models will be very simple as it stands, so I don't suppose I'll run into extreme issues with the borrow checker at this moment.

---
ItsyRealm, a quirky 2D/3D RPG where you fight, skill, and explore in a medieval world with horrors unimaginable.
they / she

Thomas Fjellstrom
Member #476
June 2000
avatar

Consoles disable exceptions? WTF? I mean I'm not exception fan, I actually dislike them somewhat, but sometimes, it's the only way to gracefully handle errors across disparate modules/libraries.

I'm still in the beginning stages of designing/coding my library/framework and was thinking of integrating exceptions, because it helps with passing errors through lua, other scripting languages, and other libraries, but if consoles don't support exceptions? I'll have to completely rethink that decision.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Mark Oates
Member #1,146
March 2001
avatar

designing/coding my library/framework

what for? :)

--
Visit CLUBCATT.com for cat shirts, cat mugs, puzzles, art and more <-- coupon code ALLEGRO4LIFE at checkout and get $3 off any order of 3 or more items!

AllegroFlareAllegroFlare DocsAllegroFlare GitHub

Chris Katko
Member #1,881
January 2002
avatar

Consoles disable exceptions?

Quote:

I'm adding another surprising example here: LLVM/CLang dont use exception nor RTTI for the followin reasons:

In an effort to reduce code and executable size, LLVM does not use RTTI (e.g. dynamic_cast<>) or exceptions. These two language features violate the general C++ principle of "you only pay for what you use", causing executable bloat even if exceptions are never used in the code base, or if RTTI is never used for a class. Because of this, we turn them off globally in the code.

That said, LLVM does make extensive use of a hand-rolled form of RTTI that use templates like isa<>, cast<>, and dyn_cast<>. This form of RTTI is opt-in and can be added to any class. It is also substantially more efficient than dynamic_cast<>.

http://programmers.stackexchange.com/questions/113479/are-there-any-real-world-cases-for-c-without-exceptions

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Mark Oates
Member #1,146
March 2001
avatar

I just want a null pointer exception. :/

--
Visit CLUBCATT.com for cat shirts, cat mugs, puzzles, art and more <-- coupon code ALLEGRO4LIFE at checkout and get $3 off any order of 3 or more items!

AllegroFlareAllegroFlare DocsAllegroFlare GitHub

SiegeLord
Member #7,827
October 2006
avatar

In Rust there are no null pointers unless you try super hard to make them ;).

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Thomas Fjellstrom
Member #476
June 2000
avatar

Quote:

what for? :)

A few reasons really.

I (very) briefly looked into other engines and they all seem to be designed for a specific use case (FPSs mostly) and are not very good at working with dynamic/modifiable worlds.

Also, this is something I want to do, for learning purposes.

Just considered you might be asking what the library itself would be for, its games mostly.

The core idea is that everything, including the core frame building bits, is data driven and can be changed at runtime (can call it a framegraph). Including the entire main renderer, or adding some pre or post processing, maybe add some overlays, whatever you want really. I can't take credit for the idea though. The rest of the engine is a scene graph of sorts, also data driven and easily mutate-able. There's a concept of a "Query", where things can ask the engine to let them know if some condition is met, and the engine then fires off an event when it happens. It should avoid the need for individual bits to scan the world for things. Everything will be scriptable. It will also be highly parallel. That may make some things a bit "interesting" to do (events of any kind will usually be fed into a job queue that can run on a number of cpus/cores)

I have somewhat grandiose plans ;D we'll see if I can make it work the way I want, and if i can get some kind of game on it working before I die ;D

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Chris Katko
Member #1,881
January 2002
avatar

You should really look into the Thief engine.

http://www.gamasutra.com/view/feature/3355/postmortem_thief_the_dark_project.php

Quote:

The final scripting system built into Thief was the Tagged Schema system. When the game required motions and sounds, it requested them as concepts modified by optional qualifiers, rather than directly. For example, an AI who had just heard the player would request the concept "moderate alert," qualified with an optional tag like "+sense:sound." A potential set of resources was then chosen using a pattern matcher; in this example, it would choose all samples in that AI's voice expressing a generic "something's not right," all samples expressing "I heard something fishy," but no samples expressing "I saw something fishy." From this set, the specific resource was then chosen using a weighted random selection. The tables used were specified by the designers using a simple language. Specifying motion and sound selection this way, designers created an interesting variety of randomized environments and behaviors without changing the code of the game.

Also, the kind of engine it is:

https://en.wikipedia.org/wiki/Entity_component_system

I think if I make an FPS engine, I'll do my own spin on something like that.

http://www.gamedev.net/page/resources/_/technical/game-programming/understanding-component-entity-systems-r3013

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

bamccaig
Member #7,536
July 2006
avatar

I think it's laughable to disregard C on the premise of MSVC not supporting C99, yet look towards far less portable platforms like D or Rust. ::) It sounds less like a legitimate question and more like you're looking for somebody to support your bias. To each his own. I'm not heavily invested in this sphere.

I have little use for writing low-level, blazing fast to run, slow to write code. New players sound like really great ideas until you look into actually targeting them and then it's really not worth it anymore. It's a pain in the neck just for a newcomer to build a program for these platforms. They aren't readily available. I'd probably appreciate some of the latest C++ features, and at the same time probably curse them for their rough edges compared with both modern and ancient options. C is nice in theory for being pure and simple, and horrible for being too close to the metal and far too much effort to get even a relatively simple job done properly (which C++ also suffers and probably D and Rust et. al. too).

I imagine that deploying a D or Rust program are also a bit of a pain right now. I think it's cool that somebody is taking on the problem of fast and smart coding platforms, but I think the evidence shows that it's still a few decades away from being free. Until then you and likely your users will have to do a lot of hard work, or you're stuck with the tried and true subset of C or C++ that is truly portable or with preprocessor magic where that fails.

The really interesting thing is how much is possible with very high-level code these days. It really makes me wonder how much value there is investing in such low-level code. It should be enough to keep somebody like the OP busy, which is arguably a good thing. That said, his efforts may be better placed trying to contribute to the dream projects he proposes to use rather than attempting to merely use them (albeit, using them is probably a contribution in itself).

???

Thomas Fjellstrom
Member #476
June 2000
avatar

Also, the kind of engine it is

Yup, my engine is also ECS based. (that's the scene graph bits)

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

SiegeLord
Member #7,827
October 2006
avatar

bamccaig said:

I think it's laughable to disregard C on the premise of MSVC not supporting C99, yet look towards far less portable platforms like D or Rust. ::)

It makes perfect sense. With D and Rust you won't have MSVC users trying to compile your code and failing. It's less of a hassle to get a user to install a Rust or a D compiler than it is to make a MSVC user install GNU tools.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Erin Maus
Member #7,537
July 2006
avatar

I expected a famous bambam post.

bamccaig said:

I think it's laughable to disregard C on the premise of MSVC not supporting C99, yet look towards far less portable platforms like D or Rust.

MSVC is the most popular compiler on Windows platforms, which includes the desktop operating system (used by the majority of games and game middleware). In any case, Rust can use MSVC's linker. In the case of other platforms, as far as I can tell, since Rust uses LLVM, it's a simple matter of having it spit out some valid object file for the variety of platforms supported by LLVM. PS4 uses Clang/LLVM and Xbox One uses MSVC, so yes, Rust would most assuredly be compatible with some effort. Wii U most probably uses CodeWarrior (if the Wii and GameCube are of any indicator), but it could be very possible to make a simple linker script to spit out a compatible RPL.

Quote:

I have little use for writing low-level, blazing fast to run, slow to write code.

Good thing you aren't writing this vector graphics library, right :)? Your needs are irrelevant.

Quote:

New players sound like really great ideas until you look into actually targeting them and then it's really not worth it anymore. It's a pain in the neck just for a newcomer to build a program for these platforms. They aren't readily available.

Rust's build system is actually pretty nice, from what I can tell. Had no problem toying around with it so far.

In comparison, C nor C++ have a good package system or build system. There's a thousand and one ways to build C++ code, and each project does it differently.

Quote:

It really makes me wonder how much value there is investing in such low-level code.

Let me know when a AAA game is written completely in vanilla Python. Oh wait, it can't be and it won't be :).

Quote:

That said, his efforts may be better placed trying to contribute to the dream projects he proposes to use rather than attempting to merely use them (albeit, using them is probably a contribution in itself).

There is exactly one notable vector graphics libraries targeted for real-time rendering: Direct2D. Its API is horrible and its only vaguely supported on Windows desktop platforms. All of the others, such as Cairo, prioritize quality over performance, and are therefore are unsuitable.

---
ItsyRealm, a quirky 2D/3D RPG where you fight, skill, and explore in a medieval world with horrors unimaginable.
they / she

Chris Katko
Member #1,881
January 2002
avatar

There's lots of C# and Java games now though, because those languages are "productivity first" programming languages.

My games engines will run D or C++ as a back-end, with Python as a scripting language.

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Erin Maus
Member #7,537
July 2006
avatar

There's lots of C# and Java games now though, because those languages are "productivity first" programming languages.

But they have to invoke native APIs. They run on a native OS. Their runtimes are built largely on native technology. They rely on a JIT compiler to achieve any sense of performance.

Even theoretical "managed" operating systems, like Singularity, have to make changes to these managed languages for writing the bulk of the OS. And not only that, but the languages compile down to native code.

In any case, Unity's rendering pipeline is written in C++. And it uses native physics library for 2D and 3D. C# and Java only viable for "AAA-light" game development now because computers are simply so powerful.

---
ItsyRealm, a quirky 2D/3D RPG where you fight, skill, and explore in a medieval world with horrors unimaginable.
they / she

Mark Oates
Member #1,146
March 2001
avatar

I've opened the can of worms for Ruby extensions. I think it's viable to write a complete backend in C/C++ and have the content creation and scripting wrapper in ruby.

I just don't know what the core module components might be.

   class Entity
   end
   class Menu
   end
   class World
   end
   class Scene
   end

?

It just seems like so much overhead. :P

--
Visit CLUBCATT.com for cat shirts, cat mugs, puzzles, art and more <-- coupon code ALLEGRO4LIFE at checkout and get $3 off any order of 3 or more items!

AllegroFlareAllegroFlare DocsAllegroFlare GitHub

Erin Maus
Member #7,537
July 2006
avatar

I've opened the can of worms for Ruby extensions. I think it's viable to write a complete backend in C/C++ and have the content creation and scripting wrapper in ruby.

That's how most games work. It's like a GUI app: the slowest part of the software tends to be user input (such as taking a few seconds to click a button). Essentially, would optimizing this feature to O(0) speed up the program noticeably, or at all?

Quote:

I just don't know what the core module components might be.

A typical entity-component-systems are very unlike OOP. This is why Unity's component system is poorly implemented.

A component only has data:

struct Transform {
  Vector3 position;
  Vector3 scale;
  Quaternion rotation;
};

Components are often stored in a flat array for cache coherence:

Transform[] transforms;

An entity is simple an identifier or handle that describes what components are attached to this entity. There's various methods to implement the relationship. A common approach is a bit flag and ID in some tiny data structure:

enum Component {
  Transform = 0x1,
  Body = 0x2,
  RenderState = 0x4
};

// First 16 bits are an ID, last 48 bits are attached components.
// Allows for concurrent ~65,000 entities.
typedef uint64 EntityHandle;

// ...

EntityHandle foo = World.CreateEntity(Component.Transform | Component.RenderState);

A system operates on entities with certain components. Iteration is often linear and can provide outputs for other processes. Example:

void UpdateRenderStateTransformSystem() {
  foreach entity in World
    where entity.HasComponent(Transform)
    and where entity.HasComponent(RenderState) {
      renderStates[entity].Transform = BuildTransform(entity);
  }
};

The benefits of an ECS include separating logic, cache coherence, better memory management (including quick pooling/unpooling of entities and components), etc.

---
ItsyRealm, a quirky 2D/3D RPG where you fight, skill, and explore in a medieval world with horrors unimaginable.
they / she

Mark Oates
Member #1,146
March 2001
avatar

So it's structured more like a packed database table than a series of scattered and uncertainly sized derived objects. I recently discovered "object slicing" about a month ago and thought dammit, now I'm going to have to rethink this whole architecture.

Components are often stored in a flat array for cache coherence:

Is the bottleneck of cache misses and cache coherence really that substantial? I wish I knew more about this.

--
Visit CLUBCATT.com for cat shirts, cat mugs, puzzles, art and more <-- coupon code ALLEGRO4LIFE at checkout and get $3 off any order of 3 or more items!

AllegroFlareAllegroFlare DocsAllegroFlare GitHub

Erin Maus
Member #7,537
July 2006
avatar

It depends. In all cases, enabling the processor to cache future data is always much faster than having to load said data from RAM on every operation, by a magnitude of 100x or more (depending on hardware specifics, mostly CPU and RAM, obviously). Of course, sometimes it's not possible or viable.

Most often it depends on scale. Do you only have a handful of entities? Then an ECS is at most a useful tool to separate concerns, but it won't boost performance much, if at all, because entity logic is not a bottleneck (see Amdahl's law; essentially, if you could make an algorithm or some such run at 0 time, would the software speed up any, if at all?). Unity's component system is fine for some small number of objects as-is, for example.

But what if you have 1,000s of independent entities? 10,000s? Even 100,000s? Unity starts suffering long before 10,000 unique "GameObjects." This is because each component has massive overhead, resulting in death by a thousand cuts. A proper ECS could very well have tens of thousands upon tens of thousands of entities with identical behavior on the same hardware.

Think of it like drawing sprites naively. If you're drawing upwards of 1,000 unique textured sprites, performance won't be much of an issue on modern hardware. You could probably get away with minimal atlas usage and each sprite could be a draw call. But say you want 20,000: the overhead of texture switching and draw calls will be a problem on classic APIs. You wouldn't worry about optimizing the first case, but you would have to worry on the second.

---
ItsyRealm, a quirky 2D/3D RPG where you fight, skill, and explore in a medieval world with horrors unimaginable.
they / she

Chris Katko
Member #1,881
January 2002
avatar

Is the bottleneck of cache misses and cache coherence really that substantial? I wish I knew more about this.

Holy balls, yes. RAM is what... 200 times slower.. worse when you multiply every redirection you have to do!

This talk opened my eyes:

https://youtu.be/rX0ItVEVjHc?t=30m24s

It was great. He was like "screw templates. screw RTTI." to a bunch of C++ elites who seem to get off on the idea of writing templates for everything. They were speechless.

Another great point he drove home in another one is how we're all brainwashed into resolving problems into OOP. "I have a car, I make a car class. I have a bird, I make a bird class." That's stupid. A computer doesn't know what a bird is, and you have assumed that the problem you're trying to solve will neatly fit into your chosen concepts, and then you wonder why it's all slow as balls--because of the ridiculous amount of redirections going on.

Like, why the hell should a "car" object operate on itself? What happens when a car has to talk to another car, like during collision detection? Who owns "collision"?

Moreover, you can't pack a bunch of "Car classes" together. You can, but they're going to be bigger than the cache lines and certainly not a multiple of the cache line size. With data-oriented design, you can keep the data separate from the code. You can not only put cars together, but you can also put like-attributes of cars/birds/etc like "position" data packed together. Why? Because you're almost always using the same data in the same way. Positions are compared to positions, not a position+sound+texture data. So why have them next to each other in memory? If they're next to each other it becomes trivial to unroll a loop to the size of your typical cache lines and presto, wizard-level speed increases. You can also do things like keep the position data of cars, dogs, worlds, and tacos all next to each other instead of trying to collide a taco (position + taco_specifics) and a car (position and car_specifics) when all you really care about is the position data of each member.

And what about cache lines? "I can't hardcode that! What if there's another architecture?" He goes on to say that "No matter what you're doing there is ALWAYS a subset of architectures you are targeting. So find your subset, and optimize for that. Nobody is writing code to run on PCs and commodore 64's."

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

bamccaig
Member #7,536
July 2006
avatar

I'm thankful you linked to a video and enraged that I didn't see it until 1:15 AM on a work night (technically tomorrow is a work day, but I had to work all weekend due to release crunch time). >:(

Chris Katko
Member #1,881
January 2002
avatar

This was an edit, but it makes more sense as another post:

----

This also works GREAT for static pools. You almost always know the maximum amount of objects you're going to have in a game. A world mesh isn't going to have more polygons than when it started (in most games). So you throw everything into static pools tied to scene data. (Global data + level specific data) Want to free all the data with a level? Well, free the [objects_in_the_level] static pool and you're done. That's it. The thing that's really great about static pools is... you're not deleting anything. You're re-using the skeleton of objects. Essentially, everything becomes a particle system. There's no need to "delete" an object from the heap when it's dead. Mark it as dead, move on, and use the corpse for your next created object.

Another thing is: Branching is evil. OOP leads to tons of branching and redirection. In a real game by AAA-programmers, they don't branch... they sort. That way, all objects of a specific branch all hit over and over so the branch prediction doesn't have to constantly try and re-learn. It goes from 1111111111... to ...1110000000 instead of say 10011110001000101011110010101001.

One example he lists in the video is literally write out your branches to a file, and then zip it. The compression ratio is an approximate indicator of how much time you're wasting branching.

Basically, anything you read under data-oriented design, or from Mike Acton of Insomniac Games is absolute gold and eye opening.

He did some other great talks on game development at a college, as well as Cell processor programming. Apparently, DoD is required for getting any real power out of a Cell so Sony put tons of time and money into making DoD talks.

Here's a great PPT of a talk:

http://harmful.cat-v.org/software/OO_programming/_pdf/Pitfalls_of_Object_Oriented_Programming_GCAP_09.pdf

I can't find the video of the talk yet. But it does break it down into numbers to prove all of their claims.

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

 1   2 


Go to: