• Post category:StudyBullet-13
  • Reading time:11 mins read


C++ Memory model, concurreny, multithreading, performance, modern C++, C++11/14/17/20, gdb, valgrind, C++ atomics

What you will learn

Details of C++ memory model on which atomics are built

Concepts of memory barriers and memory order in C++

Using Modern C++ to design lock free algorithms with C++ atomics and memory model

Importance of sequential consistency in concurrency

Getting high performane using C++ memory model

External factors influencing performance of atomics

Tools to use in production while working with C++ in production

Description

This intermediate-level course is for C++ developers who want to deepen their understanding of memory models in C++.

Memory models are critical to modern software development, especially for concurrent programming. In this course, you will learn about the C++ memory model, including the Sequential Consistency-Data Race Free (SC-DRF) guarantee, which ensures that concurrent programs are well-defined and behave as expected.

You will also learn about the different memory barriers used to enforce ordering constraints between memory accesses in a concurrent program. Finally, you will explore the impact of these barriers on performance and learn how to use them to optimize your code.

In addition, you will delve into false sharing in caches, which can lead to significant performance degradation in multi-threaded programs. You will learn how to identify and avoid false sharing in your code.

The course will also cover the performance of atomics in C++ and how to use them to implement efficient synchronization mechanisms in your programs. You will explore the different types of atomics available in C++, including lock-free and wait-free algorithms, and learn how to use them effectively.

Finally, the course will provide an overview of performance analysis tools such as perf, Valgrind, Intel Vtune, Google Orbit, and gdb, which can be used to profile, debug, and optimize your code.

Throughout the course, you will work on practical examples and hands-on exercises to reinforce your understanding of the material. By the end of this course, you will have a solid experience with memory models in C++ and be able to write correct, efficient, high-performance concurrent programs.

Importance of memory models in Modern C++


Get Instant Notification of New Courses on our Telegram channel.


Learning memory models in modern C++ is crucial for developing high-performance applications and avoiding tricky bugs resulting from the misuse of concurrent programming constructs.

Memory models define the rules for accessing shared memory in a multi-threaded program. As multi-core processors become more prevalent, writing concurrent programs that correctly and efficiently utilize shared memory is essential for achieving high performance.

Misuse of memory models can lead to subtle and hard-to-find bugs that can be difficult to diagnose and fix. These bugs can result in incorrect behavior, data corruption, and crashes. Therefore, understanding memory models and the various synchronization primitives provided by modern C++ is essential for developing reliable concurrent programs.

By learning memory models in modern C++, developers can write efficient and correct multi-threaded programs that fully utilize the available hardware resources. They can also avoid common pitfalls and tricky bugs arising from the incorrect use of memory models.

In short, learning memory models in modern C++ is a critical skill for developers who want to develop high-performance applications that utilize multiple cores and avoid the common pitfalls and tricky bugs associated with concurrent programming.

Evolution of the C++ Memory Model

The C++ concurrency model has evolved significantly from C++11 to C++20, introducing new language features and library components that provide more powerful and flexible support for concurrent programming. Here are some of the significant changes that have occurred:

  1. C++11: C++11 introduced the first set of language-level concurrency features, including the std::thread class for creating and managing threads, mutexes and condition variables for synchronization, and atomic types for lock-free programming. C++11 also defined the Sequential Consistency-Data Race Free (SC-DRF) memory model, which guarantees correct behavior for well-synchronized programs.
  2. C++14: C++14 introduced several improvements to the concurrency features introduced in C++11, including new constructors and member functions for std::thread, support for heterogeneous lookup in concurrent containers, and enhancements to the std::atomic template.
  3. C++17: C++17 introduced several new concurrency features and enhancements, including support for parallel algorithms in the Standard Template Library (STL), structured bindings for returning multiple values from std::thread functions, and support for shared_mutex for shared read access and exclusive write access. C++17 also introduced a new memory model that provides stronger guarantees for atomic operations.
  4. C++20: C++20 builds on the concurrency features introduced in previous language versions and adds several new features and enhancements. These include the atomic_ref class, which provides a safer way to access shared variables without the need for explicit synchronization, improvements to the memory model to reduce the need for explicit fences and barriers, and enhancements to the coroutines library that make it easier to write asynchronous code.

The evolution of the C++ concurrency model from C++11 to C++20 has provided developers with a more powerful and flexible set of tools for writing efficient and correct concurrent programs. The new language features and library components introduced in each version have addressed many of the challenges and limitations of earlier versions. In addition, they have made concurrent programming in C++ more accessible and easier to use.

English
language

Content

Introduction

Introduction
C++ Memory Model from standard to production

Modern C++ Memory Model : Closer look

Memory Model guarantees
External factors affecting the program execution workflow of a C++ program
Sequential conisistency definition in multithreaded applications
Race condition in concurrency with regard to sequential consistency
SC-DRF : Sequential Concistency (Data Race Free)
Role of Modern C++ memory model in guarantee of SC-DRF

Code Optimizations: Behind the scenes

Simple optimization example
Code walkthrough of issue with the concurrent execution of multithreaded code
Optimization example with single threaded code
Summary and general tips around optimization of concurrent code in modern C++

Concurrency and barriers

Thinking in Transactions
The concept of critical section in concurrent and multithreaded applications
Concurrency considerations while moving code out of critical sections
Concurrency considerations while moving code inside of critical sections
Concept of acquire and release barriers in concurrency memory models
Considerations while choosing memory barriers while desiging for multithreading
A closer look at barriers and their relation to sequential consistency
Summary of memory barriers in concurrent applications
Impact of external optimizations on concurreny in modern C++ applications

Performance analysis of atomics data types in modern C++

Considerations while making performace measurements
Code experiment recommended to be performed by students.
Code demonstartion of behavoir of atomic variables in practice
Do atomic variables wait for each other?
False sharing in concurrency and multithreading

Compare and Swap in Modern C++

Introduction to compare and swap
Example of compare and swap
Pseudo code implementation of compare and exchange strong
Pseudo code implementation of compare exchange strong – faster
Compare and exchange weak – reason for spurios failures

Memory ordering in multithreading with Modern C++ memory model

Memory ordering basics in concurrency
Memory ordering nuances
Memory ordering and memory barriers in modern C++ language
Acquire Barrier in Modern C++
Release Barrier in Modern C++
Using acquire and release barriers for synchronization in multithreading
Using memory barriers as locks for efficient concurreny with modern C++
Bidirectional barriers in Modern C++ memory model
Why does compare and exchange in C++ have two parameters for memory ordering?

Memory order : Software Design and performance cosniderations

Purpose of memory order in modern C++ concurrency memory model
Memory order as a tool to convey the C++ programmer’s intent
Memory order as programmer’s intent : Example – 1
Memory order as programmer’s intent : Example – 2
Memory order as programmer’s intent : Example – 3
Memory barriers and performance implications
Sequential consistency and performance implications
Design and implementation guidelines for using std::atomics
When to use the atomics provided by the modern C++?

Tools to work with concurrency and multithreading in C++ at production level

Demo code: Walkthrough of code sample being used to genreate results of tools
top utility output from Linux machine
htop utility output from Linux machine
perf utility output from Linux machine
gprof utility output from Linux machine
Intel Vtune utility output from Linux machine
Google Orbit utility output from Linux machine
Heaptrack utility output from Linux machine
Valgrind utility output from Linux machine
pahole utility output from Linux machine
Demonstration of false sharing in concurrent applications using modern C++
Other tools to explore: gdb, gcore, visual studio code, debugging tools
Conclusion
[Bonus Lecture]