save the earth, program in c++! · 2019. 5. 9. · 2019.05.10. porkoláb: save the earth, program...
TRANSCRIPT
SAVE THE EARTH,PROGRAM IN C++!
Zoltán PorkolábEricsson / Eötvös Loránd University
2019.05.10. Porkoláb: Save the Earth, Program in C++ 2
C++ applications
● C++ is used in various environments
– High performance computing
– GPGPU programming
– Large system implementation (like telecom)
– Device drivers (e.g. Arduino)
– Low energy environments (e.g. Mars rover)
– Performance critical systems (e.g. F35)
2019.05.10. Porkoláb: Save the Earth, Program in C++ 3
Related work
–
Rui Pereira, Marco Couto, Francisco Ribeiro, Rui Rua, Jácome Cunha, João Paulo Fernandes, and João Saraiva. 2017. Energy Efficiency across Programming Languages: How Do Energy, Time, and Memory Relate?.
In Proceedings of 2017 ACM SIGPLAN International Conference on Software Language Engineering (SLE’17). ACM, New York, NY, USA, 12 pages. https://doi.org/10.1145/3136014.3136031
2019.05.10. Porkoláb: Save the Earth, Program in C++ 4
Related work
–
Rui Pereira, Marco Couto, Francisco Ribeiro, Rui Rua, Jácome Cunha, João Paulo Fernandes, and João Saraiva. 2017. Energy Efficiency across Programming Languages: How Do Energy, Time, and Memory Relate?.
In Proceedings of 2017 ACM SIGPLAN International Conference on Software Language Engineering (SLE’17). ACM, New York, NY, USA, 12 pages. https://doi.org/10.1145/3136014.3136031
2019.05.10. Porkoláb: Save the Earth, Program in C++ 5
C++ design goals ● Type safety: strong type system● Resource safety: resource != memory
– RAII● Performance
– High performance– Low energy consumption
● Predictable run-time behavior– No virtual machine– No garbage collector
● Learnability– From expert friendly to novice friendly– Readable
2019.05.10. Porkoláb: Save the Earth, Program in C++ 6
Roots of C++
FORTRAN SIMULA67
ALGOL68ALGOL
ASSEMBLY BCPL C
C++
Efficiency
Abstraction
2019.05.10. Porkoláb: Save the Earth, Program in C++ 7
Memory layout of C++
2019.05.10. Porkoláb: Save the Earth, Program in C++ 8
Execution of C++
2019.05.10. Porkoláb: Save the Earth, Program in C++ 9
C++11● Core language improvements
– Rvalue references and move semantics– Constexpr– New memory model, thread locals– Better type inference (auto, decltype … )– Lambda functions, range based for– Initializer lists, uniform initialization, delegated constructors– Template aliases, variadic templates, user defined literals– Override, final, strongly typed enums, static assert, attributes– Right angle bracket!!! vector<list<int>>
● Libraries (mostly from Boost)– Hash tables (unordered_...)– Smart pointers (but unique_ptr instead of scoped_ptr)– Function objects and wrappers, Tuple, Array, Regular
expressions– Type traits
2019.05.10. Porkoláb: Save the Earth, Program in C++ 10
C++14 ● Core language improvements
– Lambda (generalized lambda capture, generic lambda) – Constexpr extended– Generalized return type deduction– Binary literals and digit separators– decltype(auto)– Variable templates
2019.05.10. Porkoláb: Save the Earth, Program in C++ 11
C++17 ● Language features
– Fold expressions– Template argument deduction for class templates– Constexpr if – Attributes, e.g. [[nodiscard]] (but std::remove() )– Structured binding
● New libraries– Filesystem– Utils (any, variant, optional, string_view, ...)
● Deprecated items– Trigraphs, register, auto_ptr– operator++(bool)– Old exception specification void foo() throw();
2019.05.10. Porkoláb: Save the Earth, Program in C++ 12
C++20 ● Language features
– Concepts– Coroutins– Contracts– Further constexpr extensions – Modules– Ranges– operator<=>
● New libraries– Span– Calendar, timezone– … lots more
2019.05.10. Porkoláb: Save the Earth, Program in C++ 13
Complexity grows
Page count of C++ standard
(by Bartłomiej Filipek: How to Stay Sane With Modern C++)
https://dzone.com/articles/how-to-stay-sane-with-modern-c
2019.05.10. Porkoláb: Save the Earth, Program in C++ 14
Strict aliasing
2019.05.10. Porkoláb: Save the Earth, Program in C++ 15
int f(int x, int y){ x = 0; y = 1; return x;}
Strict aliasing
2019.05.10. Porkoláb: Save the Earth, Program in C++ 16
int f(int x, int y){ x = 0; y = 1; return x;}
Strict aliasing
2019.05.10. Porkoláb: Save the Earth, Program in C++ 17
int f(int *xp, int *yp){ *xp = 0; *yp = 1; return *xp;}
Strict aliasing
2019.05.10. Porkoláb: Save the Earth, Program in C++ 18
int f(int *xp, long *yp){ *xp = 0; *yp = 1; return *xp;}
Strict aliasing
2019.05.10. Porkoláb: Save the Earth, Program in C++ 19
● Aliasing has negative effect on code optimization● Strict aliasing allows extra optimization posibilities● Using strong types have negative run time overhead● Using values instead of pointers / references may have negative overhead
Strict aliasing
2019.05.10. Porkoláb: Save the Earth, Program in C++ 20
Move semantics
2019.05.10. Porkoláb: Save the Earth, Program in C++ 21
● C++ has value semantics– Clear separation of memory areas– Significant performance loss when copying large objects– This led to improper use of (smart) pointers– … or template metaprogramming (expression templates)
C++ has value semantic
2019.05.10. Porkoláb: Save the Earth, Program in C++ 22
● C++ has value semantics– Clear separation of memory areas– Significant performance loss when copying large objects– This led to improper use of (smart) pointers– … or template metaprogramming (expression templates)–
● Java
x = y;
C++ has value semantic
2019.05.10. Porkoláb: Save the Earth, Program in C++ 23
● C++ has value semantics– Clear separation of memory areas– Significant performance loss when copying large objects– This led to improper use of (smart) pointers– … or template metaprogramming (expression templates)–
● Java
x = y;
C++ has value semantic
2019.05.10. Porkoláb: Save the Earth, Program in C++ 24
● C++ has value semantics– Clear separation of memory areas– Significant performance loss when copying large objects– This led to improper use of (smart) pointers– … or template metaprogramming (expression templates)–
● Java
x = y;
● C++
x = y;
C++ has value semantic
2019.05.10. Porkoláb: Save the Earth, Program in C++ 25
● C++ has value semantics– Clear separation of memory areas– Significant performance loss when copying large objects– This led to improper use of (smart) pointers– … or template metaprogramming (expression templates)–
● Java
x = y;
● C++
x = y;
C++ has value semantic
2019.05.10. Porkoláb: Save the Earth, Program in C++ 26
Copy semanticsclass Array {public: Array( ); Array (const Array&); Array& operator=(const Array&); Array& operator+=(const Array&); ~ Array ( );private: double *val;};Array operator+(const Array& left, const Array& right){ Array res = left; res += right; return res;}
void f(){ Array b, c, d; … Array a = b + c + d;}
2019.05.10. Porkoláb: Save the Earth, Program in C++ 27
class Array {public: Array( ); Array (const Array&); Array& operator=(const Array&); Array& operator+=(const Array&); ~ Array ( );private: double *val;};Array operator+(const Array& left, const Array& right){ Array res = left; res += right; return res;}
void f(){ Array b, c, d; … Array a = b + c + d;}
Copy semantics
2019.05.10. Porkoláb: Save the Earth, Program in C++ 28
class Array {public: Array( ); Array (const Array&); Array& operator=(const Array&); Array& operator+=(const Array&); ~ Array ( );private: double *val;};Array operator+(const Array& left, const Array& right){ Array res = left; res += right; return res;}
void f(){ Array b, c, d; … Array a = b + c + d;}
Copy semantics
2019.05.10. Porkoláb: Save the Earth, Program in C++ 29
class Array {public: Array( ); Array (const Array&); Array& operator=(const Array&); Array& operator+=(const Array&); ~ Array ( );private: double *val;};Array operator+(const Array& left, const Array& right){ Array res = left; res += right; return res;}
void f(){ Array b, c, d; … Array a = b + c + d;}
Copy semantics
2019.05.10. Porkoláb: Save the Earth, Program in C++ 30
class Array {public: Array( ); Array (const Array&); Array& operator=(const Array&); Array& operator+=(const Array&); ~ Array ( );private: double *val;};Array operator+(const Array& left, const Array& right){ Array res = left; res += right; return res;}
void f(){ Array b, c, d; … Array a = b + c + d;}
Copy semantics
2019.05.10. Porkoláb: Save the Earth, Program in C++ 31
2019.05.10. Porkoláb: Save the Earth, Program in C++ 32
● Move semantics– Instead of copying, reusing the resources– Leave the other object in a valid, unspecified, destructible state– Rule of three becomes rule of five (or rule of one)– All standard library components has been extended
● Backward compatibility– If we implemented the old-style member functions with lvalue reference
but do not implement the rvalue reference overloading versions we keep the old behavior -> gradually shift to move semantics.
● Serious performance gain– Except some RVO situations
Move semantics
2019.05.10. Porkoláb: Save the Earth, Program in C++ 33
class Array {public: Array( ); Array (const Array&); Array& operator=(const Array&); Array& operator+=(const Array&); ~ Array ( );private: double *val;};Array operator+(const Array& left, const Array& right){ Array res = left; res += right; return res;}
void f(){ Array b, c, d; … Array a = b + c + d;}
Move semantics
2019.05.10. Porkoláb: Save the Earth, Program in C++ 34
class Array {public: Array( ); Array (const Array&); Array& operator=(const Array&); Array& operator+=(const Array&); ~ Array ( );private: double *val;};Array operator+(const Array& left, const Array& right){ Array res = left; res += right; return res;}
void f(){ Array b, c, d; … Array a = b + c + d;}
Move semantics
2019.05.10. Porkoláb: Save the Earth, Program in C++ 35
class Array {public: Array( ); Array (const Array&); …};Array operator+(const Array& left, const Array& right){ Array res = left; res += right; return res;}Array operator+(Array&& left, const Array& right){ left += right; return std::move(left);}void f(){ Array b, c, d; … Array a = b + c + d;}
Move semantics
2019.05.10. Porkoláb: Save the Earth, Program in C++ 36
class Array {public: Array ( ); Array (const Array&); Array (Array&&); Array& operator=(const Array&); Array& operator=(Array&&); ~ Array ( );private: double *val;};
Array operator+(const Array& left, const Array& right);Array operator+(Array&& left, const Array& right);
void f(){ Array b, c, d; … Array a = b + c + d;}
Move semantics
2019.05.10. Porkoláb: Save the Earth, Program in C++ 37
Exception specification
2019.05.10. Porkoláb: Save the Earth, Program in C++ 38
template <class T>
class vector{public: // usual operations
void push_back(cons T && );
void pop_back( );
// ...private: // usual implementation with 3 pointers T* buffer;
T* size;
T* end;
};
Vector
2019.05.10. Porkoláb: Save the Earth, Program in C++ 39
template <class T>
class vector{public: // usual operations
void push_back(cons T && );
void pop_back( );
// ...private: // usual implementation with 3 pointers T* buffer;
T* size;
T* end;
};
Vector
2019.05.10. Porkoláb: Save the Earth, Program in C++ 40
template <class T>
class vector{public: // usual operations
void push_back(cons T && );
void pop_back( );
// ...private: // usual implementation with 3 pointers T* buffer;
T* size;
T* end;
};
Vector
2019.05.10. Porkoláb: Save the Earth, Program in C++ 41
template <class T>
class vector{public: // usual operations
void push_back(cons T && );
void pop_back( );
// ...private: // usual implementation with 3 pointers T* buffer;
T* size;
T* end;
};
Vector
2019.05.10. Porkoláb: Save the Earth, Program in C++ 42
#include <iostream>#include <vector>
struct S{ S() : id(++cnt) { std::cout << "S() "; } S(const S& rhs) : id(rhs.id) { std::cout << "copyCtr "; } S(S&& rhs) : id(std::move(rhs.id)) { std::cout << "moveCtr "; } S& operator=(const S& rhs) { id = rhs.id; std::cout << "copy= "; return *this; } S& operator=(S&& rhs) { id = std::move(rhs.id); std::cout << "move= "; return *this; } int id; static int cnt;};int S::cnt = 0;
int main() { std::vector<S> sv(5); sv.push_back(S());
for (std::size_t i = 0; i < sv.size(); ++i) std::cout << sv[i].id << " "; std::cout << '\n'; }
Exception specification
2019.05.10. Porkoláb: Save the Earth, Program in C++ 43
#include <iostream>#include <vector>
struct S{ S() : id(++cnt) { std::cout << "S() "; } S(const S& rhs) : id(rhs.id) { std::cout << "copyCtr "; } S(S&& rhs) : id(std::move(rhs.id)) { std::cout << "moveCtr "; } S& operator=(const S& rhs) { id = rhs.id; std::cout << "copy= "; return *this; } S& operator=(S&& rhs) { id = std::move(rhs.id); std::cout << "move= "; return *this; } int id; static int cnt;};int S::cnt = 0;
int main() { std::vector<S> sv(5); sv.push_back(S());
for (std::size_t i = 0; i < sv.size(); ++i) std::cout << sv[i].id << " "; std::cout << '\n'; }
$ g++ -std=c++11 && ./a.out
S() S() S() S() S() S() moveCtr copyCtr copyCtr copyCtr copyCtr copyCtr 1 2 3 4 5 6
Exception specification
2019.05.10. Porkoláb: Save the Earth, Program in C++ 44
#include <iostream>#include <vector>
struct S{ S() : id(++cnt) { std::cout << "S() "; } S(const S& rhs) : id(rhs.id) { std::cout << "copyCtr "; } S(S&& rhs) : id(std::move(rhs.id)) { std::cout << "moveCtr "; } S& operator=(const S& rhs) { id = rhs.id; std::cout << "copy= "; return *this; } S& operator=(S&& rhs) { id = std::move(rhs.id); std::cout << "move= "; return *this; } int id; static int cnt;};int S::cnt = 0;
int main() { std::vector<S> sv(5); sv.push_back(S());
for (std::size_t i = 0; i < sv.size(); ++i) std::cout << sv[i].id << " "; std::cout << '\n'; }
$ g++ -std=c++11 && ./a.out
S() S() S() S() S() S() moveCtr copyCtr copyCtr copyCtr copyCtr copyCtr 1 2 3 4 5 6
Exception specification
2019.05.10. Porkoláb: Save the Earth, Program in C++ 45
#include <iostream>#include <vector>
struct S{ S() : id(++cnt) { std::cout << "S() "; } S(const S& rhs) : id(rhs.id) { std::cout << "copyCtr "; } S(S&& rhs) noexcept : id(std::move(rhs.id)) { std::cout << "moveCtr "; } S& operator=(const S& rhs) { id = rhs.id; std::cout << "copy= "; return *this; } S& operator=(S&& rhs) noexcept { id = std::move(rhs.id); std::cout << "move= "; return *this; } int id; static int cnt;};int S::cnt = 0;
int main() { std::vector<S> sv(5); sv.push_back(S());
for (std::size_t i = 0; i < sv.size(); ++i) std::cout << sv[i].id << " "; std::cout << '\n'; }
Exception specification
2019.05.10. Porkoláb: Save the Earth, Program in C++ 46
#include <iostream>#include <vector>
struct S{ S() : id(++cnt) { std::cout << "S() "; } S(const S& rhs) : id(rhs.id) { std::cout << "copyCtr "; } S(S&& rhs) noexcept : id(std::move(rhs.id)) { std::cout << "moveCtr "; } S& operator=(const S& rhs) { id = rhs.id; std::cout << "copy= "; return *this; } S& operator=(S&& rhs) noexcept { id = std::move(rhs.id); std::cout << "move= "; return *this; } int id; static int cnt;};int S::cnt = 0;
int main() { std::vector<S> sv(5); sv.push_back(S());
for (std::size_t i = 0; i < sv.size(); ++i) std::cout << sv[i].id << " "; std::cout << '\n'; }
$ g++ -std=c++11 && ./a.out
S() S() S() S() S() S()moveCtr moveCtr moveCtr moveCtr moveCtr moveCtr 1 2 3 4 5 6
Exception specification
2019.05.10. Porkoláb: Save the Earth, Program in C++ 47
#include <iostream>#include <vector>
#include <algorithm>
struct S{ S() : id(++cnt) { std::cout << "S() "; } S(const S& rhs) : id(rhs.id) { std::cout << "copyCtr "; } S(S&& rhs) noexcept : id(std::move(rhs.id)) { std::cout << "moveCtr "; } S& operator=(const S& rhs) { id = rhs.id; std::cout << "copy= "; return *this; } S& operator=(S&& rhs) noexcept { id = std::move(rhs.id); std::cout << "move= "; return *this; } int id; static int cnt;};int S::cnt = 0;
int main() { std::vector<S> v1(5); std::vector<S> v2(5); std::move( v1.begin(), v1.end(), v2.begin());}
$ g++ -std=c++11 && ./a.out
S() S() S() S() S() S() S() S() S() S() S() S()
move= move= move= move= move=
std::move algorithm
2019.05.10. Porkoláb: Save the Earth, Program in C++ 48
#include <iostream>#include <vector>
#include <algorithm>#include <iterator>
struct S{ S() : id(++cnt) { std::cout << "S() "; } S(const S& rhs) : id(rhs.id) { std::cout << "copyCtr "; } S(S&& rhs) noexcept : id(std::move(rhs.id)) { std::cout << "moveCtr "; } S& operator=(const S& rhs) { id = rhs.id; std::cout << "copy= "; return *this; } S& operator=(S&& rhs) noexcept { id = std::move(rhs.id); std::cout << "move= "; return *this; } int id; static int cnt;};int S::cnt = 0;
int main() { std::vector<S> v1(5); std::vector<S> v2; std::move( v1.begin(), v1.end(), back_inserter(v2));}
$ g++ -std=c++11 && ./a.out
S() S() S() S() S() S()
moveCtr moveCtr moveCtr moveCtr moveCtrmoveCtr moveCtr moveCtr moveCtr moveCtrmoveCtr moveCtr
std::move algorithm
2019.05.10. Porkoláb: Save the Earth, Program in C++ 49
#include <iostream>#include <vector>
#include <algorithm>#include <iterator>
struct S{ S() : id(++cnt) { std::cout << "S() "; } S(const S& rhs) : id(rhs.id) { std::cout << "copyCtr "; } S(S&& rhs) noexcept : id(std::move(rhs.id)) { std::cout << "moveCtr "; } S& operator=(const S& rhs) { id = rhs.id; std::cout << "copy= "; return *this; } S& operator=(S&& rhs) noexcept { id = std::move(rhs.id); std::cout << "move= "; return *this; } int id; static int cnt;};int S::cnt = 0;
int main() { std::vector<S> v1(5); std::vector<S> v2; v2.reserve(5); std::move( v1.begin(), v1.end(), back_inserter(v2));}
$ g++ -std=c++11 && ./a.out
S() S() S() S() S() S()
moveCtr moveCtr moveCtr moveCtr moveCtr
std::move algorithm
2019.05.10. Porkoláb: Save the Earth, Program in C++ 50
● Move semantics– Instead of copying, reusing the resources– Leave the other object in a valid, unspecified, destructible state– Rule of three becomes rule of five (or rule of one)– All standard library components has been extended
● Reverse compatibility– If we implemented the old-style member functions with lvalue reference
but do not implement the rvalue reference overloading versions we keep the old behavior -> gradually shift to move semantics.
● Serious performance gain– Except some RVO situations
Move semantics
2019.05.10. Porkoláb: Save the Earth, Program in C++ 51
Undefined behavior
void f(){ int i = 1; std::cout << i << ++i;}
2019.05.10. Porkoláb: Save the Earth, Program in C++ 52
Undefined behavior
void f(){ int i = 1; std::cout << i << ++i;}
$ ./a.out12
2019.05.10. Porkoláb: Save the Earth, Program in C++ 53
Undefined behavior
void f(){ int i = 1; std::cout << i << ++i;}
$ ./a.out22
2019.05.10. Porkoláb: Save the Earth, Program in C++ 54
Undefined behavior
void f(){ int i = 1; std::cout << i << ++i;}
$ ./a.out12
2019.05.10. Porkoláb: Save the Earth, Program in C++ 55
cout i i
<<
<<
++
Undefined behavior
void f(){ int i = 1; std::cout << i << ++i;}
$ ./a.out22
2019.05.10. Porkoláb: Save the Earth, Program in C++ 56
cout i i
<<
<<
++?
Undefined behavior
void f(){ int i = 1; std::cout << i << ++i;}
$ ./a.out22
2019.05.10. Porkoláb: Save the Earth, Program in C++ 57
Undefined behavior
int f(int x){ int i; // not initialized if ( x < 0 ) i = 1; return i;}
2019.05.10. Porkoláb: Save the Earth, Program in C++ 58
Constant evaluation
2019.05.10. Porkoláb: Save the Earth, Program in C++ 59
Constant evaluation
int f(){ int sum = 0; for ( int i = 0; i < 100; ++i) sum += i; return sum;}
2019.05.10. Porkoláb: Save the Earth, Program in C++ 60
Constant evaluation
int f(int n){ int sum = 0; for ( int i = 0; i < n; ++i) sum += i; return sum;}
2019.05.10. Porkoláb: Save the Earth, Program in C++ 61
Constant evaluation
int f(int i) { return i; }
int main() { const int c1 = 1; // initialized compile time const int c2 = 2; // initialized compile time const int c3 = f(3); // f() is not constexpr const int *ptr = &c2; return 0;}
2019.05.10. Porkoláb: Save the Earth, Program in C++ 62
Constant evaluation
int f(int i) { return i; }
int main() { const int c1 = 1; // initialized compile time const int c2 = 2; // initialized compile time const int c3 = f(3); // f() is not constexpr const int *ptr = &c2; return 0;}
2019.05.10. Porkoláb: Save the Earth, Program in C++ 63
Constant evaluation
int main() { const int ci = 10; int *ip = const_cast<int*>(&ci); ++*rpm; cout << ci << " " << *ip << endl; return 0;}
$ ./a.out 10 11
2019.05.10. Porkoláb: Save the Earth, Program in C++ 64
Constant evaluation
include <cstdio>
size_t constexpr strlen(const char* str){ return *str ? 1 + length(str + 1) : 0;}
2019.05.10. Porkoláb: Save the Earth, Program in C++ 65
Constant evaluation C++14
#include <iostream>
constexpr int strlen(const char *s){ const char *p = s; while ( '\0' != *p ) ++p; return p-s;}
2019.05.10. Porkoláb: Save the Earth, Program in C++ 66
Constant evaluation C++17
std::visit([](auto&& arg) { using T = std::decay_t<decltype(arg)>; if constexpr (std::is_same_v<T, int>) std::cout << "int with value " << arg << '\n'; else if constexpr (std::is_same_v<T, long>) std::cout << "long with value " << arg << '\n'; else if constexpr (std::is_same_v<T, double>) std::cout << "double with value " << arg << '\n'; else if constexpr (std::is_same_v<T, std::string>) std::cout << "std::string with value " << std::quoted(arg); else static_assert(always_false<T>::value,"non-exhaustive visitor!");}, w);
2019.05.10. Porkoláb: Save the Earth, Program in C++ 67
C++11 concurrency model
2019.05.10. Porkoláb: Save the Earth, Program in C++ 68
Main memory (common)
CacheCache
CPU
CacheCache
CPU
CacheCache
CPU
CacheCache
CPU
CacheCache
CPU
Modern multicore hardware
2019.05.10. Porkoláb: Save the Earth, Program in C++ 69
● Describes the interactions of threads through memory
● Describes well defined behavior
● Constraints compiler for code generation
● C++ memory model contract
– Programmer ensures that the program has no data race
– System guarantees sequentially consistent execution
C++11 memory model
2019.05.10. Porkoláb: Save the Earth, Program in C++ 70
● Only minimal progress guaranties are given on threads:
– unblocked threads will make progress
– implementation should ensure that writes in a thread should be visible in other threads "in a finite amount of time".
● The A happens before B relationship:
– A is sequenced before B or
– A inter-thread happens before B
== there is a synchronization point between A and B
● Synchronization point:
– thread creation sync with start of thread execution
– thread completion sync with the return of join()
– unlocking a mutex sync with the next locking of that mutex
C++11 memory model
2019.05.10. Porkoláb: Save the Earth, Program in C++ 71
● Memory location
– an object of scalar type
– a maximal sequence of adjacent bit-fields all having non-zero width
● Data race
A program contains data race if contains two actions in different threads, at least one is not "atomic" and neither happens before the other.
● Two threads of execution can update and access separate memory locations without interfering each others
C++11 memory model
2019.05.10. Porkoláb: Save the Earth, Program in C++ 72
● Memory location
– an object of scalar type
– a maximal sequence of adjacent bit-fields all having non-zero width
● Data race == undefined behavior== undefined behavior
A program contains data race if contains two actions in different threads, at least one is not "atomic" and neither happens before the other.
● Two threads of execution can update and access separate memory locations without interfering each others
C++11 memory model
2019.05.10. Porkoláb: Save the Earth, Program in C++ 73
● Sequential consistent (default behavior)
– Leslie Lamport, 1979
– Each threads are executed in sequential order
– The operations of each thread appear in this sequence for the other threads in that order
Sequential consistency
2019.05.10. Porkoláb: Save the Earth, Program in C++ 74
std::mutex m;Data d;bool flag = false;
// thread 1 | // thread 2void Produce() | void Consume(){ | { | bool ready; d = ... | flag = true; | | | | bool ready = flag; | | | if ( ready ) use(d);} | }
Sequential consistency
2019.05.10. Porkoláb: Save the Earth, Program in C++ 75
std::mutex m;Data d;bool flag = false;
// thread 1 | // thread 2void Produce() | void Consume(){ | { | bool ready; d = ... | flag = true; | | | | bool ready = flag; | | | if ( ready ) use(d);} | }
Sequential consistency
2019.05.10. Porkoláb: Save the Earth, Program in C++ 76
std::mutex m;Data d;bool flag = false;
// thread 1 | // thread 2void Produce() | void Consume(){ | { | bool ready; d = ... | flag = true; | | | | bool ready = flag; | | | if ( ready ) use(d);} | }
Data race!
Sequential consistency
2019.05.10. Porkoláb: Save the Earth, Program in C++ 77
std::mutex m;Data d;bool flag = false;
// thread 1 | // thread 2void Produce() | void Consume(){ | { m.lock(); | bool ready; d = ... | flag = true; | m.unlock(); | | m.lock(); | bool ready = flag; | m.unlock(); | | if ( ready ) use(d);} | }
Sequential consistency
2019.05.10. Porkoláb: Save the Earth, Program in C++ 78
std::mutex m;Data d;bool flag = false;
// thread 1 | // thread 2void Produce() | void Consume(){ | { m.lock(); | bool ready; d = ... | flag = true; | m.unlock(); | | m.lock(); | bool ready = flag; | m.unlock(); | | if ( ready ) use(d);} | }
Synchronized with
Sequential consistency
2019.05.10. Porkoláb: Save the Earth, Program in C++ 79
std::mutex m;Data d;bool flag = false;
// thread 1 | // thread 2void Produce() | void Consume(){ | { m.lock(); | bool ready; d = ... | flag = true; | m.unlock(); | | m.lock(); | bool ready = flag; | m.unlock(); | | if ( ready ) use(d);} | }
Happens before
Sequential consistency
2019.05.10. Porkoláb: Save the Earth, Program in C++ 80
std::mutex m;Data d;std::atomic<bool> flag = false;
// thread 1 | // thread 2void Produce() | void Consume(){ | { m.lock(); | bool ready; d = ... | flag.store(true); | m.unlock(); | | m.lock(); | bool ready = flag.load(); | m.unlock(); | | if ( ready ) use(d);} | }
Sequential consistency
2019.05.10. Porkoláb: Save the Earth, Program in C++ 81
int x, y;
// thread 1 | // thread 2 x = 1; | cout << y << ", ";y = 2; | cout << x << endl;
In C++03 not even Undefined Behavior
In C++11 Undefined Behavior
Sequential consistency
2019.05.10. Porkoláb: Save the Earth, Program in C++ 82
std::atomic<int> x, y;
// thread 1 | // thread 2x.store(1); | cout << y.load() << ", ";y.store(2); | cout << x.load() << endl;
Equivalent to:
int x, y;mutex x_mutex, y_mutex;
// thread 1 | // thread 2 x_mutex.lock() | y_mutex.lock();x = 1; | cout << y << ", ";x_mutex.unlock() | y_mutex.unlock();y_mutex.lock() | x_mutex.lock();y = 2; | cout << x << endl;y_mutex.unlock() | x_mutex.unlock();
Sequential consistency
2019.05.10. Porkoláb: Save the Earth, Program in C++ 83
std::atomic<int> x, y;x.store(0); y.store(0);
// thread 1 | // thread 2x.store(1); | cout << y.load() << ", ";y.store(2); | cout << x.load() << endl;
Result can be:
0 02 10 1// never prints: 2 0
Sequential consistency: atomics == atomic load/store + ordering
Sequential consistency
2019.05.10. Porkoláb: Save the Earth, Program in C++ 84
● memory_order_seq_cst (default)
● memory_order_consume
● memory_order_acquire
● memory_order_release
● memory_order_acq_rel
● memory_order_relaxed
X86/x86_64 does not require additional instructions to implement acquire-release ordering
Other memory orders
2019.05.10. Porkoláb: Save the Earth, Program in C++ 85
● Each memory location has a total modification order
– But this may be not observable directly
● Memory operations performed by
– The same thread and
– On the same memory location
are not reordered with respect of modification order
Relaxed order
2019.05.10. Porkoláb: Save the Earth, Program in C++ 86
std::atomic<int> x, y;
// relaxed// thread 1 | // thread 2x.store(1, memory_order_relaxed); | cout << y.load(memory_order_relaxed) << ", ";y.store(2, memory_order_relaxed); | cout << x.load(memory_order_relaxed) << endl;
// Defined, atomic, but not ordered, result may be:0 02 10 12 0
//======================== read-modify-write ====================
std::atomic<int> x;
// relaxed// thread 1 int i = x.load();While ( ! x.compare_exchnge_weak( i, // expected value i+1, // desired value memory_order_relaxed ) );
x.fetch_add( 1, memory_order_relaxed);
Relaxed order
2019.05.10. Porkoláb: Save the Earth, Program in C++ 87
● A store-release operation synchronizes with all load-acquire operations reading a stored value
● Operations preceding the store-release in the releasing thread happens before operations following the load-acquire
● On some platforms acquire-release is cheaper than sequention consistency
std::mutex m; Data d;std::atomic<bool> flag = false;
// thread 1 | // thread 2void Produce() | void Consume(){ | { d = ... | flag.store(true, | memory_order_release); | bool flag.load(memory_order_acquire); | if ( ready ) use(d);} | }
Acquire-release order
2019.05.10. Porkoláb: Save the Earth, Program in C++ 88
● A store-release operation synchronizes with all load-acquire operations reading a stored value
● Operations preceding the store-release in the releasing thread happens before operations following the load-acquire
● On some platforms acquire-release is cheaper than sequention consistency
std::mutex m; Data d;std::atomic<bool> flag = false;
// thread 1 | // thread 2void Produce() | void Consume(){ | { d = ... | flag.store(true, | memory_order_release); | bool flag.load(memory_order_acquire); | if ( ready ) use(d);} | }
Acquire-release order
2019.05.10. Porkoláb: Save the Earth, Program in C++ 89
● A store-release operation synchronizes with all load-acquire operations reading a stored value
● Operations preceding the store-release in the releasing thread happens before operations following the load-acquire
● On some platforms acquire-release is cheaper than sequention consistency
std::mutex m; Data d;std::atomic<bool> flag = false;
// thread 1 | // thread 2void Produce() | void Consume(){ | { d = ... | flag.store(true, | memory_order_release); | bool flag.load(memory_order_acquire); | if ( ready ) use(d);} | }
Acquire-release order
2019.05.10. Porkoláb: Save the Earth, Program in C++ 90
// acquire-release// thread 1 | // thread 2x.store(1, memory_order_release); | cout << y.load(memory_order_acquire) << ", ";y.store(2, memory_order_release); | cout << x.load(memory_order_acquire) << endl;
// In C++11 Defined and the result can be: 0 02 10 1// never prints: 2 0, but can be faster than strict ordering.// results may be different in more complex programs
Acquire-release order
2019.05.10. Porkoláb: Save the Earth, Program in C++ 91
● Operations preceding the store-release in the releasing thread happens before an operation X in the consuming thread where X has a data dependency on the loaded value
int d = 0;std::atomic<int *> ptr = std::nullptr;
// thread 1 | // thread 2void Produce() | void Consume(){ | { d = 42 | int *p; ptr.store(&x, | memory_order_release); | if (p=ptr.load(memory_order_consume)) | { | assert ( 42 == *p ); // true | assert ( 42 == d ); // DATA RACE | }} | }
Consume-release order
2019.05.10. Porkoláb: Save the Earth, Program in C++ 92
● Operations preceding the store-release in the releasing thread happens before an operation X in the consuming thread where X has a data dependency on the loaded value
int d = 0;std::atomic<int *> ptr = std::nullptr;
// thread 1 | // thread 2void Produce() | void Consume(){ | { d = 42 | int *p; ptr.store(&x, | memory_order_release); | if (p=ptr.load(memory_order_consume)) | { | assert ( 42 == *p ); // true | assert ( 42 == d ); // DATA RACE | }} | }
Consume-release order
2019.05.10. Porkoláb: Save the Earth, Program in C++ 93
● Operations preceding the store-release in the releasing thread happens before an operation X in the consuming thread where X has a data dependency on the loaded value
int d = 0;std::atomic<int *> ptr = std::nullptr;
// thread 1 | // thread 2void Produce() | void Consume(){ | { d = 42 | int *p; ptr.store(&x, | memory_order_release); | if (p=ptr.load(memory_order_consume)) | { | assert ( 42 == *p ); // true | assert ( 42 == d ); // DATA RACE | }} | }
Consume-release order
2019.05.10. Porkoláb: Save the Earth, Program in C++ 94
Dead code optimization
2019.05.10. Porkoláb: Save the Earth, Program in C++ 95
#include <memory.h>
void consume(char *);
void f(){ int i; char secret[BUFSIZE]; fgets( secret,v BUFSIZE, stdin); // use secret consume(secret); }
Dead code optimization
2019.05.10. Porkoláb: Save the Earth, Program in C++ 96
#include <memory.h>
void consume(char *);
int main(){ int i; char secret[BUFSIZE]; fgets( secret,v BUFSIZE, stdin); // use secret consume(secret); // erase secret for security reasons memset(secret, 0, sizeof(secret)); // check secret for(i = 0; i < BUFSIZE; ++i) printf(“d”,secret[i]);}
Dead code optimization
2019.05.10. Porkoláb: Save the Earth, Program in C++ 97
Dead code optimization
2019.05.10. Porkoláb: Save the Earth, Program in C++ 98
#include <memory.h>
void consume(char *);
int main(){ int i; char secret[BUFSIZE]; fgets( secret,v BUFSIZE, stdin); // use secret consume(secret); // erase secret for security reasons memset(secret, 0, sizeof(secret)); }
Dead code optimization
2019.05.10. Porkoláb: Save the Earth, Program in C++ 99
Dead code optimization
2019.05.10. Porkoláb: Save the Earth, Program in C++ 100
Dead code optimization -O3
2019.05.10. Porkoláb: Save the Earth, Program in C++ 101
#include <memory.h>
void consume(char *);
int main(){ void* (*fp)(void*,int,size_t); int i; fp = memset; char secret[BUFSIZE]; fgets( secret,v BUFSIZE, stdin); // use secret consume(secret); // erase secret for security reasons fp(secret, 0, sizeof(secret)); }
Dead code optimization
2019.05.10. Porkoláb: Save the Earth, Program in C++ 102
Dead code optimization
2019.05.10. Porkoláb: Save the Earth, Program in C++ 103
#include <memory.h>
void consume(char *);
volatile void* (*fp)(void*,int,size_t);
int main(){ int i; fp = memset; char secret[BUFSIZE]; fgets( secret,v BUFSIZE, stdin); // use secret consume(secret); // erase secret for security reasons fp(secret, 0, sizeof(secret)); }
Dead code optimization
2019.05.10. Porkoláb: Save the Earth, Program in C++ 104
Dead code optimization
2019.05.10. Porkoláb: Save the Earth, Program in C++ 105
#include <memory.h>
void consume(char *);
int main(){ int i; char secret[BUFSIZE]; fgets( secret,v BUFSIZE, stdin); // use secret consume(secret); // erase secret for security reasons memset_s(secret, sizeof(secret), 0, sizeof(secret)); }
Dead code optimization
2019.05.10. Porkoláb: Save the Earth, Program in C++ 106
Dead code optimization
2019.05.10. Porkoláb: Save the Earth, Program in C++ 107
Summary
● C++ gives you full control over resources
– No virtual machine, no interpretation
● Compilers can make extreme optimizations● Programmer can destroy these opportunities
– Unnecessary copies– False optimizations– Aliasing
● Programmer can provide optimizations
– Writing “not too clever” code– Using strong types– Avoid aliasing– Choosing the right memory model
2019.05.10. Porkoláb: Save the Earth, Program in C++ 108
Q&A
Zoltán PorkolábEricsson / Eötvös Loránd Universityhttp://gsd.web.elte.hu