败犬日报 2025-01-03
1. 多线程写相邻变量
cpp
struct Point {
int x;
int y;
};
int main() {
struct Point point = {0, 0};
// Thread 1
for (int i = 0; i < 100; i++) {
point.x++;
}
// Thread 2
for (int j = 0; j < 100; j++) {
point.y++;
}
}
缓存一致性保证了最终结果不会出现 point.x
和 point.y
不一致的情况。
所以问题只出在性能上,即伪共享(false sharing)。伪共享是多个线程同时修改位于同一 cacheline 中的不同变量,cacheline 会频繁地在多个核的 cahe 之间切换,从而降低性能。
解决方案是通过对齐让 x, y 位于不同的 cacheline 里(cacheline 是 64 字节,考虑到相邻 cacheliine 的硬件预取,推荐 128 字节对齐):
cpp
struct Point {
alignas(128) int x;
alignas(128) int y;
};
此事在 c++ 性能优化指南上亦有记载。
2. C++ 和 Java 的 volatile 有啥共同点
名字一样,没了。
C++ 的 volatile 极少使用,它的语义和 Java 的 volatile 完全不同。Java 可以用来做线程同步,C++ 请使用 std::atomic
。
3. 转换函数(operator T()
)必须用别名
cpp
struct A {
using T = int (*)(int);
operator T() {
return nullptr;
}
};
https://zh.cppreference.com/w/cpp/language/cast_operator 记载:
转换类型标识是一个类型标识,但它的声明符中不能出现函数与数组运算符
[]
或()
(因此转换到例如数组的指针的类型就需要使用类型别名、typedef 或标识模板:见下文)
普通的成员函数不需要别名。