Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Separating allocation from code - NDC

December 05, 2014

Separating allocation from code - NDC

My development career has taken me from AAA games to high performance server programming to front end web applications. Along the way, I've learnt a lot through following seemingly counter-intuitive advice.

I was taught to write tests before my code. This made no sense at first, but gradually it became less about writing tests, and more about good software design. I was taught to separate allocation from code. This made no sense at first, but then I began to realise that John McCarthy was possibly right after all... and that Philip Greenspun had a point.

Join me for a tour through software development paradigms, games, dependency injection, data oriented programming, the NIH syndrome and the fundamental limitations of computing. Hopefully you'll emerge with a better idea of where high performance computing is going, and how to get ahead of the curve.

Warning: There might be C++ code from time to time, but it will be painless. Promise.


December 05, 2014

More Decks by chrismdp

Other Decks in Programming


  1. ()

  2. class Monster : public Entity { int size; string name;

    int attributes[100]; Animation* animation; public: Monster(string const& n, EntityTemplate* t) { size = t->size; name = n; animation = new Animation(n + “.ani”); } }; (
  3. auto it = monsters.begin(); for (; it != monsters.end(); ++it)

    { if ((*it)->size > SMALL) { render(*it); } } ()
  4. ()

  5. “I want to say something about implementing blobs that also

    applies to programming in general. Separate your memory allocation policy from your algorithm code. A higher level module should be used to glue the two together in the most optimal way. It always troubles me to see modules that allocate their own memory…” www.johnmccutchan.com/2011/10/programmer-and-his-blobs.html ()(
  6. class Blob { int8* data; Blob(size_t size) { data =

    new int8[size]; } … }; Blob* obj = new Blob(1024); ()(
  7. struct Blob { int8* data; size_t size; }; int8* backend

    = new int8[1024]; Blob blob = { backend, 1024 }; ()(
  8. struct Blob { int8* data; size_t size; }; int8* file

    = getFileArray(“foo.txt”); Blob blob = { file, 1024 }; ()(
  9. (()() class Blob { char *_data; public: Blob() : _data(NULL)

    {} Blob(char* data) : _data(data) {} template <typename T> T* get(unsigned offset) const { return (T*)(_data + offset); } };
  10. Array(Blob blob, unsigned capacity) { _blob = blob; _blob.set1(0, BL_ARRAY);

    _blob.set4(CAPACITY_OFFSET, capacity); _blob.set4(USED_OFFSET, FIRST_VALUE); _blob.set4(SIZE_OFFSET, 0); } char data[128]; Blob blob(data) Array array(blob, 128); TYPE CAPACITY USED (13) SIZE (0) 13 0 (()()
  11. RANDOM ACCESS Random access was at least an order of

    magnitude slower for all sizes These blobs aren’t designed for random access ((()()
  12. SEQUENTIAL TRAVERSAL With a map containing 50 vectors of 500

    numbers each: STL implementation was ~20% quicker With a map containing 500 vectors of 50 numbers each: Blob implementation was ~30% quicker ((()()
  13. struct Blob { int8* data; size_t size; }; int8 backend[1024];

    Blob blob = { backend, 1024 }; ((())()
  14. class AiData { char _goalData[GOAL_DATA_SIZE]; char _knData[MAX_ACTORS][ACTOR_K_SIZE]; char _qData[MAX_ACTORS][ACTOR_Q_SIZE]; char

    _sharedKnData[SHARED_K_SIZE]; char _sharedQData[SHARED_Q_SIZE]; }; AiData* ai = new AiData; ((())()()
  15. CPU Intel Core i5 Haswell L2 cache (512 KB) CORE

    + L1 (64 KB) L3 cache (3,072 KB) Main Memory (16,777,216 KB) Bus CORE + L1 (64 KB) This presentation brought to you by… ((())()()
  16. class Blob { int8* data; Blob(size_t size) { data =

    new int8[size]; } … }; Blob* obj = new Blob(1024); ((())()()
  17. ((())()() auto it = monsters.begin(); for (; it != monsters.end();

    ++it) { if ((*it)->size > SMALL) { render(*it); } } Monsters render() render() render()
  18. ((())()() auto it = monsters.begin(); for (; it != monsters.end();

    ++it) { // update, then if ((*it)->sizeChanged()) { copyToRender(*it, monstersToRender); } } Monsters
  19. (l(())(s)p) * * * * * * * * Monsters

    * * * MonstersToRender vector<Monster*> monsters;
  20. (l(())(s)p) M M M M M M M M Monsters

    M M M MonstersToRender vector<Monster> monsters; Now we’re copying the object not a pointer!
  21. Functional programming We mapping data to data - easy to

    reason about, referential transparency for free You’re copying data (or modifying in place as an optimisation) - levering the properties of immutability.
  22. Gold-plate it! (sometimes) Sometimes you need to keep cleaning code,

    learning and investigating. It’s amazing what you’ll find out. Kent Beck: “I’m in the habit of trying stupid things out to see what happens”
  23. Web developers You’re already doing this kind of work with

    SQL Consider your data before building elaborate and messy OO code Thinking this way helps to break work into asynchronous jobs Keep an eye on Gary Bernhardt’s new Structured Design work
  24. C++/C# developers Check out data-oriented design if you haven’t already

    Be wary of naïve OO design The future of performance is parallelisation Don’t write a program that’s hard to parallelise Unlock the power of modern GPUs
  25. Questions? [email protected] / @chrismdp on Twitter I coach and team

    teams on agile, code quality, and BDD I make Sol Trader http://soltrader.net and Card Pirates http://cardpirates.com I co-founded Kickstart Academy