我有一个高效的 C# 应用程序,它在多线程 CPU 上以每秒 5k 到 10k 条记录的速率接收 80 字节的数据。
我现在需要在内存缓存中设置一个来检测和过滤重复记录,这样我就可以阻止它们在管道中进一步移动。
缓存规范(最大阈值)
- 80 字节数据
- 10,000 条记录/秒
- 60 秒缓存 = key 数量 = 60,000
- (小计 48000000 字节 = 48Mb)
- 理想缓存大小 = 5 分钟(或 240Mb)
- 可接受的运行时缓存大小膨胀 = 1 GB
问题
设置内存缓存、字典、哈希表、数组等的最佳方法是什么,以实现最高效的查找、清除旧缓存数据并防止命中的数据过期。
我看了ASP.Net Cache , System.Runtime.MemoryCache但我认为我需要更轻量级和自定义的东西来实现正确的吞吐量。我也在看 System.Collections.Concurrent作为替代和this related whitepaper .
有没有人对最好的方法有什么建议?
最佳答案
记住,不要过早优化!
可能有一种相当简洁的方法可以做到这一点,而无需诉诸非托管代码、指针等。
在我的旧的普通笔记本电脑上进行的快速测试表明,您可以在 ~100 毫秒内向 HashSet
添加 1,000,000 个条目,同时删除 100,000 个条目。然后,您可以在 ~60 毫秒内使用相同的 1,000,000 个值重复该操作。这仅适用于长整型 - 80 字节数据结构显然更大,但需要一个简单的基准。
我的建议:
将“查找”和“重复检测”作为
HashSet
实现,这对于插入、删除和查找来说速度极快。将实际缓冲区(接收新事件并使旧事件过期)实现为适当大的循环/环形缓冲区。这将避免内存分配和释放,并且可以在前面添加条目并从后面删除它们。以下是一些有用的链接,其中一个(第二个)描述了使缓存中的项目过期的算法:
Fast calculation of min, max, and average of incoming numbers
How would you code an efficient Circular Buffer in Java or C#
请注意,如果您希望缓存受元素数量(比如 100,000)而不是事件时间(比如最后 5 分钟)的限制,那么循环缓冲区会更好。
当项目从缓冲区中移除时(首先从末尾搜索),它们也可以从
HashSet
中移除。无需使两个数据结构相同。在需要之前避免使用多线程!您有一个自然的“串行”工作负载。除非您知道您的 CPU 线程之一无法处理速度,否则请将其保持在单个线程中。这避免了争用、锁定、CPU 缓存未命中和其他多线程问题,这些问题往往会减慢非 embarrassingly parallel 的工作负载的速度。 .我在这里的主要警告是,您可能希望将事件的“接收”卸载到与处理事件不同的线程。
以上推荐是Staged event-driven architecture (SEDA)背后的主要思想用作高性能和稳定行为的事件驱动系统(例如消息队列)的基础。
以上设计可以被干净地包装,并试图以最小的复杂性实现所需的原始性能。这仅提供了一个体面的基线,现在可以从中提取和测量效率。
(注意:如果您需要缓存持久化,请查看Kyoto Cabinet。如果您需要缓存对其他用户可见或分布式,请查看Redis。
关于c# - 需要一个高效的内存缓存,每秒可以处理 4k 到 7k 的查找或写入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10564181/