layout | title |
---|---|
post |
第178期 |
公众号
点击「查看原文」跳转到 GitHub 上对应文件,链接就可以点击了
qq群 点击进入 满了加这个 729240657
欢迎投稿,推荐或自荐文章/软件/资源等,评论区留言
本期文章由 ZIRQ 赞助 在此表示感谢 祝老板发大财身体健康永远不死
标准委员会动态/ide/编译器信息放在这里
编译器信息最新动态推荐关注hellogcc公众号 本周更新 2025-02-05 第292期
省流 不共享不要滥用
列举一些对空指针的误解,比如访问空指针会挂,c/c++语言设定如此,其他语言会捕获异常特殊处理
额我觉得还是不要知道的好
c++的未定义行为涉及的面太广,有必要收敛一些场景,比如没有初始化读就读这种场景,归纳为EB
如果真的需要这种行为,主动标记[[indeterminiate]] 这种标记下没初始化就使用才被归纳为UB
比如
void foo() {
int d [[indeterminate]]; // d has an indeterminate value
bar(d); // that's undefined behaviour!
}
不过目前为止只是提案 P2795,没有编译器支持实现
inline void Relaxed_Memcpy(volatile Atomic8* dst, volatile const Atomic8* src,
size_t bytes) {
constexpr size_t kAtomicWordSize = sizeof(AtomicWord);
while (bytes > 0 &&
!IsAligned(reinterpret_cast<uintptr_t>(dst), kAtomicWordSize)) {
Relaxed_Store(dst++, Relaxed_Load(src++));
--bytes;
}
if (IsAligned(reinterpret_cast<uintptr_t>(src), kAtomicWordSize) &&
IsAligned(reinterpret_cast<uintptr_t>(dst), kAtomicWordSize)) {
while (bytes >= kAtomicWordSize) {
Relaxed_Store(
reinterpret_cast<volatile AtomicWord*>(dst),
Relaxed_Load(reinterpret_cast<const volatile AtomicWord*>(src)));
dst += kAtomicWordSize;
src += kAtomicWordSize;
bytes -= kAtomicWordSize;
}
}
while (bytes > 0) {
Relaxed_Store(dst++, Relaxed_Load(src++));
--bytes;
}
}
每个byte都原子store load 线程安全了
对于V8来说,安全比较重要。即使这玩意慢三四十倍
省流 shared ptr一律使用make_shared构造
使用alias 构造 搭配weak ptr使用存在问题
如何避免类只能通过make_shared构造?构造函数tag + 静态函数匹配。看嗲吗
class A: std::enable_shared_from_this<A> {
private:
struct Private {};
public:
A(Private dummy, int member) : member_(member) {}
template <typename... ArgsT>
static std::shared_ptr<A> create(ArgsT&&... args) {
return std::make_shared<A>(Private(), std::forward<ArgsT>(args)...);
}
private:
int member_ = 1;
};
int main() {
std::cout << "The Start\n\n";
auto a_sptr = A::create(42);
std::cout << std::endl << "The End!";
return 0;
}
介绍数据局部性有利的数据结构 Dense/Sparse Array
简单说就是这个德行
#include <vector>
#include <cassert>
template<typename T>
class DenseSparseArray {
public:
// 插入元素(假设 entity 是唯一标识)
void insert(uint32_t entity, const T& value) {
if (entity >= sparse.size()) {
sparse.resize(entity + 1, -1); // -1 表示无效索引
}
if (sparse[entity] == -1) {
sparse[entity] = dense.size();
dense.push_back({entity, value});
}
}
// 删除元素
void erase(uint32_t entity) {
if (contains(entity)) {
size_t dense_idx = sparse[entity];
auto& last = dense.back();
// 将要删除的元素与最后一个元素交换
std::swap(dense[dense_idx], last);
sparse[last.entity] = dense_idx;
dense.pop_back();
sparse[entity] = -1;
}
}
// 访问元素
T& operator[](uint32_t entity) {
assert(contains(entity));
return dense[sparse[entity]].value;
}
// 判断是否存在
bool contains(uint32_t entity) const {
return entity < sparse.size() && sparse[entity] != -1;
}
// 迭代器支持
auto begin() { return dense.begin(); }
auto end() { return dense.end(); }
private:
struct Element {
uint32_t entity;
T value;
};
std::vector<int> sparse; // 稀疏数组(存储索引)
std::vector<Element> dense; // 密集数组(实际数据)
};
enseSparseArray<int> arr;
arr.insert(100, 42); // 插入 entity=100
arr.insert(200, 77); // 插入 entity=200
std::cout << arr[100]; // 输出 42
arr.erase(100); // 删除 entity=100
for (auto& elem : arr) { // 遍历所有有效元素
std::cout << elem.entity << ": " << elem.value << "\n";
}
deepseek帮我写的
这种玩法比较方便遍历且不失访问速度,对于小数据集是非常有用的
简单介绍MESI那套东西,多线程 cache局部性非常重要,另外介绍一些影响性能的场景
- 数据竞争,比如atomic fetch add
- 数据局部性影响,多个线程访问一份数据
- 线程过多,上下文切换开销重
- False Sharing,不同数据在同一块cacheline造成互相干扰
- 使用std::harware_destructive_interference_size alignas
- 典型场景
- 线程数组,每个线程访问数组的一个元素,没padding大概率互相干扰
- 类似,矩阵计算分块,分的不够开导致互相干扰
- 结构体字端访问,没有pading导致互相干扰
- 动态分配的小对象,存在可能
- 分配大内存专属使用
- 尽可能对齐
比较常规
介绍了基本的字段大小,继承影响,对齐影响,EBO,no_unique_address, 以及分析工具
clang++ -cc1 -fdump-record-layouts # (or -Xclang -fdump-record-layouts)
g++ -fdump-lang-class (or -fdump-class-hierarchy) # (dumps to a file, so not usable in Compiler explorer)
msvc /d1reportAllClassLayout
vc是c++26simd前身。感觉不是很好用。
业界还是用google highway多一些。
另外社区xmind很火(或者说炒作很火)但是这里没收录,可能是太新了
cat example_AliasViolation.c
int main(int argc, char **argv) {
int x = 100;
float *y = (float*)&x;
*y += 2.0f; // Strict aliasing violation
return 0;
}
#Compile and link
clang++ -g -fsanitize=type example_AliasViolation.cc
很好用。想体验的可以试一下
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang" -DLLVM_ENABLE_RUNTIMES="compiler-rt" <path to source>/llvm
#include <iostream>
#include <type_traits>
#include <cstddef>
using VoidPtrFn = void (*)(void*);
struct MemberScopeGuard
{
VoidPtrFn fn;
~MemberScopeGuard() { fn(this); }
};
#define TOKEN_PASTE_IMPL(x, y) x##y
#define TOKEN_PASTE(x, y) TOKEN_PASTE_IMPL(x, y)
#define MEMBER_SCOPE_GUARD(...) \
MemberScopeGuard TOKEN_PASTE(memberScopeGuard, __LINE__) \
{ \
([]<auto Fn>(auto* xThis) -> VoidPtrFn \
{ \
return [](void* x) -> void \
{ \
using T = std::remove_pointer_t<decltype(xThis)>; \
Fn(*(reinterpret_cast<T*>( \
reinterpret_cast<char*>(x) - offsetof(T, TOKEN_PASTE(memberScopeGuard, __LINE__))))); \
}; \
}).template operator()<[](auto& self) __VA_ARGS__>(this) \
}
struct parent {
int x = 10;
MEMBER_SCOPE_GUARD({
std::cout << "Parent pointer is " << &self << '\n';
std::cout << "Parent x is " << self.x << '\n';
});
int y = 25;
MEMBER_SCOPE_GUARD({
std::cout << "Parent pointer is " << &self << '\n';
std::cout << "Parent y is " << self.y << '\n';
});
};
int main() {
parent x;
std::cout << "Address of parent is " << &x << '\n';
}
除了感叹c++真夸张之外没什么用。
另外这个字段guard必须得放在成员后面,不能使用guard后面的字段(反向析构顺序:)
感谢天失败投稿以及原作者 Vittorio Romeo
- asteria 一个脚本语言,可嵌入,长期找人,希望胖友们帮帮忙,也可以加群753302367和作者对线
街霸6不知火舞联动上线了,非常好玩