系统编程( 四 )


static_assert(sizeof(uintptr_t) == 8, \"Only works on 64-bit systems\");

template <typename T>
uintptr_t MarkDirty(T* t) {
return reinterpret_cast<uintptr_t>(t) | kDirtyBit;
}

template <typename T>
T* GetPointer(uintptr_t t) {
return reinterpret_cast<T*>(t & kPtrMask);
}不过,有趣的是,由于这是Linux内存管理/虚拟地址空间的一个特性,所以它可能会发生变化,而且实际上已经发生了变化!
LWN(Linux Weekly News)在2017年发布了补丁集,实现了五级页表,以支持更大数量的可寻址内存空间 。如果启用这个更改的话,Linux的虚拟内存寻址空间将从现在48位提高到57位,从而将虚拟内存寻址空间的大小从256 TiB增加到128 PiB,这对于每个人来说都足够了 。
默认情况下这个更改无法启用 。部分原因是各种高性能程序,特别是各种JavaScript引擎和 LuaJIT,对寻址空间高位用途的调整会导致一些额外的数据被打包到指针中 。
锁定条带化(Lock Striping)
当你希望多个线程以独占方式访问共享数据时,锁可以用于互斥 。但缺点是 , 如果共享数据被频繁访问,而且这是系统的关键部分的话 , 那么线程可能会将大部分时间花在锁的争用上,而不是实际工作上 。
解决这个问题的一个常见方法是引入更多的锁 。你说什么?等一下!
好吧 , 我想说的是:不是一个保护所有数据的锁,而是有许多只负责一部分数据的锁 。通过这种方式 , 我们将数据分成独立的、互不竞争的存储桶 。假设数据访问方式都倾向于一致的,增加数据的切分会按比例减少争用锁的线程数 。
下面是用C++写的一个小例子 , 提供了线程安全的hash-set的两种实现 。第一个实现ThreadSafeHashSet使用单个锁来保护单个基础hash-set(absl::flat_hash_set) 。第二个实现LockStripedHashSet有N个单独的锁,保护N个单独的基础hash-set(abs::flat_hash_sets) 。
// Simple thread-safe single-lock hash set
class ThreadSafeHashSet {
public:
void Insert(uint64 i) {
absl::MutexLock lock(&mu_);
hash_set_.insert(i);
}

bool Contains(uint64 i) {
absl::MutexLock lock(&mu_);
return hash_set_.contains(i);
}

private:
absl::Mutex mu_;
absl::flat_hash_set<uint64> hash_set_;
};

// Chunk the data into `kNumChunks` separate hash sets, guarded by separate
// locks.
template <size_t kNumChunks>
class LockStripedHashSet {
public:
void Insert(uint64 i) {
// Mod the data to calculate which hash_set/lock to use
const size_t idx = i % kNumChunks;
absl::MutexLock lock(&mu_[idx]);
hash_set_[idx].insert(i);
}

bool Contains(uint64 i) {
const size_t idx = i % kNumChunks;
absl::MutexLock lock(&mu_[idx]);
return hash_set_[idx].contains(i);
}

private:
std::array<absl::Mutex, kNumChunks> mu_;
std::array<absl::flat_hash_set<uint64>, kNumChunks> hash_set_;
};
为了说明锁定条带化的好处,我们在多个线程存在的情况下对两个线程安全的hash-set性能进行了基准测试,每个线程都插入了一百万项 。对于LockStripedHashSet , 我们尝试将数据拆分成4块和8块 。结果如下:
Executing tests from //examples:lock-striping
-----------------------------------------------------------------------------
2019-08-24 22:24:37
Run on (4 X 2800 MHz CPU s)
CPU Caches:
L1 Data 32K (x2)
L1 Instruction 32K (x2)
L2 Unified 262K (x2)
L3 Unified 4194K (x1)
--------------------------------------------------------------------------
Benchmark Time CPU Iterations
--------------------------------------------------------------------------
BM_SingleLock/threads:1 65 ms 65 ms 11
BM_SingleLock/threads:2 140 ms 254 ms 2

相关经验推荐