35,99 €
Write code that scales across CPU registers, multi-core, and machine clusters
C++ is a highly portable language and can be used to write both large-scale applications and performance-critical code. It has evolved over the last few years to become a modern and expressive language. This book will guide you through optimizing the performance of your C++ apps by allowing them to run faster and consume fewer resources on the device they're running on without compromising the readability of your code base.
The book begins by helping you measure and identify bottlenecks in a C++ code base. It then moves on by teaching you how to use modern C++ constructs and techniques. You'll see how this affects the way you write code. Next, you'll see the importance of data structure optimization and memory management, and how it can be used efficiently with respect to CPU caches. After that, you'll see how STL algorithm and composable Range V3 should be used to both achieve faster execution and more readable code, followed by how to use STL containers and how to write your own specialized iterators.
Moving on, you’ll get hands-on experience in making use of modern C++ metaprogramming and reflection to reduce boilerplate code as well as in working with proxy objects to perform optimizations under the hood. After that, you’ll learn concurrent programming and understand lock-free data structures. The book ends with an overview of parallel algorithms using STL execution policies, Boost Compute, and OpenCL to utilize both the CPU and the GPU.
If you're a C++ developer looking to improve the speed of your code or simply wanting to take your skills up to the next level, then this book is perfect for you.
Viktor Sehr is the main developer at Toppluva, working with a highly-optimized graphics engine aimed for mobile hardware. He has 10 years of professional experience using C++, with real-time graphics, audio, and architectural design as his focus areas. Through his career, he has developed medical visualization software at Mentice and Raysearch Laboratories as well as real-time audio applications at Propellerhead Software. Viktor holds an M.S. in media science from Linköping University. Björn Andrist is a freelance software consultant currently focusing on audio applications. For more than 10 years, he has been working professionally with C++ in projects ranging from Unix server applications to real-time audio applications on desktop and mobile. In the past, he has also taught courses in algorithms and data structures, concurrent programming, and programming methodologies. Björn holds a BS in computer engineering and an MS in computer science from KTH Royal Institute of Technology.Sie lesen das E-Book in den Legimi-Apps auf:
Seitenzahl: 449
Veröffentlichungsjahr: 2018
Copyright © 2018 Packt Publishing
All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.
Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author(s), nor Packt Publishing or its dealers and distributors, will be held liable for any damages caused or alleged to have been caused directly or indirectly by this book.
Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.
Commissioning Editors: Aaron LazarAcquisition Editor: Denim PintoContent Development Editors: Nikhil BorkarTechnical Editor: Jijo MaliyekalCopy Editor: Safis EditingProject Coordinator: Vaidehi SawantProofreader: Safis EditingIndexers: Rekha NairGraphics: Tania DuttaProduction Coordinator: Arvindkumar Gupta
First published: January 2018
Production reference: 1300118
Published by Packt Publishing Ltd. Livery Place 35 Livery Street Birmingham B3 2PB, UK.
ISBN 978-1-78712-095-2
www.packtpub.com
Mapt is an online digital library that gives you full access to over 5,000 books and videos, as well as industry leading tools to help you plan your personal development and advance your career. For more information, please visit our website.
Spend less time learning and more time coding with practical eBooks and Videos from over 4,000 industry professionals
Improve your learning with Skill Plans built especially for you
Get a free eBook or video every month
Mapt is fully searchable
Copy and paste, print, and bookmark content
Did you know that Packt offers eBook versions of every book published, with PDF and ePub files available? You can upgrade to the eBook version at www.PacktPub.com and as a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at [email protected] for more details.
At www.PacktPub.com, you can also read a collection of free technical articles, sign up for a range of free newsletters, and receive exclusive discounts and offers on Packt books and eBooks.
C++ stepped onto the stage in 1983 and hasn’t stopped reinventing itself for a single year since. It has gone from a single frontend language on top of C to a first-class citizen of the compiler world. Each new C++ standard has added substantial functionality, sometimes to excess. In the words of Stroustrup, “Within C++, there is a much smaller and cleaner language struggling to get out.”
The problem, of course, is that the “smaller and cleaner language” to be found within C++ changes with situation. Mastering C++ is akin to mastering a multitude of domain-specific languages, each tailored to a specific use. The dialect that makes sense for embedded system is nonsensical for large enterprise applications, and the phrasing that powers a game engine is unbearable when applied to a word processor.
C++ High Performance teaches you a C++ dialect for rapidly developing high-performance code. From C++11 onward, there have been a vast array of features in both the C++ language and the C++ STL that let you spend more time writing your application and less time handling implementation details. This is the focus of the book and where it shines.
Each topic is framed in the larger context of application development and computer science. For the reader who needs to get up to speed with the latest C++ techniques on a short notice, this information provides the necessary landmarks to stay oriented. Specific examples, recipes, and logical progressions take you from a basic use of auto and <algorithm> all the way up to using Boost to transparently run your algorithms on the GPU via OpenCL. Fundamental issues around modern C++ (memory management and ownership, considerations of time and space, advance template usage, and others) are explained step by step so that, by the later chapters, the readers proceed into advanced territory with confidence.
I have worked on a wide variety of projects—large and small, low level and managed, and some even in custom languages I designed and built—but C++ holds a special place in my heart. My first full-time job was writing C++ code for a game technology company in the early 2000s. I loved it, not least of all because a big part of the technology revolved around the reflection of the C++ code base into the editor and scripting language. Someone once described C++ as an octopus made by nailing extra legs onto a dog, and I spent a lot of time with a hammer making our code base do things that C++ was never intended to do. Yet, the octopus we ended up with was, in its own way, beautiful and very effective.
C++ has evolved tremendously since those days, and it is my privilege to open the door for you as you walk into an exciting new world of possibilities. Viktor and Björn are brilliant and experienced developers with a remarkable pedigree, and they have a lot of great things in store for you.
Ben Garney CEO, The Engine Company
Viktor Sehr is the main developer at Toppluva, working with a highly-optimized graphics engine aimed for mobile hardware.He has 10 years of professional experience using C++, with real-time graphics, audio, and architectural design as his focus areas. Through his career, he has developed medical visualization software at Mentice and Raysearch Laboratories as well as real-time audio applications at Propellerhead Software. Viktor holds an M.S. in media science from Linköping University.
Björn Andrist is a freelance software consultant currently focusing on audio applications. For more than 10 years, he has been working professionally with C++ in projects ranging from Unix server applications to real-time audio applications on desktop and mobile. In the past, he has also taught courses in algorithms and data structures, concurrent programming, and programming methodologies. Björn holds a BS in computer engineering and an MS in computer science from KTH Royal Institute of Technology.
Sergey Gomon started his journey in IT 10 years ago in Belarus State University of Informatics and Radioelectronics in the artificial intelligence department. He has about 8 years of industrial programming experience using C++ in several fields, such as network programming, information security, and image processing. In his free time, he likes reading, traveling, and studying something new.
He currently works at Regula and SolarWinds MSP, and is an activist of the CoreHard C++ community.
Louis E. Mauget never saw the ENIAC but coded in those languages, adding several modern languages and frameworks. C++ continues to evolve, and Lou evolves with it. Interested in reactive functional programming, containers, and deep learning, he blogs about software technology for Keyhole Software, where he works as a senior software engineer. A coauthor of three computer books, he also wrote IBM developerWorks tutorials and a WebSphere Journal two-part LDAP tutorial. He cowrote several J2EE certification tests for IBM and has been a reviewer for Packt Publishing and others.
If you're interested in becoming an author for Packt, please visit authors.packtpub.com and apply today. We have worked with thousands of developers and tech professionals, just like you, to help them share their insight with the global tech community. You can make a general application, apply for a specific hot topic that we are recruiting an author for, or submit your own idea.
Preface
Who this book is for
What this book covers
To get the most out of this book
Download the example code files
Conventions used
Get in touch
Reviews
A Brief Introduction to C++
Why C++?
Zero-cost abstractions
Programming languages and machine code abstractions
Abstractions in other languages
Portability
Robustness
C++ of today
The aim of this book
Expected knowledge of the reader
C++ compared with other languages
Competing languages and performance
Non-performance-related C++ language features
Value semantics
Const correctness
Object ownership and garbage collection in C++
Avoiding null objects using C++ references
Drawbacks of C++
Class interfaces and exceptions
Strict class interfaces
Error handling and resource acquisition
Preserving the valid state
Resource acquisition
Exceptions versus error codes
Libraries used in this book
Summary
Modern C++ Concepts
Automatic type deduction with the auto keyword
Using auto in function signatures
Using auto for variables
Const reference
Mutable reference
Forwarding reference
Conclusion
The lambda function
Basic syntax of a C++ lambda function
The capture block
Capture by reference versus capture by value
Similarities between a Lambda and a class
Initializing variables in capture
Mutating lambda member variables
Mutating member variables from the compiler's perspective
Capture all
Assigning C function pointers to lambdas
Lambdas and std::function
Assigning lambdas to std::functions
Implementing a simple Button class with std::function
Performance consideration of std::function
An std::function cannot be inlined
An std::function heap allocates and captures variables
Invoking an std::function requires a few more operations than a lambda
The polymorphic lambda
Creating reusable polymorphic lambdas
Const propagation for pointers
Move semantics explained
Copy-construction, swap, and move
Copy-constructing an object
Swapping two objects
Move-constructing an object
Resource acquisition and the rule of three
Implementing the rule of three
Constructor
Limitations of the rule of three
Avoiding copies without move semantics
Introducing move semantics
Named variables and r-values
Accept arguments by move when applicable
Default move semantics and the rule of zero
Rule of zero in a real code base
A note on empty destructors
A common pitfall - moving non-resources
Applying the && modifier to class member functions
Representing optional values with std::optional
Optional return values
Optional member variables
Sorting and comparing std::optional
Representing dynamic values with std::any
Performance of std::any
Summary
Measuring Performance
Asymptotic complexity and big O notation
Growth rates
Amortized time complexity
What to measure?
Performance properties
Performance testing – best practices
Knowing your code and hot spots
Profilers
Instrumentation profilers
Sampling profilers
Summary
Data Structures
Properties of computer memory
STL containers
Sequence containers
Vector and array
Deque
List and forward_list
The basic_string
Associative containers
Ordered sets and maps
Unordered sets and maps
Hash and equals
Hash policy
Container adaptors
Priority queues
Parallel arrays
Summary
A Deeper Look at Iterators
The iterator concept
Iterator categories
Pointer-mimicking syntax
Iterators as generators
Iterator traits
Implementing a function using iterator categories
Extending the IntIterator to bidirectional
Practical example – iterating floating point values within a range
Illustrated usage examples
Utility functions
How to construct a linear range iterator
Iterator usage example
Generalizing the iterator pair to a range
The make_linear_range convenience function
Linear range usage examples
Summary
STL Algorithms and Beyond
Using STL algorithms as building blocks
STL algorithm concepts
Algorithms operate on iterators
Implementing a generic algorithm that can be used with any container
Iterators for a range point to the first element and the element after the last
Algorithms do not change the size of the container
Algorithms with output require allocated data
Algorithms use operator== and operator< by default
Custom comparator function
General-purpose predicates
Algorithms require move operators not to throw
Algorithms have complexity guarantees
Algorithms perform just as well as C library function equivalents
STL algorithms versus handcrafted for-loops
Readability and future-proofing
Real-world code base example
Usage examples of STL algorithms versus handcrafted for-loops
Example 1 – Unfortunate exceptions and performance problems
Example 2 – STL has subtle optimizations even in simple algorithms
Sorting only for the data you need to retrieve
Use cases
Performance evaluation
The future of STL and the ranges library
Limitations of the iterators in STL
Introduction to the ranges library
Composability and pipeability
Actions, views, and algorithms
Actions
Views
Algorithms
Summary
Memory Management
Computer memory
The virtual address space
Memory pages
Thrashing
Process memory
Stack memory
Heap memory
Objects in memory
Creating and deleting objects
Placement new
The new and delete operators
Memory alignment
Padding
Memory ownership
Handling resources implicitly
Containers
Smart pointers
Unique pointer
Shared pointer
Weak pointer
Small size optimization
Custom memory management
Building an arena
A custom memory allocator
Summary
Metaprogramming and Compile-Time Evaluation
Introduction to template metaprogramming
Using integers as template parameters
How the compiler handles a template function
Using static_assert to trigger errors at compile time
Type traits
Type trait categories
Using type traits
Receiving the type of a variable with decltype
Conditionally enable functions based on types with std::enable_if_t
Introspecting class members with std::is_detected
Usage example of is_detected and enable_if_t combined
The constexpr keyword
Constexpr functions in a runtime context
Verify compile-time computation using std::integral_constant
The if constexpr statement
Comparison with runtime polymorphism
Example of generic modulus function using if constexpr
Heterogeneous containers
Static-sized heterogenous containers
The std::tuple container
Accessing the members of a tuple
Iterating std::tuple
Unrolling the tuple
Implementing other algorithms for tuples
Accessing tuple elements
Structured bindings
The variadic template parameter pack
An example of a function with variadic number of arguments
How to construct a variadic parameter pack
Dynamic-sized heterogenous containers
Using std::any as the base for a dynamic-size heterogenous container
The std::variant
Visiting variants
Heterogenous container of variants
Accessing the values in our variant container
Global function std::get
Real world examples of metaprogramming
Example 1 – Reflection
Making a class reflect its members
C++ libraries which simplifies reflection
Using the reflection
Evaluating the assembler output of the reflection
Conditionally overloading global functions
Testing reflection capabilities
Example 2 – Creating a generic safe cast function
Example 3 – Hash strings at compile time
The advantages of compile-time hash sum calculation
Implement and verify a compile-time hash function
Constructing a PrehashedString class
Forcing PrehashedString to only accept compile time string literals
Evaluating PrehashedString
Evaluating get_bitmap_resource() with PrehashedString
Summary
Proxy Objects and Lazy Evaluation
An introduction to lazy evaluation and proxy objects
Lazy versus eager evaluation
Proxy objects
Comparing concatenated strings using a proxy
Implementing the proxy
Performance evaluation
The r-value modifier
Assigning a concatenated proxy
Postponing an sqrt computation when comparing distances
A simple two-dimensional point class
The underlying mathematics
Implementing the DistProxy object
Expanding DistProxy to something more useful
Comparing distances with DistProxy
Calculating distances with DistProxy
Preventing the misuse of DistProxy
Performance evaluation
Creative operator overloading and proxy objects
The pipe operator as an extension method
The pipe operator
The infix operator
Further reading
Summary
Concurrency
Understanding the basics of concurrency
What makes concurrent programming hard?
Concurrency and parallelism
Time slicing
Shared memory
Data races
Mutex
Deadlock
Synchronous and asynchronous tasks
Concurrent programming in C++
The thread support library
Threads
Thread states
Protecting critical sections
Avoiding deadlocks
Condition variables
Returning data and handling errors
Tasks
Atomic support in C++
Using shared_ptr in a multithreaded environment
C++ memory model
Instruction reordering
Atomics and memory orders
Lock-free programming
Lock-free queue example
Performance guidelines
Avoid contention
Avoid blocking operations
Number of threads/CPU cores
Thread priorities
Thread affinity
False sharing
Summary
Parallel STL
Importance of parallelism
Parallel algorithms
Implementing parallel std::transform()
Naive implementation
Performance evaluation
Shortcomings of the naive implementation
Divide and conquer
Implementation
Performance evaluation
Implementing parallel std::count_if
Implementing parallel std::copy_if
Approach one – Use a synchronized write position
Inner function
Outer function
Approach two – Split algorithm into two parts
Part one – Copy elements in parallel into the destination range
Part two – Move the sparse range sequentially into a continuous range
Performance evaluation
Parallel STL
Execution policies
Sequenced policy
Parallel policy
Parallel unsequenced policy
Parallel modifications of algorithm
std::accumulate and std::reduce
std::transform_reduce
std::for_each
Parallelizing an index-based for-loop
Combining std::for_each with linear range
Simplifying construction via a wrapper
Executing STL algorithms on the GPU
GPU APIs and parallel operations
Programmable GPUs
Shader programs
STL algorithms and the GPU
Boost Compute
Basic concepts of Boost Compute
OpenCL
Initializing Boost Compute
Transfer a simple transform-reduce algorithm to Boost Compute
The algorithm in standard C++
Transforming the algorithm to Boost Compute
Adapting the circle struct for use with Boost Compute
Converting circle_area_cpu to Boost Compute
The BOOST_COMPUTE_FUNCTION macro
Implementing the transform-reduction algorithm on the GPU
Using predicates with Boost Compute
Using a custom kernel in Boost Compute
Box filter
Implementing the kernel
Parallelizing for two dimensions
Verify GPU computation on the CPU
Summary
Other Books You May Enjoy
Leave a review - let other readers know what you think
C++ of today provides programmers the ability to write expressive and robust code while still having the ability to target almost any hardware platform or real-time requirements. This makes C++ a unique language. Over the last few years, C++ has turned into a modern language that is more fun to use and with better defaults.
This book aims to give the reader a solid foundation to write efficient applications as well as an insight into strategies for implementing libraries in modern C++. We have tried to take a practical approach to explain how C++ works today, where C++14/C++17 features are a natural part of the language, rather than looking at C++ historically.
This book has been written by us, Viktor and Björn, collaboratively. However, the drafts of each chapter were written individually, and after that, we have worked together to improve the chapters and assemble them into a complete book. Viktor is the main author of chapter 1, 2, 5, 8, 9, and 11. Björn is the main author of chapter 3, 4, 7, and 10. We have worked hard to attain a consistent style throughout the book, and we think that it has been a big advantage to write this book together. Many subjects have been debated and processed for the better.
This book expects you to have a basic knowledge of C++ and computer architecture and a genuine interest in evolving your skills. Hopefully, by the time you finish this book, you will have gained a few insights into how you can improve your C++ applications, both performance-wise and syntactically. On top of that, we also hope that you will have a fewahamoments.
Chapter 1,A Brief Introduction to C++, introduces some important properties of C++ such as zero-cost abstractions, value semantics, const correctness, explicit ownership, and error handling. It also discusses the drawbacks of C++.
Chapter 2, Modern C++ Concepts, outlines automatic type deduction using auto, lambda functions, move semantics, std::optional, and std::any.
Chapter 3, Measuring Performance, discusses asymptotic complexity and big O notation, practical performance testing, and how to profile your code to find hotspots.
Chapter 4, Data Structures, takes you through the importance of structuring the data so that it can be accessed quickly. STL containers such as std::vector, std::list, std::unordered_map, and std::priority_queue are introduced. Finally, we describe how to iterate over parallel arrays.
Chapter 5, A Deeper Look at Iterators, dives into the concept of iterators, and shows how iterators can go beyond just referring to objects in containers.
Chapter 6, STL Algorithms and Beyond, shows the obvious, and the not so obvious, advantages of STL algorithms over hand rolled for loops. It also takes a look at the limitations of STL algorithms and how the new Ranges library overcomes these limits.
Chapter 7, Memory Management, focuses on safe and efficient memory management. This includes memory ownership, RAII, smart pointers, stack memory, dynamic memory, and custom memory allocators.
Chapter 8, Metaprogramming and Compile-Time Evaluation, explains metaprogramming concepts such as constexpr, heterogeneous containers, type_traits, std::enable_if, and std::is_detected. It also gives practical examples of metaprogramming use cases, such as reflection.
Chapter 9, Proxy Objects and Lazy Evaluation, explores how proxy objects can be used to perform under-the-hood optimizations while preserving clean syntax. Additionally, some creative uses of operator-overloading are demonstrated.
Chapter 10, Concurrency, covers the fundamentals of concurrent programming, including parallel execution, shared memory, data races, and deadlocks. It also includes an introduction to the C++ thread support library, the atomic library, and the C++ memory model.
Chapter 11, Parallel STL, starts by showing the complexity of writing parallel algorithms. It then demonstrates how to utilize STL algorithms in a parallel context using the parallel extensions for STL and Boost Compute.
To get the most out of this book, you need to have a basic knowledge of C++. It's preferable if you have already been facing problems related to performance and are now looking for new tools and practices to have ready the next time you need to work with performance and C++.
There are a lot of code examples in this book. Some are taken from the real world, but most of them are artificial or vastly simplified examples to prove a concept rather than providing you with production-ready code. We have put all the code examples in source files divided by chapter so that it is fairly easy to find the examples you want to experiment with. If you open up the source code files, you will note that we have replaced most of the main() functions from the examples with test cases written with Google Test framework. We hope that this will help you rather than confuse you. It allowed us towrite helpful descriptions for each example, and it also makes it easier to run all the examples from one chapter at once.
In order to compile and run the examples, you will need the following:
A computer
An operation system (we have verified the examples on Windows and macOS)
A compiler (we have been using Clang, GCC, and
Microsoft Visual C++)
CMake
The CMake script provided with the example code will download and install further dependencies such as Boost, OpenCL, and Google Test.
During the writing of this book, it has been of great help for us to use Compiler Explorer, which is available at https://godbolt.org/. Compiler Explorer is an online compiler service that lets you try various compilers and versions. Try it out if you haven't already!
You can download the example code files for this book from your account at www.packtpub.com. If you purchased this book elsewhere, you can visit www.packtpub.com/support and register to have the files emailed directly to you.
You can download the code files by following these steps:
Log in or register at
www.packtpub.com
.
Select the
SUPPORT
tab.
Click on
Code Downloads & Errata
.
Enter the name of the book in the
Search
box and follow the onscreen instructions.
Once the file is downloaded, please make sure that you unzip or extract the folder using the latest version of:
WinRAR/7-Zip for Windows
Zipeg/iZip/UnRarX for Mac
7-Zip/PeaZip for Linux
The code bundle for the book is also hosted on GitHub athttps://github.com/PacktPublishing/Cpp-High-Performance. We also have other code bundles from our rich catalog of books and videos available athttps://github.com/PacktPublishing/. Check them out!
There are a number of text conventions used throughout this book.
CodeInText: Indicates code words in text, folder names, filenames, file extensions, dummy URLs, and user input. Here is an example: "The keyword constexpr was introduced in C++11."
A block of code is set as follows:
#include <iostream>auto main() -> int { std::cout << "High Performance C++\n";}
When we wish to draw your attention to a particular part of a code block, the relevant lines or items are set in bold:
#include <iostream>auto main() -> int {
std::cout << "High Performance C++\n";
}
Any command-line input or output is written as follows:
$ clang++ -std=c++17 high_performance.cpp
$ ./a.out$ High Performance C++
Bold: Indicates a new term, an important word, or words that you see onscreen. For example, words in menus or dialog boxes appear in the text like this. Here is an example: "Select System info from the Administration panel."
Feedback from our readers is always welcome.
General feedback: Email [email protected] and mention the book title in the subject of your message. If you have questions about any aspect of this book, please email us at [email protected].
Errata: Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you have found a mistake in this book, we would be grateful if you would report this to us. Please visit www.packtpub.com/submit-errata, selecting your book, clicking on the Errata Submission Form link, and entering the details.
Piracy: If you come across any illegal copies of our works in any form on the Internet, we would be grateful if you would provide us with the location address or website name. Please contact us at [email protected] with a link to the material.
If you are interested in becoming an author: If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, please visit authors.packtpub.com.
Please leave a review. Once you have read and used this book, why not leave a review on the site that you purchased it from? Potential readers can then see and use your unbiased opinion to make purchase decisions, we at Packt can understand what you think about our products, and our authors can see your feedback on their book. Thank you!
For more information about Packt, please visit packtpub.com.
This chapter will introduce some of the features of C++ that we think are important for writing robust and high performance applications. We will also discuss advantages and disadvantages of C++ over languages based upon a garbage collector. Last, we will look at some examples of how to handle exceptions and resources.
We begin this book by exploring some of the reasons for using C++ today. In short, C++ is a highly portable language which offers zero-cost abstractions. Furthermore, we believe that C++ provides programmers with the ability to write and manage large, expressive, and robust code bases. Let's explore the meaning of each of these properties.
Active code bases grow. The more developers working on a code base, the larger the code base becomes. We need abstractions such as functions, classes, data structures, layers and so on in order to manage the complexity of a large-scale code base. But constantly adding abstractions and new levels of indirection comes at a price — efficiency. This is where zero-cost abstractions plays its role. A lot of the abstractions offered by C++ comes at a very low price. At a minimum, C++ offers efficient alternatives at hot spots where performance really is a concern.
With C++ you are free to talk about memory addresses and other computer related low-level terms when needed. However, in a large-scale software project it is desirable to express code in terms that deals with whatever the application is doing, and let the libraries handle the computer related terminology. The source code of a graphics application may deal with pencils, colors, and filters, whereas a game may deal with mascots, castles, and mushrooms. Low-level computer-related terms such as memory addresses can stay hidden in C++ library code where performance is critical.
By library code, we refer to code whose concepts are not strictly related to the application. The line between library code and application code can be blurry though, and libraries are often built upon other libraries. An example could be the container algorithms provided in the Standard Template Library (STL) of C++ or a general-purpose math library.
Most programming languages are based on abstractions, which are transformed into machine code to be executed by the CPU. C++ has evolved into a highly expressive language just like many of the other popular programming languages of today. What distinguishes C++ from most other languages is that, while the other languages have implemented these abstractions at the cost of runtime performance, C++ has always strived to implement its abstractions at zero cost at runtime. This doesn't mean that an application written in C++ is by default faster than the equivalent in, say, C#. Rather, it means that by using C++, you'll have explicit control of the emitted machine code instructions and memory footprint if needed.
To be fair, optimal performance is very rarely required today and compromising performance for lower compilation times, garbage collection, or safety, like other languages do, is in many cases more reasonable.
C++ has been a popular and comprehensive language for a long time. It's highly compatible with C and very little has been deprecated in the language, for better or worse. The history and design of C++ has made it to a highly portable language, and the evolution of modern C++ has ensured that it will stay that way for a long time to come. C++ is a living language and compiler vendors are currently doing a remarkable job to implement new language features rapidly.
In addition to performance, expressiveness, and portability, C++ offers a set of language features that gives the programmer the ability to write robust code.
In our experience, robustness does not refer to strength in the programming language itself – it's possible to write robust code in any language. However, strict ownership of resources, const correctness, value semantics, type safety, and deterministic destruction of objects are some of the features offered by C++ that makes it easier to write robust code. That is, the ability to write functions, classes, and libraries that are easy to use and hard to misuse.
To sum it up, C++ of today provides programmers the ability to write an expressive and robust code base while still having the ability to target almost any hardware platform or real-time requirements. Of the most commonly used languages today, C++ is the only one that gives all of these properties.
This book aims to give the reader a solid foundation to write efficient applications as well as an insight into strategies for implementing the libraries in modern C++. We have tried to take a practical approach to explain how C++ works today where C++14/C++17 features are a natural part of the language, rather than looking at C++ historically.
This book expects you to have a basic knowledge of C++ and computer architecture, and a genuine interest in evolving your skills. Hopefully, by the time you finish this book, you will have gained a few insights into how you can improve your C++ applications, both performance-wise and syntactically. On top of that, we also hope that you will have a few aha moments.
A multitude of application types, platforms, and programming languages have emerged since C++ was first released. Still, C++ is a widely used language, and its compilers are available for most platforms. The major exception, as of today, is the web platform, where JavaScript and its related technologies are the foundation. However, the web platform is evolving into being able to execute what was previously only possible in desktop applications, and in that context C++ has found its way into web applications using technologies such as Emscripten/asm.js and web assembly.
In some discussions about C++ versus other languages, it's concluded that C++ should only be used if performance is a major concern. Otherwise, it's said to just increase the complexity of the code base due to manual memory handling, which may result in memory leaks and hard-to-track bugs.
This may have been true several C++ versions ago, but a modern C++ programmer relies on the provided containers and smart pointer types, which are part of the STL.
We would here like to highlight two powerful features of C++ related to robustness rather than performance, that we think are easily overlooked: value semantics and const correctness.
Except in very rare situations, a C++ programmer should leave the memory handling to containers and smart pointers and never have to rely on manual memory handling.
To put it clearly, the garbage collection model in Java could almost be emulated in C++ by using std::shared_ptr for every object. Note that garbage-collecting languages don't use the same algorithm for allocation tracking as std::shared_ptr. The std::shared_ptr is a smart pointer based on a reference-counting algorithm that will leak memory if objects have cyclic dependencies. Garbage-collecting languages have more sophisticated methods that can handle and free cyclic dependent objects.
However, rather than relying on a garbage collector, forcing a strict ownership delicately avoids subtle bugs that may result from sharing objects by default, as in the case of Java.
If a programmer minimize shared ownership in C++, the resulting code is easier to use and harder to abuse, as it can force the user of the class to use it as it is intended.
Comparing C++ with other programming languages wouldn't be fair without mentioning some of its drawbacks. As mentioned earlier, C++ has more concepts to learn, and is therefore harder to use correctly and to its full potential. However, if a programmer can master C++, the higher complexity turns into an advantage and the code base becomes more robust and performs better.
There are, nonetheless, some shortcomings of C++, which are simply just shortcomings. The most severe of those shortcomings are long compilation times, the reliance on the manual handling of forward declarations, header/source files, and the complexity of importing libraries.
This is mainly a result of C++ relying on an outdated import system where imported headers are simply pasted into whatever includes them. At the time of writing this book, a modern module-based import system is up for standardization, but until the standardized C++ version becomes available, project management remains very tedious.
Another apparent drawback of C++ is the lack of provided libraries. While other languages usually come with all the libraries needed for most applications, such as graphics, user interfaces, networking, threading, resource handling, and so on, C++ provides, more or less, nothing more than the bare minimum of algorithms, threads, and, as of C++17, file system handling. For everything else, programmers have to rely on external libraries.
To summarize, although C++ has a steeper learning curve than most other languages, if used correctly, the robustness of C++ is an advantage compared to many other languages. So, despite the outdated import/library system of C++, we believe that C++ is a well suited language for large-scale projects, even for projects where performance is not the highest priority.
Before diving deeper into the concepts of C++ high performance, we would like to emphasize some concepts that you should not compromise on when writing C++ code.
