HAPPY BOOKSGIVING
Use code BOOKSGIVING during checkout to save 40%-55% on books and eBooks. Shop now.
Register your product to gain access to bonus material or receive a coupon.
Far too many programmers and software designers consider efficient C++ to be an oxymoron. They regard C++ as inherently slow and inappropriate for performance-critical applications. Consequently, C++ has had little success penetrating domains such as networking, operating system kernels, device drivers, and others.
Efficient C++ explodes that myth. Written by two authors with first-hand experience wringing the last ounce of performance from commercial C++ applications, this book demonstrates the potential of C++ to produce highly efficient programs. The book reveals practical, everyday object-oriented design principles and C++ coding techniques that can yield large performance improvements. It points out common pitfalls in both design and code that generate hidden operating costs.
This book focuses on combining C++'s power and flexibility with high performance and scalability, resulting in the best of both worlds. Specific topics include temporary objects, memory management, templates, inheritance, virtual functions, inlining, reference-counting, STL, and much more.
With this book, you will have a valuable compendium of the best performance techniques at your fingertips.
(Each chapter concludes with Key Points.)
Preface.
Introduction.
Roots of Software Inefficiency.
Our Goal.
Software Efficiency: Does It Matter?
Terminology.
Organization of This Book.
1. The Tracing War Story.
Our Initial Trace Implementation.
What Went Wrong.
The Recovery Plan.
Inheritance.
Composition.
Lazy Construction.
Redundant Construction.
Key Points.
Virtual Function Mechanics.
Templates and Inheritance.
Hard Coding.
Inheritance.
Templates.
The Mechanics of Return-by-Value.
The Return Value Optimization.
Computational Constructors.
Object Definition.
Type Mismatch.
Pass by Value.
Return by Value.
Eliminate Temporaries with op=().
Version 0: The Global new() and delete().
Version 1: Specialized Rational Memory Manager.
Version 2: Fixed-Size Object Memory Pool.
Version 3: Single-Threaded Variable-Size Memory Manager.
Version 4: Implementation.
Version 5: Faster Locking.
What Is Inlining?
Method Invocation Costs.
Why Inline?
Inlining Details.
Inlining Virtual Methods.
Performance Gains from Inlining.
Cross-Call Optimization.
Why Not Inline?
Development and Compile-Time Inlining
Considerations. Profile-Based
Inlining.
Inlining Rules.
Singletons.
Trivials.
Conditional Inlining.
Selective Inlining.
Recursive Inlining.
Inlining with Static Local Variables.
Architectural Caveat: Multiple Register Sets.
Asymptotic Complexity.
Insertion.
Deletion.
Traversal.
Find.
Function Objects.
Better than STL?
Implementation Details.
Preexisting Classes.
Concurrent Reference Counting.
Caching.
Precompute.
Reduce Flexibility.
80-20 Rule: Speed Up the Common Path.
Lazy Evaluation.
Useless Computations.
System Architecture.
Memory Management.
Library and System Calls.
Compiler Optimization.
Design Flexibility.
Caching.
Web Server Timestamps.
Data Expansion.
The Common Code Trap.
Efficient Data Structures.
Lazy Evaluation.
getpeername().
Useless Computations.
Obsolete Code.
The SMP Architecture.
Amdahl’s Law.
Multithreaded and Synchronization Terminology.
Break Up a Task into Multiple Subtasks.
Cache Shared Data.
Share Nothing.
Partial Sharing.
Lock Granularity.
False Sharing.
Thundering Herd.
Reader/Writer Locks.
Memory Hierarchies.
Registers: Kings of Memory.
Disk and Memory Structures.
Cache Effects.
Cache Thrash.
Avoid Branching.
Prefer Simple Calculations to Small Branches.
Threading Effects.
Context Switching.
Kernel Crossing.
Threading Choices.
If you conducted an informal survey of software developers on the issue of C++ performance, you would undoubtedly find that the vast majority of them view performance issues as the Achilles' heel of an otherwise fine language. We have heard it repeatedly ever since C++ burst on the corporate scene: C++ is a poor choice for implementing performance-critical applications. In the mind of developers, this particular application domain was ruled by plain C and, occasionally, even assembly language.
As part of that software community we had the opportunity to watch that myth develop and gather steam. Years ago, we participated in the wave that embraced C++ with enthusiasm. All around us, many development projects plunged in headfirst. Some time later, software solutions implemented in C++ began rolling out. Their performance was typically less than optimal, to put it gently. Enthusiasm over C++ in performance-critical domains has cooled. We were in the business of supplying networking software whose execution speed was not up for negotiation--speed was top priority. Since networking software is pretty low on the software food-chain, its performance is crucial. Large numbers of applications were going to sit on top of it and depend on it. Poor performance in the low levels ripples all the way up to higher level applications.
Our experience was not unique. All around, early adopters of C++ had difficulties with the resulting performance of their C++ code. Instead of attributing the difficulties to the steep learning curve of the new object-oriented software development paradigm, we blamed it on C++, the dominant language for the expression of the paradigm. Even though C++ compilers were still essentially in their infancy, the language was branded as inherently slow. This belief spread quickly and is now widely accepted as fact. Software organizations that passed on C++ frequently pointed to performance as their key concern. That concern was rooted in the perception that C++ cannot match the performance delivered by its C counterpart. Consequently, C++ has had little success penetrating software domains that view performance as top priority: operating system kernels, device drivers, networking systems (routers, gateways, protocol stacks), and more.
We have spent years dissecting large systems of C and C++ code trying to squeeze every ounce of performance out of them. It is through our experience of slugging it out in the trenches that we have come to appreciate the potential of C++ to produce highly efficient programs. We've seen it done in practice. This book is our attempt to share that experience and document the many lessons we have learned in our own pursuit of C++ efficiency. Writing efficient C++ is not trivial, nor is it rocket science. It takes the understanding of some performance principles, as well as information on C++ performance traps and pitfalls.
The 80-20 rule is an important principle in the world of software construction. We adopt it in the writing of this book as well: 20% of all performance bugs will show up 80% of the time. We therefore chose to concentrate our efforts where it counts the most. We are interested in those performance issues that arise frequently in industrial code and have significant impact. This book is not an exhaustive discussion of the set of all possible performance bugs and their solutions; hence, we will not cover what we consider esoteric and rare performance pitfalls.
Our point of view is undoubtedly biased by our practical experience as programmers of server-side, performance-critical communications software. This bias impacts the book in several ways:
So how do we separate myth from reality? Is C++ performance truly inferior to that of C? It is our contention that the common perception of inferior C++ performance is invalid. We concede that in general, when comparing a C program to a C++ version of what appears to be the same thing, the C program is generally faster. However, we also claim that the apparent similarity of the two programs typically is based on their data handling functionality, not their correctness, robustness, or ease of maintenance. Our contention is that when C programs are brought up to the level of C++ programs in these regards, the speed differences disappear, or the C++ versions are faster.
Thus C++ is inherently neither slower nor faster. It could be either, depending on how it is used and what is required from it. It's the way it is used that matters: If used properly, C++ can yield software systems exhibiting not just acceptable performance, but yield superior software performance.
We would like to thank the many people who contributed to this work. The toughest part was getting started and it was our editor, Marina Lang, who was instrumental in getting this project off the ground. Julia Sime made a significant contribution to the early draft and Yomtov Meged contributed many valuable suggestions as well. He also was the one who pointed out to us the subtle difference between our opinions and the absolute truth. Although those two notions may coincide at times, they are still distinct.
Many thanks to the reviewers hired by Addison-Wesley; their feedback was extremely valuable.
Thanks also to our friends and colleagues who reviewed portions of the manuscript. They are, in no particular order, Cyndy Ross, Art Francis, Scott Snyder, Tricia York, Michael Fraenkel, Carol Jones, Heather Kreger, Kathryn Britton, Ruth Willenborg, David Wisler, Bala Rajaraman, Don "Spike" Washburn, and Nils Brubaker.
Last but not least, we would like to thank our wives, Cynthia Powers Bulka and Ruth Washington Mayhew.