如果pthread_rwlock_t上没有写入锁,那么调用pthread_rwlock_rdlock / pthread_rwlock_unlock会有很多开销吗?
这是我想到的情况。如果还有其他可能有用的启发式方法,那么听到它们会很棒。
你有一个简单的程序,它通过调用每个输入项上的函数将输入数据列表转换为输出结果。一些输入是相同的,或者至少相似,足以使您的函数可以记忆计算。我们假设您选择使用哈希值来记住此函数。
随着程序的进展,哈希增长,命中率接近100%。
以下是使用pthread_rwlock_t的可能解决方案。这样做的一个缺点是,即使程序达到100%命中率,该功能仍然会调用rdlock / unlock。
在某些时候,人们想知道是否最好设置一个名为' hash_frozen'并且在那时将其视为常量共享数据,并且此时不再添加任何键。然而,这似乎是一个笨重的解决方案。
struct hash h;
pthread_rwlock_t rwl = ...;
struct val fcn(struct inp i)
{
struct hashkey k;
struct hashval v;
pthread_rwlock_rdlock(rwl);
if ((k = hash_find(h, i)) != hash_end(h))
{
struct retval v = hash_val(k);
pthread_rwlock_unlock(rwl);
return v;
}
else
{
pthread_rwlock_unlock(rwl);
/* i'm aware that another thread could insert the value at
this moment, duplicating work, but let's ignore that minor
inefficiency. */
pthread_rwlock_wrlock(rwl);
struct retval v = compute_value(i);
k = hash_put(h, i);
hash_val(k) = v; /* let's say this is a macro, as in khash.h */
pthread_rwlock_unlock(rwl);
return v;
}
}
答案 0 :(得分:0)
这显然取决于具体实施。
使用current glibc (NPTL) implementation,即使在rdlock快速路径中,每个读取器也必须采用并释放保护rwlock数据结构本身的低级锁。如果这个低级锁定没有很多争用 - 即。您的线程在读取锁定部分内部或外部执行重要工作 - 然后此低级别锁定也将完全在快速路径中执行,并且开销将仅由硬件强制保持包含低级别锁定的内存和__nr_readers
计数器在CPU之间同步。
如果 在这个低级锁上有足够的争用 - 因为你有这么多的读锁定器执行速度太快,以至于其中很大一部分最终执行pthead_rwlock_rdlock()
或{{ 1}}同时 - 其中一些人最终会睡在那个锁上,不得不被一个解锁器吵醒,这会增加很多开销。
实际上,它归结为:你说的是4核还是4千核?最终找到答案的最佳方式是实施和分析它。
如果rwlock争用确实很重要,那么对于非调整大小的散列表,您可以通过使用多个rwlock以更细粒度的方式锁定,每个rwlock覆盖散列表条目的不相交子集。您在没有锁定的情况下计算哈希值(因此称为“非大小调整”标准),并在检查哈希条目本身之前使用哈希查找要锁定的rwlock。