0%

A comprehensive guide to help aspiring and professional C++ developers elevate the performance of their apps by allowing them to run faster and consume fewer resources

Key Features

  • Updated to C++20 with completely revised code and more content on error handling, benchmarking, memory allocators, and concurrent programming
  • Explore the latest C++20 features including concepts, ranges, and coroutines
  • Utilize C++ constructs and techniques to carry out effective data structure optimization and memory management

Book Description

C++ High Performance, Second Edition guides you through optimizing the performance of your C++ apps. This allows them to run faster and consume fewer resources on the device they're running on without compromising the readability of your codebase.

The book begins by introducing the C++ language and some of its modern concepts in brief. Once you are familiar with the fundamentals, you will be ready to measure, identify, and eradicate bottlenecks in your C++ codebase. By following this process, you will gradually improve your style of writing code. The book then explores data structure optimization, memory management, and how it can be used efficiently concerning CPU caches.

After laying the foundation, the book trains you to leverage algorithms, ranges, and containers from the standard library to achieve faster execution, write readable code, and use customized iterators. It provides hands-on examples of C++ metaprogramming, coroutines, reflection to reduce boilerplate code, proxy objects to perform optimizations under the hood, concurrent programming, and lock-free data structures. The book concludes with an overview of parallel algorithms.

By the end of this book, you will have the ability to use every tool as needed to boost the efficiency of your C++ projects.

What you will learn

  • Write specialized data structures for performance-critical code
  • Use modern metaprogramming techniques to reduce runtime calculations
  • Achieve efficient memory management using custom memory allocators
  • Reduce boilerplate code using reflection techniques
  • Reap the benefits of lock-free concurrent programming
  • Gain insights into subtle optimizations used by standard library algorithms
  • Compose algorithms using ranges library
  • Develop the ability to apply metaprogramming aspects such as constexpr, constraints, and concepts
  • Implement lazy generators and asynchronous tasks using C++20 coroutines

Who this book is for

If you're a C++ developer looking to improve the efficiency of your code or just keen to upgrade your skills to the next level, this book is for you.

Table of Contents

  1. Preface
    1. Who this book is for
    2. What this book covers
    3. Get the most out of this book
    4. Download the example code files
    5. Download the color images
    6. Conventions used
    7. Get in touch
    8. Reviews
  2. A Brief Introduction to C++
    1. Why C++?
    2. Zero-cost abstractions
    3. Programming languages and machine code abstractions
    4. Abstractions in other languages
    5. The zero-overhead principle
    6. Portability
    7. Robustness
    8. C++ of today
    9. C++ compared with other languages
    10. Competing languages and performance
    11. Non-performance-related C++ language features
    12. Value semantics
    13. Const correctness
    14. Object ownership
    15. Deterministic destruction in C++
    16. Avoiding null objects using C++ references
    17. Drawbacks of C++
    18. Libraries and compilers used in this book
    19. Summary
  3. Essential C++ Techniques
    1. Automatic type deduction with the auto keyword
    2. Using auto in function signatures
    3. Forwarding the return type using decltype(auto)
    4. Using auto for variables
    5. A const reference
    6. A mutable reference
    7. A forwarding reference
    8. Practices for ease of use
    9. Const propagation for pointers
    10. Move semantics explained
    11. Copy-construction, swap, and move
    12. Copy-constructing an object
    13. Resource acquisition and the rule of five
    14. Named variables and rvalues
    15. Default move semantics and the rule of zero
    16. Rule of zero in a real code base
    17. A common pitfall – moving non-resources
    18. Applying the && modifier to class member functions
    19. Don't move when copies are elided anyway
    20. Pass by value when applicable
    21. Cases where pass-by-value is not applicable
    22. Moving constructor parameters
    23. Designing interfaces with error handling
    24. Contracts
    25. Class invariants
    26. Maintaining contracts
    27. Error handling
    28. Programming error or runtime error?
    29. Programming errors (bugs)
    30. Recoverable runtime errors
    31. Function objects and lambda expressions
    32. The basic syntax of a C++ lambda
    33. The capture clause
    34. Capture by reference versus capture by value
    35. Similarities between a lambda and a class
    36. Initializing variables in capture
    37. Mutating lambda member variables
    38. Capture all
    39. Assigning C function pointers to lambdas
    40. Lambda types
    41. Lambdas and std::function
    42. Implementing a simple Button class with std::function
    43. Performance consideration of std::function
    44. Generic lambdas
    45. Summary
  4. Analyzing and Measuring Performance
    1. Asymptotic complexity and big O notation
    2. Growth rates
    3. Amortized time complexity
    4. What to measure and how?
    5. Performance properties
    6. Speedup of execution time
    7. Performance counters
    8. Performance testing — best practices
    9. Knowing your code and hot spots
    10. Instrumentation profilers
    11. Sampling profilers
    12. Microbenchmarking
    13. Amdahl's law
    14. Pitfalls of microbenchmarking
    15. A microbenchmark example
    16. Summary
  5. Data Structures
    1. The properties of computer memory
    2. The standard library containers
    3. Sequence containers
    4. Vectors and arrays
    5. Deque
    6. List and forward_list
    7. The basic_string
    8. Associative containers
    9. Ordered sets and maps
    10. Unordered sets and maps
    11. Container adaptors
    12. Priority queues
    13. Using views
    14. Avoiding copies with string_view
    15. Eliminating array decay with std::span
    16. Some performance considerations
    17. Balancing between complexity guarantees and overhead
    18. Knowing and using the appropriate API functions
    19. Parallel arrays
    20. Summary
  6. Algorithms
    1. Introducing the standard library algorithms
    2. Evolution of the standard library algorithms
    3. Solving everyday problems
    4. Iterating over a sequence
    5. Generating elements
    6. Sorting elements
    7. Finding elements
    8. Finding using binary search
    9. Testing for certain conditions
    10. Counting elements
    11. Minimum, maximum, and clamping
    12. Iterators and ranges
    13. Introducing iterators
    14. Sentinel values and past-the-end iterators
    15. Ranges
    16. Iterator categories
    17. Features of the standard algorithms
    18. Algorithms do not change the size of the container
    19. Algorithms with output require allocated data
    20. Algorithms use operator==() and operator<() by default
    21. Custom comparator functions
    22. Constrained algorithms use projections
    23. Algorithms require move operators not to throw
    24. Algorithms have complexity guarantees
    25. Algorithms perform just as well as C library function equivalents
    26. Writing and using generic algorithms
    27. Non-generic algorithms
    28. Generic algorithms
    29. Data structures that can be used by generic algorithms
    30. Best practices
    31. Using the constrained algorithms
    32. Sorting only for the data you need to retrieve
    33. Use cases
    34. Performance evaluation
    35. Use standard algorithms over raw for-loops
    36. Example 1: Readability issues and mutable variables
    37. Example 2: Unfortunate exceptions and performance problems
    38. Example 3: Exploiting the standard library optimizations
    39. Avoiding container copies
    40. Summary
  7. Ranges and Views
    1. The motivation for the Ranges library
    2. Limitations of the Algorithm library
    3. Understanding views from the Ranges library
    4. Views are composable
    5. Range views come with range adaptors
    6. Views are non-owning ranges with complexity guarantees
    7. Views don't mutate the underlying container
    8. Views can be materialized into containers
    9. Views are lazy evaluated
    10. Views in the standard library
    11. Range views
    12. Generating views
    13. Transforming views
    14. Sampling views
    15. Utility views
    16. Revisiting std::string_view and std::span
    17. The future of the Ranges library
    18. Summary
  8. Memory Management
    1. Computer memory
    2. The virtual address space
    3. Memory pages
    4. Thrashing
    5. Process memory
    6. Stack memory
    7. Heap memory
    8. Objects in memory
    9. Creating and deleting objects
    10. Placement new
    11. The new and delete operators
    12. Memory alignment
    13. Padding
    14. Memory ownership
    15. Handling resources implicitly
    16. Containers
    17. Smart pointers
    18. Unique pointer
    19. Shared pointer
    20. Weak pointer
    21. Small object optimization
    22. Custom memory management
    23. Building an arena
    24. A custom memory allocator
    25. Using polymorphic memory allocators
    26. Implementing a custom memory resource
    27. Summary
  9. Compile-Time Programming
    1. Introduction to template metaprogramming
    2. Creating templates
    3. Using integers as template parameters
    4. Providing specializations of a template
    5. How the compiler handles a template function
    6. Abbreviated function templates
    7. Receiving the type of a variable with decltype
    8. Type traits
    9. Type trait categories
    10. Using type traits
    11. Programming with constant expressions
    12. Constexpr functions in a runtime context
    13. Declaring immediate functions using consteval
    14. The if constexpr statement
    15. Comparison with runtime polymorphism
    16. Example of the generic modulus function using if constexpr
    17. Checking programming errors at compile time
    18. Using assert to trigger errors at runtime
    19. Using static_assert to trigger errors at compile time
    20. Constraints and concepts
    21. An unconstrained version of a Point2D template
    22. Generic interfaces and bad error messages
    23. A syntactic overview of constraints and concepts
    24. Defining new concepts
    25. Constraining types with concepts
    26. Function overloading
    27. A constrained version of the Point2D template
    28. Adding constraints to your code
    29. Concepts in the standard library
    30. Real-world examples of metaprogramming
    31. Example 1: creating a generic safe cast function
    32. Example 2: hash strings at compile time
    33. The advantages of the compile-time hash sum calculation
    34. Implementing and verifying a compile-time hash function
    35. Constructing a PrehashedString class
    36. Evaluating PrehashedString
    37. Evaluating get_bitmap_resource() with PrehashedString
    38. Summary
  10. Essential Utilities
    1. Representing optional values with std::optional
    2. Optional return values
    3. Optional member variables
    4. Avoiding empty states in enums
    5. Sorting and comparing std::optional
    6. Fixed size heterogenous collections
    7. Using std::pair
    8. The std::tuple
    9. Accessing the members of a tuple
    10. Iterating std::tuple members
    11. Unrolling the tuple
    12. Accessing tuple elements
    13. The variadic template parameter pack
    14. Dynamically sized heterogenous collections
    15. The std::variant
    16. Exception safety of std::variant
    17. Visiting variants
    18. Heterogenous collections using variant
    19. Accessing the values in our variant container
    20. Global function std::get()
    21. Some real-world examples
    22. Example 1: projections and comparison operators
    23. Example 2: reflection
    24. Making a class reflect its members
    25. C++ libraries that simplify reflection
    26. Using reflection
    27. Conditionally overloading global functions
    28. Testing reflection capabilities
    29. Summary
  11. Proxy Objects and Lazy Evaluation
    1. Introducing lazy evaluation and proxy objects
    2. Lazy versus eager evaluation
    3. Proxy objects
    4. Avoiding constructing objects using proxy objects
    5. Comparing concatenated strings using a proxy
    6. Implementing the proxy
    7. The rvalue modifier
    8. Assigning a concatenated proxy
    9. Performance evaluation
    10. Postponing sqrt computations
    11. A simple two-dimensional vector class
    12. The underlying mathematics
    13. Implementing the LengthProxy object
    14. Comparing lengths with LengthProxy
    15. Calculating length with LengthProxy
    16. Preventing the misuse of LengthProxy
    17. Performance evaluation
    18. Creative operator overloading and proxy objects
    19. The pipe operator as an extension method
    20. The pipe operator
    21. Summary
  12. Concurrency
    1. Understanding the basics of concurrency
    2. What makes concurrent programming hard?
    3. Concurrency and parallelism
    4. Time slicing
    5. Shared memory
    6. Data races
    7. Example: A data race
    8. Avoiding data races
    9. Mutex
    10. Deadlock
    11. Synchronous and asynchronous tasks
    12. Concurrent programming in C++
    13. The thread support library
    14. Threads
    15. Thread states
    16. Joinable thread
    17. Protecting critical sections
    18. Avoiding deadlocks
    19. Condition variables
    20. Returning data and handling errors
    21. Tasks
    22. Additional synchronization primitives in C++20
    23. Using latches
    24. Using barriers
    25. Signalling and resource counting using semaphores
    26. Atomic support in C++
    27. The lock-free property
    28. Atomic flags
    29. Atomic wait and notify
    30. Using shared_ptr in a multithreaded environment
    31. Atomic references
    32. The C++ memory model
    33. Instruction reordering
    34. Atomics and memory orders
    35. Lock-free programming
    36. Example: A lock-free queue
    37. Performance guidelines
    38. Avoid contention
    39. Avoid blocking operations
    40. Number of threads/CPU cores
    41. Thread priorities
    42. Thread affinity
    43. False sharing
    44. Summary
  13. Coroutines and Lazy Generators
    1. A few motivating examples
    2. The coroutine abstraction
    3. Subroutines and coroutines
    4. Executing subroutines and coroutines on the CPU
    5. CPU registers, instructions, and the stack
    6. Call and return
    7. Suspend and resume
    8. Stackless versus stackful coroutines
    9. Performance cost
    10. Memory footprint
    11. Context switching
    12. What you have learned so far
    13. Coroutines in C++
    14. What's included in standard C++ (and what's not)?
    15. What makes a C++ function a coroutine?
    16. A minimal but complete example
    17. The coroutine return object
    18. The promise type
    19. Awaitable types
    20. Passing our coroutine around
    21. Allocating the coroutine state
    22. Avoiding dangling references
    23. Passing parameters to coroutines
    24. Member functions that are coroutines
    25. Lambdas that are coroutines
    26. Guidelines to prevent dangling references
    27. Handling errors
    28. Customization points
    29. Generators
    30. Implementing a generator
    31. Using the Generator class
    32. Solving generator problems
    33. An iterator implementation
    34. A solution using the Ranges library
    35. Conclusion
    36. A real-world example using generators
    37. The problem
    38. Delta encoding
    39. Performance
    40. Summary
  14. Asynchronous Programming with Coroutines
    1. Awaitable types revisited
    2. The implicit suspend points
    3. Implementing a rudimentary task type
    4. Handling return values and exceptions
    5. Resuming an awaiting coroutine
    6. Supporting void tasks
    7. Synchronously waiting for a task to complete
    8. Implementing sync_wait()
    9. Implementing SyncWaitTask
    10. Testing asynchronous tasks with sync_wait()
    11. Wrapping a callback-based API
    12. A concurrent server using Boost.Asio
    13. Implementing the server
    14. Running and connecting to the server
    15. What we have achieved with the server (and what we haven't)
    16. Summary
  15. Parallel Algorithms
    1. The importance of parallelism
    2. Parallel algorithms
    3. Evaluating parallel algorithms
    4. Amdahl's law revisited
    5. Implementing parallel std::transform()
    6. Naive implementation
    7. Divide and conquer
    8. Implementing parallel std::count_if()
    9. Implementing parallel std::copy_if()
    10. Approach 1: Use a synchronized write position
    11. Approach 2: Split the algorithm into two parts
    12. Performance evaluation
    13. Parallel standard library algorithms
    14. Execution policies
    15. Sequenced policy
    16. Parallel policy
    17. Unsequenced policy
    18. Parallel unsequenced policy
    19. Exception handling
    20. Additions and changes to parallel algorithms
    21. std::accumulate() and std::reduce()
    22. std::for_each()
    23. Parallelizing an index-based for-loop
    24. Combining std::for_each() with std::views::iota()
    25. Simplifying construction via a wrapper
    26. Executing algorithms on the GPU
    27. Summary
  16. Other Books You May Enjoy
  17. Index
18.117.196.217