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

Separating Allocation from Code

2687ad35ad8b93df96c5521a86c7101c?s=47 chrismdp
September 04, 2014

Separating Allocation from Code

My journey started with a challenge that memory allocation was a separate concern to algorithm design, and ended in some quite unexpected discoveries.

2687ad35ad8b93df96c5521a86c7101c?s=128

chrismdp

September 04, 2014
Tweet

Transcript

  1. Separating Allocation from Code A blob implementation in C++ (

  2. Separating Allocation from Code Things I learnt coding AAA games

    that I’ve had to unlearn (
  3. Separating Allocation from Code How my mind was blown repeatedly

    (
  4. Separating Allocation from Code How everything is really LISP (

  5. Separating Allocation from Code The endless quest for a descriptive

    subtitle (
  6. Elixir Studios (

  7. 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”); } }; (
  8. vector<Monster*> monsters; ! Monster* m = new Monster(t, “Shrek”); monsters.push_back(m);

    (
  9. auto it = monsters.begin(); for (; it != monsters.end(); ++it)

    { if ((*it)->size > SMALL) { render(*it); } } ()
  10. Ruby on Rails, Coaching, Training ()

  11. Sol Trader soltrader.net ()

  12. Blob ()

  13. Blob ()

  14. ()

  15. “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 ()(
  16. class Blob { int8* data; Blob(size_t size) { data =

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

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

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

    file = getFileArray(“foo.txt”); Blob blob = { file, 1024 }; ()(
  20. “Tying allocation to code is a form of coupling, and

    coupling sucks.” (()(
  21. Tying allocation to code is a form of coupling bit.ly/1pHzTNf

    (()(
  22. class Blob { char *data; ! public: Blob() : _data(NULL)

    {} Blob(void* data) : data((char*)data) {} ! template <typename T> T* get(unsigned offset) const { return (T*)(_data + offset); } }; (()()
  23. Integer(Blob blob) : _blob(blob) {} ! void set(int value) {

    _blob.set1(0, BL_INT); _blob.set4(1, value); } (()()
  24. 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 (()()
  25. void Array::push_back(int value) { fix(place<Integer>().set(value)); } TYPE CAPACITY USED (18)

    SIZE (1) TYPE VALUE 13 0 18 (()()
  26. Results Random access was slower Sequential iteration was faster (()()

  27. RANDOM ACCESS Random access was at least an order of

    magnitude slower for all sizes These blobs aren’t designed for random access ((()()
  28. 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 ((()()
  29. void Array::push_back(int value) { fix(place<Integer>().set(value)); } ((())()

  30. struct Blob { int8* data; size_t size; }; ! int8

    backend[1024]; Blob blob = { backend, 1024 }; ((())()
  31. TDD: “Write tests first” ! (it’s not about the tests,

    actually, but we’ll let you figure that out) ((())()()
  32. 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; ((())()()
  33. Data-oriented design ((())()()

  34. Memory is slow ((())()()

  35. Memory is getting slower (relatively) ((())()()

  36. Memory is expensive ((())()()

  37. ! 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… ((())()()
  38. class Blob { int8* data; Blob(size_t size) { data =

    new int8[size]; } … }; ! Blob* obj = new Blob(1024); ((())()()
  39. CPU Blob (Heap) Data (Heap) 1 2 ((())()()

  40. sed https://www.flickr.com/photos/k_putt/sets/72157644129911474/ ((())()()

  41. Monsters auto it = monsters.begin(); for (; it != monsters.end();

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

    ++it) { // update, then if ((*it)->sizeChanged()) { copyToRender(*it, monstersToRender); } } ((())()()
  43. Monsters MonstersToRender ((())()()

  44. MonstersToRender auto it = monstersToRender.begin(); for (; it != monstersToRender.end();

    ++it) { render(*it); } render() render() render() ((())(s)()
  45. Advantages ((())(s)()

  46. Parallelisation is cheap MonstersToRender Thread 1 Thread 2 Thread 3

    ((())(s)p)
  47. ((())(s)p)

  48. * * * * * * * * Monsters *

    * * MonstersToRender vector<Monster*> monsters; (l(())(s)p)
  49. M M O M M O M M O M

    M O Monsters M M M MonstersToRender vector<Monster> monsters; Now we’re copying the object not a pointer! (l(())(s)p)
  50. Unit testing is easy Monsters MonstersToRender (l(())(s)p)

  51. It’s rather like… Functional Programming (l((i)(s)p)

  52. It’s rather like… Functional Programming (l((i)(s)p)

  53. 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.
  54. So what?

  55. Optimise early! (sometimes) Sometimes you need to to completely change

    your paradigm.
  56. 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”
  57. None
  58. 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
  59. 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
  60. Questions? chris@thinkcodelearn.com / @chrismdp on Twitter I coach and team

    teams on code quality, agile and BDD I make Sol Trader http://soltrader.net Co-founded Kickstart Academy http:// kickstartacademy.io