Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant...

149

Transcript of Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant...

Page 1: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape
Page 2: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Thinking Outside

the Synchronisation

Quadrant

@KevlinHenney

Page 3: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape
Page 4: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape
Page 5: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape
Page 6: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Architecture represents the

significant design decisions

that shape a system, where

significant is measured by

cost of change.

Grady Booch

Page 7: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Concurrency

Page 8: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Concurrency

Threads

Page 9: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Concurrency

Threads

Locks

Page 10: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Architecture is the art

of how to waste space.

Philip Johnson

Page 11: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Architecture is the art

of how to waste time.

Page 12: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Mutable

Immutable

Unshared Shared

Unshared mutable data needs no synchronisation

Unshared immutable data needs no synchronisation

Shared mutable data needs synchronisation

Shared immutable data needs no synchronisation

Page 13: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Mutable

Immutable

Unshared Shared

Unshared mutable data needs no synchronisation

Unshared immutable data needs no synchronisation

Shared mutable data needs synchronisation

Shared immutable data needs no synchronisation

The Synchronisation Quadrant

Page 14: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Systems have properties

— capabilities, features,

characteristics, etc. —

inside and out.

Page 15: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape
Page 16: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Functional

Operational

Developmental

Page 17: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Functional

Operational

Developmental

Page 18: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

This is the monstrosity in love,

lady, that the will is infinite,

and the execution confined;

that the desire is boundless,

and the act a slave to limit.

William Shakespeare

Troilus and Cressida

Page 19: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Multitasking is really just rapid

attention-switching.

And that'd be a useful skill, except it

takes us a second or two to engage

in a new situation we've graced with

our focus.

So, the sum total of attention is

actually decreased as we multitask.

Slicing your attention, in other

words, is less like slicing potatoes

than like slicing plums: you always

lose some juice.

David Weinberger

Page 20: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

http://ithare.com/infographics-operation-costs-in-cpu-clock-cycles/

Page 21: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

completion time for single thread

𝑡 = 𝑡1

Page 22: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

division of labour

𝑡 =𝑡1

𝑛

Page 23: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

𝑡 = 𝑡1 1 − 𝑝 𝑛 − 1

𝑛

portion in parallel

Amdahl's law

Page 24: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

𝑡 = 𝑡1 1 − 𝑝 𝑛 − 1

𝑛+ 𝑘

𝑛 𝑛 − 1

2

typical communication

overhead

inter-thread connections (worst case)

Page 25: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

𝑡 = 𝑡1 1 − 𝑝 𝑛 − 1

𝑛+ 𝑘

𝑛 𝑛 − 1

2

Page 26: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename TaskIterator, typename Reducer> void map_reduce( TaskIterator begin, TaskIterator end, Reducer reduce) { std::vector<std::thread> threads;

for(auto task = begin; task != end; ++task) threads.push_back(std::thread(*task));

for(auto & to_join : threads) to_join.join();

reduce(); }

Page 27: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Command-line tools

can be 235x faster than

your Hadoop cluster

Adam Drake

http://aadrake.com/command-line-tools-can-be-235x-faster-than-your-hadoop-cluster.html

Page 28: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Functional

Operational

Developmental

Page 29: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape
Page 30: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Some people, when confronted with a problem, think, "I know, I'll use threads," and then two they hav erpoblesms.

Ned Batchelder https://twitter.com/#!/nedbat/status/194873829825327104

Page 31: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Shared memory is like a canvas where threads collaborate in painting images, except that they stand on the opposite sides of the canvas and use guns rather than brushes.

The only way they can avoid killing each other is if they shout "duck!" before opening fire.

Bartosz Milewski "Functional Data Structures and Concurrency in C++"

http://bartoszmilewski.com/2013/12/10/functional-data-structures-and-concurrency-in-c/

Page 32: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

There are several ways to

address the problem of

deadlock...

http://www.cs.rpi.edu/academics/courses/fall04/os/c10/index.html

Page 33: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Just ignore it and hope it

doesn't happen.

Ostrich Algorithm

http://www.cs.rpi.edu/academics/courses/fall04/os/c10/index.html

Page 34: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Detection and recovery —

if it happens, take action.

http://www.cs.rpi.edu/academics/courses/fall04/os/c10/index.html

Page 35: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Dynamic avoidance by careful

resource allocation — check to

see if a resource can be

granted, and if granting it will

cause deadlock, don't grant it.

http://www.cs.rpi.edu/academics/courses/fall04/os/c10/index.html

Page 36: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Prevention — change the rules.

http://www.cs.rpi.edu/academics/courses/fall04/os/c10/index.html

Page 37: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Functional

Operational

Developmental

Page 38: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

habitable

Page 39: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape
Page 40: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Habitability is the characteristic

of source code that enables

programmers, coders, bug-fixers,

and people coming to the code

later in its life to understand its

construction and intentions and

to change it comfortably and

confidently.

Page 41: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Habitability makes a place

livable, like home. And this is

what we want in software — that

developers feel at home, can

place their hands on any item

without having to think deeply

about where it is.

Page 42: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

testable

Page 43: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Simple Testing Can Prevent

Most Critical Failures

An Analysis of Production Failures in

Distributed Data-Intensive Systems

https://www.usenix.org/system/files/conference/osdi14/osdi14-paper-yuan.pdf

Page 44: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

We want our code

to be unit testable.

What is a unit test?

Page 45: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

A test is not a unit test if:

It talks to the database

It communicates across the network

It touches the file system

It can't run at the same time as any of your other

unit tests

You have to do special things to your environment

(such as editing config files) to run it.

Michael Feathers http://www.artima.com/weblogs/viewpost.jsp?thread=126923

Page 46: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

A unit test is a test of behaviour

whose success or failure is wholly

determined by the correctness of

the test and the correctness of the

unit under test.

Kevlin Henney http://www.theregister.co.uk/2007/07/28/what_are_your_units/

Page 47: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

What do we want

from unit tests?

Page 48: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

When a unit test

passes, it shows

the code is correct.

Page 49: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

When a unit test

fails, it shows the

code is incorrect.

Page 50: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

isolated

Page 51: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

asynchronous

Page 52: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

sequential

Page 53: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape
Page 54: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Future

Immediately return a ‘virtual’ data object—

called a future—to the client when it invokes a

service. This future [...] only provides a value

to clients when the computation is complete.

Page 55: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

std::future<ResultType>

iou = std::async(function);

...

ResultType result = iou.get();

Page 56: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

joiner<ResultType>

iou = thread(function);

...

ResultType result = iou();

"C++ Threading", ACCU Conference, April 2003 "More C++ Threading", ACCU Conference, April 2004

"N1883: Preliminary Threading Proposal for TR2", JTC1/SC22/WG21, August 2005

Page 57: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

immutable

Page 58: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

In functional programming, programs are executed by evaluating expressions, in contrast with imperative programming where programs are composed of statements which change global state when executed. Functional programming typically avoids using mutable state.

https://wiki.haskell.org/Functional_programming

Page 59: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Many programming languages support programming in both functional and imperative style but the syntax and facilities of a language are typically optimised for only one of these styles, and social factors like coding conventions and libraries often force the programmer towards one of the styles.

https://wiki.haskell.org/Functional_programming

Page 60: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

William Cook, "On Understanding Data Abstraction, Revisited"

Page 61: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

[](){}

Page 62: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

[](){}()

Page 63: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

https://twitter.com/mfeathers/status/29581296216

Page 64: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

To keep our C++ API boundary simple, we [...] adopted one-way data flow. The API consists of methods to perform fire-and-forget mutations and methods to compute view models required by specific views. To keep the code understandable, we write functional style code converting raw data objects into immutable view models by default. As we identified performance bottlenecks through profiling, we added caches to avoid recomputing unchanged intermediate results. The resulting functional code is easy to maintain, without sacrificing performance.

https://code.facebook.com/posts/498597036962415/under-the-hood-building-moments/

Page 65: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Immutable Value

Define a value object type whose instances

are immutable. The internal state of a value

object is set at construction and no

subsequent modifications are allowed.

Page 66: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

const

Page 67: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

&

Page 68: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

&&

Page 69: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Copied Value

Define a value object type whose instances

are copyable. When a value is used in

communication with another thread, ensure

that the value is copied.

Page 70: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

class date { public: date(int year, int month, int day_in_month); date(const date &); date & operator=(const date &); ... int get_year() const; int get_month() const; int get_day_in_month() const; ... void set_year(int); void set_month(int); void set_day_in_month(int); ... };

Page 71: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Just because you have a getter, doesn't mean you should have a matching setter.

Page 72: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

class date { public: date(int year, int month, int day_in_month); date(const date &); date & operator=(const date &); ... int get_year() const; int get_month() const; int get_day_in_month() const; ... void set(int year, int month, int day_in_month); ... };

today.set(2016, 11, 16);

Page 73: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

class date { public: date(int year, int month, int day_in_month); date(const date &); date & operator=(const date &); ... int get_year() const; int get_month() const; int get_day_in_month() const; ... };

today = date(2016, 11, 16);

Page 74: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

class date { public: date(int year, int month, int day_in_month); date(const date &); date & operator=(const date &); ... int get_year() const; int get_month() const; int get_day_in_month() const; ... };

today = date { 2016, 11, 16 };

Page 75: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

class date { public: date(int year, int month, int day_in_month); date(const date &); date & operator=(const date &); ... int get_year() const; int get_month() const; int get_day_in_month() const; ... };

today = { 2016, 11, 16 };

Page 76: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

"Get something" is an imperative with an expected side effect.

Page 77: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

class date { public: date(int year, int month, int day_in_month); date(const date &); date & operator=(const date &); ... int get_year() const; int get_month() const; int get_day_in_month() const; ... };

Page 78: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

class date { public: date(int year, int month, int day_in_month); date(const date &); date & operator=(const date &); ... int year() const; int month() const; int day_in_month() const; ... };

Page 79: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

class date { public: date(int year, int month, int day_in_month); date(const date &); date & operator=(const date &); ... int year() const; int month() const; int day_in_month() const; date with_year(int) const; ... };

Page 80: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Builder

Introduce a builder that provides separate

methods for constructing and disposing of

each different part of a complex object, or

for combining cumulative changes in the

construction of whole objects.

Page 81: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

class date { public: ... int year() const; int month() const; int day_in_month() const; date with_year(int) const; ... };

Page 82: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

class date { public: ... int year() const; int month() const; int day_in_month() const; date with_year(int new_year) const { return { new_year, month(), day_in_month() }; } ... };

Page 83: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

class date { public: ... int year() const; int month() const; int day_in_month() const; date with_year(int new_year) const { return new_year == year ? *this : date { new_year, month(), day_in_month() }; } ... };

Page 84: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

class date { public: ... int year() const; int month() const; int day_in_month() const; date with_year(int) const; date with_month(int) const; date with_day_in_month(int) const ... };

Page 85: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Asking a question should not change the answer.

Bertrand Meyer

Page 86: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Asking a question should not change the answer, and nor should asking it twice!

Page 87: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Referential transparency is a very

desirable property: it implies that

functions consistently yield the same

results given the same input,

irrespective of where and when they are

invoked. That is, function evaluation

depends less—ideally, not at all—on the

side effects of mutable state.

Edward Garson "Apply Functional Programming Principles"

Page 88: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

// "FTL" (Functional Template Library :->) // container style template<typename ValueType> class container { public: typedef const ValueType value_type; typedef ... iterator; ... bool empty() const; std::size_t size() const; iterator begin() const; iterator end() const; ... container & operator=(const container &); ... };

Page 89: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename ValueType> class set { public: typedef const ValueType * iterator; ... set(std::initializer_list<ValueType> values); ... bool empty() const; std::size_t size() const;

iterator begin() const; iterator end() const;

iterator find(const ValueType &) const; std::size_t count(const ValueType &) const; iterator lower_bound(const ValueType &) const; iterator upper_bound(const ValueType &) const; pair<iterator, iterator> equal_range(const ValueType &) const; ... private: ValueType * members; std::size_t cardinality; };

set<int> c { 2, 9, 9, 7, 9, 2, 4, 5, 8 };

Page 90: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename ValueType> class array { public: typedef const ValueType * iterator; ... array(std::initializer_list<ValueType> values); ... bool empty() const; std::size_t size() const;

iterator begin() const; iterator end() const;

const ValueType & operator[](std::size_t) const; const ValueType & front() const; const ValueType & back() const; const ValueType * data() const; ... private: ValueType * elements; std::size_t length; };

array<int> c { 2, 9, 9, 7, 9, 2, 4, 5, 8 };

Page 91: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

In computing, a persistent data structure is a data structure that always preserves the previous version of itself when it is modified. Such data structures are effectively immutable, as their operations do not (visibly) update the structure in-place, but instead always yield a new updated structure.

http://en.wikipedia.org/wiki/Persistent_data_structure

(A persistent data structure is not a data structure committed to persistent storage, such as a disk; this is a different and unrelated sense of the word "persistent.")

Page 92: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape
Page 93: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape
Page 94: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape
Page 95: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape
Page 96: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename ValueType> class vector { public: typedef const ValueType * iterator; ... bool empty() const; std::size_t size() const;

iterator begin() const; iterator end() const;

const ValueType & operator[](std::size_t) const; const ValueType & front() const; const ValueType & back() const; const ValueType * data() const;

vector pop_front() const; vector pop_back() const; ... private: ValueType * anchor; iterator from, until; };

Page 97: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename ValueType> class vector { public: typedef const ValueType * iterator; ... bool empty() const; std::size_t size() const;

iterator begin() const; iterator end() const;

const ValueType & operator[](std::size_t) const; const ValueType & front() const; const ValueType & back() const; const ValueType * data() const;

vector pop_front() const; vector pop_back() const; ... private: ValueType * anchor; iterator from, until; };

Page 98: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename ValueType> class vector { public: typedef const ValueType * iterator; ... bool empty() const; std::size_t size() const;

iterator begin() const; iterator end() const;

const ValueType & operator[](std::size_t) const; const ValueType & front() const; const ValueType & back() const; const ValueType * data() const;

vector popped_front() const; vector popped_back() const; ... private: ValueType * anchor; iterator from, until; };

Page 99: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename ValueType> class vector { public: typedef const ValueType * iterator; ... bool empty() const; std::size_t size() const;

iterator begin() const; iterator end() const;

const ValueType & operator[](std::size_t) const; const ValueType & front() const; const ValueType & back() const; const ValueType * data() const;

vector popped_front() const; vector popped_back() const; ... private: ValueType * anchor; iterator from, until; };

Page 100: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape
Page 101: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

I still have a deep fondness for the Lisp model. It is simple, elegant, and something with which all developers should have an infatuation at least once in their programming life.

Kevlin Henney "A Fair Share (Part I)", CUJ C++ Experts Forum, October 2002

Page 102: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

lispt

Page 103: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename ValueType> class list { public: class iterator; ... std::size_t size() const;

iterator begin() const; iterator end() const;

const ValueType & front() const; list popped_front() const; list pushed_front() const; ... private: struct link { link(const ValueType & value, link * next);

ValueType value; link * next; }; link * head; std::size_t length; };

Page 104: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Hamlet: Yea, from the table

of my memory I'll wipe away

all trivial fond records.

William Shakespeare

The Tragedy of Hamlet

[Act I, Scene 5]

Page 105: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Garbage collection [...] is optional in C++; that is, a garbage collector is not a compulsory part of an implementation.

Bjarne Stroustrup http://stroustrup.com/C++11FAQ.html

Page 106: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

assert( std::get_pointer_safety() == std::pointer_safety::strict);

Page 107: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Ophelia: 'Tis in my memory

locked, and you yourself shall

keep the key of it.

William Shakespeare

The Tragedy of Hamlet

[Act I, Scene 3]

Page 108: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

A use-counted class is more

complicated than a non-use-

counted equivalent, and all of

this horsing around with use

counts takes a significant

amount of processing time.

Robert Murray

C++ Strategies and Tactics

Page 109: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename ValueType> class vector { public: typedef const ValueType * iterator; ... bool empty() const; std::size_t size() const;

iterator begin() const; iterator end() const;

const ValueType & operator[](std::size_t) const; const ValueType & front() const; const ValueType & back() const; const ValueType * data() const;

vector popped_front() const; vector popped_back() const; ... private: std::shared_ptr<ValueType> anchor; iterator from, until; };

Uses std::default_delete<ValueType[]>, but cannot be initialised from std::make_shared

Page 110: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename ValueType> class list { public: class iterator; ... std::size_t size() const;

iterator begin() const; iterator end() const;

const ValueType & front() const; list popped_front() const; list pushed_front() const; ... private: struct link { link(const ValueType & value, std::shared_ptr<link> next);

ValueType value; std::shared_ptr<link> next; }; std::shared_ptr<link> head; std::size_t length; };

Page 111: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

{ list<Anything> chain; std::fill_n( std::front_inserter(chain), how_many, something); }

On destruction, deletion of links is recursive through each link, causing the stack to blow up for surprisingly small values of how_many.

Page 112: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Instead of using threads and shared

memory as our programming model, we

can use processes and message passing.

Process here just means a protected

independent state with executing code,

not necessarily an operating system

process.

Russel Winder "Message Passing Leads to Better Scalability in Parallel Systems"

Page 113: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Languages such as Erlang (and occam

before it) have shown that processes are a

very successful mechanism for

programming concurrent and parallel

systems. Such systems do not have all

the synchronization stresses that shared-

memory, multithreaded systems have.

Russel Winder "Message Passing Leads to Better Scalability in Parallel Systems"

Page 114: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape
Page 115: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename ValueType> class channel { public: void send(const ValueType &); bool try_receive(ValueType &); private: ... };

Page 116: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename ValueType> class channel { public: void send(const ValueType &); bool try_receive(ValueType &); private: std::deque<ValueType> fifo; };

Page 117: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename ValueType> class channel { public: void send(const ValueType & to_send) { fifo.push_back(to_send); } ... };

Page 118: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename ValueType> class channel { public: ... bool try_receive(ValueType & to_receive) { bool received = false;

if (!fifo.empty()) { to_receive = fifo.front(); fifo.pop_front(); received = true; }

return received; } ... };

Page 119: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename ValueType> class channel { public: void send(const ValueType &); bool try_receive(ValueType &); private: std::mutex key; std::deque<ValueType> fifo; };

Page 120: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

void send(const ValueType & to_send) { std::lock_guard<std::mutex> guard(key); fifo.push_back(to_send); }

Page 121: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

bool try_receive(ValueType & to_receive) { bool received = false;

if (key.try_lock()) { std::lock_guard<std::mutex> guard(key, std::adopt_lock);

if (!fifo.empty()) { to_receive = fifo.front(); fifo.pop_front(); received = true; } }

return received; }

Page 122: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename ValueType> class channel { public: void send(const ValueType &); void receive(ValueType &); bool try_receive(ValueType &); private: std::mutex key; std::condition_variable_any non_empty; std::deque<ValueType> fifo; };

Page 123: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

void send(const ValueType & to_send) { std::lock_guard<std::mutex> guard(key); fifo.push_back(to_send); non_empty.notify_all(); }

Page 124: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

void receive(ValueType & to_receive) { std::lock_guard<std::mutex> guard(key); non_empty.wait( key, [this] { return !fifo.empty(); }); to_receive = fifo.front(); fifo.pop_front(); }

Page 125: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

https://twitter.com/richardadalton/status/591534529086693376

Page 126: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

std::string fizzbuzz(int n) { return n % 15 == 0 ? "FizzBuzz" : n % 3 == 0 ? "Fizz" : n % 5 == 0 ? "Buzz" : std::to_string(n); }

Page 127: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

void fizzbuzzer(channel<int> & in, channel<std::string> & out) { for (;;) { int n; in.receive(n); out.send(fizzbuzz(n)); } }

Page 128: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

int main() { channel<int> out; channel<std::string> back;

std::thread fizzbuzzing(fizzbuzzer, out, back)

for (int n = 1; n <= 100; ++n) { out.send(n); std::string result; back.receive(result); std::cout << result << "\n"; } ... }

Page 129: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

int main() { channel<int> out; channel<std::string> back;

std::thread fizzbuzzing(fizzbuzzer, out, back)

for (int n = 1; n <= 100; ++n) { out << n; std::string result; back >> result; std::cout << result << "\n"; } ... }

Page 130: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

void fizzbuzzer(channel<int> & in, channel<std::string> & out) { for (;;) { int n; in >> n; out << fizzbuzz(n); } }

Page 131: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename ValueType> class channel { public: void send(const ValueType &); void receive(ValueType &); bool try_receive(ValueType &);

void operator<<(const ValueType &); void operator>>(ValueType &); private: std::mutex key; std::condition_variable_any non_empty; std::deque<ValueType> fifo; };

Page 132: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename ValueType> class channel { public: void send(const ValueType &); void receive(ValueType &); bool try_receive(ValueType &);

void operator<<(const ValueType &); receiving operator>>(ValueType &); private: std::mutex key; std::condition_variable_any non_empty; std::deque<ValueType> fifo; };

Page 133: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename ValueType> class channel { public: void send(const ValueType &); void receive(ValueType &); bool try_receive(ValueType &);

void operator<<(const ValueType & to_send) { send(to_send); } receiving operator>>(ValueType & to_receive); { return receiving(this, to_receive); } ... };

Page 134: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

class receiving { public: receiving(channel * that, ValueType & to_receive) : that(that), to_receive(to_receive) { } receiving(receiving && other) : that(other.that), to_receive(other.to_receive) { other.that = nullptr; } operator bool() { auto from = that; that = nullptr; return from && from->try_receive(to_receive); } ~receiving() { if (that) that->receive(to_receive); } private: channel * that; ValueType & to_receive; };

Page 135: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

std::string fizzbuzz(int n) { if (n < 1 || n > 100) throw std::domain_error( "fizzbuzz(n) is defined for n in [1..100]")

return n % 15 == 0 ? "FizzBuzz" : n % 3 == 0 ? "Fizz" : n % 5 == 0 ? "Buzz" : std::to_string(n); }

Page 136: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

void fizzbuzzer(channel<int> & in, channel<std::any> & out) { for (;;) { try { int n; in >> n; out << fizzbuzz(n); } catch (...) { out << std::current_exception(); } } }

Page 137: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

int main() { channel<int> out; channel<std::any> back;

std::thread fizzbuzzing(fizzbuzzer, out, back)

for (int n = 1; n <= 100; ++n) { out << n; std::any result; back >> result; if (result.type() == typeid(std::string)) std::cout << std::any_cast<std::string>(result) << "\n"; } ... }

Page 138: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

template<typename ValueType> class channel { public: channel(); channel(std::function<void(const ValueType &)>);

void send(const ValueType &); void receive(ValueType &); bool try_receive(ValueType &);

void operator<<(const ValueType &); receiving operator>>(ValueType &); private: std::mutex key; std::condition_variable_any non_empty; std::deque<ValueType> fifo; };

Page 139: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

int main() { channel<int> out; channel<std::any> back( [](const any & received) { if (received.type() == typeid(std::exception_ptr)) std::rethrow_exception(any_cast<std::exception_ptr>(received)); });

std::thread fizzbuzzing(fizzbuzzer, out, back) ... }

Page 140: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

int main() { ... for (int n = 1; n <= 100; ++n) { try { out << n; std::any result; back >> result; std::cout << std::any_cast<std::string>(result) << "\n"; } catch (std::domain_error & caught) { std::cout << caught.what() << "\n"; } } ... }

Page 141: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Pipes and Filters

Divide the application's task into

several self-contained data

processing steps and connect these

steps to a data processing pipeline

via intermediate data buffers.

Page 142: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Simple filters that can be arbitrarily chained are more easily re-used, and more robust, than almost any other kind of code.

Brandon Rhodes http://rhodesmill.org/brandon/slides/2012-11-pyconca/

Page 143: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape
Page 144: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Multithreading is just one damn thing after, before, or simultaneous with another.

Andrei Alexandrescu

Page 145: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Actor-based concurrency is just one damn message after another.

Page 146: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape
Page 147: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape
Page 148: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

Think outside the

synchronisation

quadrant...

Page 149: Thinking Outside the Synchronisation Quadrant€¦ · Thinking Outside the Synchronisation Quadrant @KevlinHenney . Architecture represents the significant design decisions that shape

All computers

wait at the

same speed.