用法:在我们的生产中,我们有大约 100 个线程可以访问我们尝试实现的缓存。如果缺少缓存,则将从数据库中获取信息,并通过编写器线程更新缓存。
为了实现这一目标,我们计划实施multiple read and single writer
我们无法更新 g++ 版本,因为我们使用的是g++-4.4
更新:每个工作线程都可以用于读取和写入。如果缺少缓存,则从数据库缓存信息。
问题陈述:我们需要实现缓存来增强性能。 为此,缓存读取更频繁,对缓存的写入操作要少得多。
我认为我们可以使用boost::shared_mutex boost::shared_lock
、boost::upgrade_lock
、boost::upgrade_to_unique_lock implementation
但我们了解到boost::shared_mutex
存在性能问题:
- 读取器写入器锁的性能比较
- Lib boost devel
问题
boost::shared_mutex
是否会影响万一读取频繁时的性能?- 在考虑编译器版本
g++4.4
时,我们可以采取哪些其他构造和设计方法? - 有没有办法绕过如何设计它,这样
reads are lock free
?
此外,我们打算使用Map
来保存缓存信息。
如果写入不存在,一种可能性可以2-level cache
,首先有一个thread-local cache
,然后是normal cache with mutex or reader/writer lock
。
如果写入极为罕见,您也可以这样做。但是有一些无锁的方式来使线程本地缓存失效,例如每次写入都会更新原子 int,并在这些情况下清除线程本地缓存。
你需要分析它。
如果你因为没有一个可以实际测试东西的"足够相似"的环境而陷入困境,你可能会使用 pthreads 编写一个简单的包装器:pthread_rwlock_t
- pthread_rwlock_rdlock
- pthread_rwlock_wrlock
- pthread_rwlock_unlock
当然,您可以将东西设计为无锁。最明显的解决方案是不共享状态。(如果您确实共享状态,则必须检查目标平台是否支持原子指令)。但是,在对您的应用程序域一无所知的情况下,我觉得非常安全,建议您不要无锁。例如,参见无锁算法真的比完全锁定算法的性能更好吗?
这完全取决于更新的频率、缓存的大小以及更新中的更改量。
-
假设您有一个相当大的缓存,每次更新都会进行大量更改。然后我会使用无锁的读取-复制-更新模式。
-
如果您的缓存数据非常小并且一次性读取(例如单个整数),RCU 也是一个不错的选择。
具有 小更新的大缓存或具有 RCU 频繁更新的大缓存读写锁是一个不错的选择。
除了建议你分析它的其他答案外,如果你能以某种方式构建或预测请求的类型、顺序和大小,就会有很大的好处。
-
如果在典型周期中请求特定类型的数据,则最好按数据类型拆分缓存。您将增加缓存命中/未命中比率,并且每个缓存的大小可以适应类型。您还将减少可能的争用。
-
同样,在选择更新方法时,请求的大小也很重要。较小的数据片段可以存储更长时间,甚至可以汇集在一起,而较大的数据片段的请求频率可能较低。
-
即使制定了仅涵盖最常见抓取模式的基本预测方案,您也可能已经大大提高了性能。尝试和训练例如 NN(神经网络)来提前猜测下一个请求绝对是值得的。