What are C++ [[likely]] and [[unlikely]] attributes?

Efficiency and optimization are crucial in programming. This is especially true in systems programming, where C++ shines with its rich features designed to give programmers greater control over performance. Among these features are the [[likely]] and [[unlikely]] attributes introduced in C++20. These attributes allow programmers to hint to the compiler about the expected likelihood of a branch, aiding in more efficient code generation.

Use

The [[likely]] and [[unlikely]] attributes are used in conditional statements to provide the compiler with hints about which branch of the statement is expected to be taken more frequently. The compiler can use this information to optimize the generated machine code for better performance, especially in branch prediction on modern processors.

Syntax

The syntax for these attributes is straightforward. They can be applied to labels within a switch statement or to the individual branches of if statements.

Example 1: Using [[likely]] in an if statement

This example demonstrates how to use the [[likely]] attribute in an if statement to hint to the compiler that a certain branch of the statement is expected to be the more commonly executed path. This can assist the compiler in optimizing code execution.

#include <iostream>
#include <cstdlib> // For std::rand() and std::srand()
#include <ctime> // For std::time()
int main() {
// Initialize random seed
std::srand(std::time(nullptr));
// Generate a random number: 0 or 1
int condition = std::rand() % 2;
// Use the likely attribute to hint that the condition is more likely to be true
if (condition) [[likely]] {
std::cout << "This branch is likely to execute." << std::endl;
} else {
std::cout << "This branch is less likely to execute." << std::endl;
}
return 0;
}

Example 2: Using [[unlikely]] in a switch statement

This example showcases how to simulate the use of the [[unlikely]] attribute within a switch statement to hint to the compiler that a certain case is expected to be less frequently executed. This technique is used to potentially guide optimizations by the compiler.

#include <iostream>
#include <cstdlib> // For rand() and srand()
#include <ctime> // For time()
int main() {
// Seed the random number generator
std::srand(static_cast<unsigned int>(std::time(nullptr)));
// Generate a random value of either 1 or 2
int value = std::rand() % 2 + 1;
// User input no longer needed
std::cout << "Randomly generated value is: " << value << std::endl;
switch (value) {
case 1:
// Most common case
std::cout << "Case 1 is the most common case." << std::endl;
break;
case 2: {
// Mimicking the intention of [[unlikely]] by using an if statement
if (value == 2) [[unlikely]] {
std::cout << "Case 2 is an uncommon case." << std::endl;
}
break;
}
// Other cases can be added here
default:
std::cout << "Other cases not covered explicitly." << std::endl;
}
return 0;
}

Practical use cases

Now that we’ve seen the syntax and a basic example, let’s explore some practical use cases of these attributes.

Performance optimization

The primary use case for [[likely]] and [[unlikely]] is in performance-critical sections of code where branch prediction can have a significant impact. By providing the compiler with hints about which branches are more likely, the generated machine code can be optimized for the most common scenarios.

Example: Optimizing loop conditions

This example showcases how to use the [[unlikely]] attribute within a loop to optimize performance based on the predicted frequency of a condition. By marking a condition as [[unlikely]], we hint to the compiler that a particular path is expected to be less common, potentially enabling more efficient code generation for the more frequent execution path.

#include <iostream>
#include <cstdlib> // For std::rand() and std::srand()
#include <ctime> // For std::time()
// Function to simulate a rare condition
bool rare_condition(int i) {
return (i % 10 == 0); // True if i is a multiple of 10
}
int main() {
// Initialize random seed
std::srand(std::time(nullptr));
// Generate a random size for n, between 1 and 100
int n = std::rand() % 100 + 1;
for (int i = 0; i < n; ++i) {
if (rare_condition(i)) [[unlikely]] {
std::cout << "Rare condition met at i = " << i << std::endl;
} else {
// Handle the common path
// In a real scenario, you would have code here
}
}
return 0;
}

Readability and intent

Beyond performance, these attributes also serve as documentation, indicating the expected code path to other developers. This can improve the readability of the code and make maintenance easier.

Example: Documenting expected behavior

This example illustrates the use of the [[unlikely]] attribute to document and optimize for expected behavior within an if statement. By indicating that a branch of code is unlikely to be executed, it helps the compiler to generate more efficient code for the more probable execution path.

#include <iostream>
#include <cstdlib> // For rand() and srand()
#include <ctime> // For time()
// Simulates checking for an error, with "true" being an error condition
bool error_occurred() {
// Simulate an error condition with a 10% chance
return (std::rand() % 10 == 0);
}
int main() {
// Initialize random seed
std::srand(static_cast<unsigned int>(std::time(nullptr)));
if (error_occurred()) [[unlikely]] {
std::cout << "An error occurred, handling error..." << std::endl;
// Error handling code would go here
} else {
std::cout << "Normal execution path, no error." << std::endl;
// Normal execution code would go here
}
return 0;
}

Conclusion

The [[likely]] and [[unlikely]] attributes in C++20 are powerful tools for performance optimization and code documentation. By guiding the compiler on branch prediction, they can lead to more efficient code generation. However, it’s important to use these attributes judiciously, as incorrect predictions can actually degrade performance. As with any optimization technique, they should be applied based on profiling and a thorough understanding of the code’s behavior.

Remember, these attributes are hints, not guarantees. The final decision on code optimization rests with the compiler, which will use these hints along with other information to generate the best possible machine code.

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved