llvm / llvm/llvm-project

[clang-tidy] New check: performance-use-memory-order-relaxed

Open
#222,307 1 comment 0 reactions 0 assignees View on GitHub
check-request clang-tidy
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.