[clang-tidy] New check: performance-use-memory-order-relaxed
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
Here is suggestion to add a new clang-tidy check that suggests using `std::memory_order_relaxed` for atomic counters when the analyzer can prove that the counter is only ever incremented via `fetch_add` and never read or modified in any other way.
**Motivation**
Currently, developers often use `std::memory_order_seq_cst` for atomic counters by default, either out of habit or because they're unsure about memory ordering semantics. This leads to unnecessary performance overhead, especially on weakly-ordered architectures like ARM, PowerPC, and RISC-V.
For pure counters used solely for ID generation, logging, statistics, or request tagging, `std::memory_order_relaxed` is sufficient and provides the best performance. However, there's currently no tooling that helps developers identify these optimization opportunities safely.
**Proposed Check Behavior**
The first version of check should emit a warning when:
1. A static local `std::atomic` variable exists (where `T` is an integral type),
2. The variable is used only in `fetch_add` operations with a constant increment of `1`,
3. No `load()`, `store()`, `exchange()`, `compare_exchange_weak/strong()`, or `fetch_*` operations are performed on the variable,
4. No references or pointers to the variable escape the function scope,
5. The current memory order is stronger than `std::memory_order_relaxed`.
**Example:**
```cpp
// Warning: Use memory_order_relaxed for this atomic counter
uint32_t next_request_id() {
static std::atomic counter{0};
return counter.fetch_add(1); // warning here
}
// Suggested fix:
uint32_t next_request_id() {
static std::atomic counter{0};
return counter.fetch_add(1, std::memory_order_relaxed);
}
```
**Non-example (should NOT warn):**
```cpp
// No warning: counter is read via load()
uint32_t get_current_id() {
static std::atomic counter{0};
return counter.load(std::memory_order_acquire);
}
```
```cpp
// No warning: pointer to counter escapes
uint32_t next_id() {
static std::atomic counter{0};
std::atomic* ptr = &counter;
return ptr->fetch_add(1, std::memory_order_seq_cst);
}
```
```cpp
std::atomic counter{0};
uint32_t next_request_id() {
return counter.fetch_add(1);
}
```
Contributor guide
Research direction
Start with the existing clang-tidy check structure and tests, then determine how to identify static local integral std::atomic variables and prove that only constant-increment fetch_add operations occur. The check is done when it warns only for stronger-than-relaxed ordering under all listed safety conditions and provides the relaxed-order suggestion without warning on the non-examples.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100