测试模型中每秒钟的读和写的比例是7:3,以下是依次在3个时间点截取的性能计数器图。
后续改进 1、 用游标链表来代替普通的双头链表,程序起来就收工分配固定大小的数组,然后用数组的索引来做链表,省得每次添加和删除节点都要GC的参与,这相当于手工管理内存了,但目前我还没找到c#合适的实现。 2、 有人说链表不适合用在多线程环境中,因为对链表的每个操作都要加互斥锁,连读写锁都用不上,我目前的实现是直接用互斥锁做的线程同步,每秒的吞吐量七八十万,感觉lock也不是瓶颈,如果要改进的话可以把Dictionary用ThreadSafelyDictionary来代替,然后链表还用互斥锁(刚开始设想的链表操作只锁要操作的几个节点以降低并发冲突的想法应该不可取,不严谨)。 3、 还有一个地方就是把锁细分以下,链表还用链表,但每个链表的节点是个HashSet,对HashSet的操作如果只有读,写,删,没有遍历的话应该不需要做线程同步(我感觉不用,因为Set就是一个集合,一个线程往里插入,一个线程往里删除,一个线程读取应该没问题,顶多读进来的数据可能马上就删除了,而整个Set的结构不会破坏)。然后新增数据的时候往链表头顶Set里插入,读取某个数据的时候把它所在的节点的Set里删除该数据,然后再链表头的Set里插入一个数据,这样反复操作后,链表的最后一个节点的Set里的数据都是旧数据了,可以安全的删除了,当然这个删除的时候应该是要锁整个链表的。每个Set应该有个大小上限,比如20w,但set不能安全的遍历,就不能得到当前大小,所以添加、删除Set的数据的时候应该用Interlocked.Decrement()和 Interlocked.Increment()维护一个Count,一遍一个Set满的时候,再到链表的头新增一个Set节点。 性能测试脚本 class Program {
(责任编辑:admin)private static PerformanceCounter _addCounter; private static PerformanceCounter _getCounter; static void Main(string[] args) { SetupCategory(); _addCounter = new PerformanceCounter("wawasoft.lrucache", "add/sec", false); _getCounter = new PerformanceCounter("wawasoft.lrucache", "get/sec", false); _addCounter.RawValue = 0; _getCounter.RawValue = 0; Random rnd = new Random(); const int max = 500 * 10000; LRUCacheHelper<int, int> cache = new LRUCacheHelper<int, int>(200 * 10000, max); for (int i = 10000*100000 - 1; i >= 0; i--) { if(i % 10 > 7) { ThreadPool.QueueUserWorkItem( delegate { cache.Add(rnd.Next(0, 10000), 0); _addCounter.Increment(); }); } else { ThreadPool.QueueUserWorkItem( delegate { int pop = cache.Get(i); _getCounter.Increment(); }); } } Console.ReadKey(); } private static void SetupCategory() { if (!PerformanceCounterCategory.Exists("wawasoft.lrucache")) { CounterCreationDataCollection CCDC = new CounterCreationDataCollection(); CounterCreationData addcounter = new CounterCreationData(); addcounter.CounterType = PerformanceCounterType.RateOfCountsPerSecond32; addcounter.CounterName = "add/sec"; CCDC.Add(addcounter); CounterCreationData getcounter = new CounterCreationData(); getcounter.CounterType = PerformanceCounterType.RateOfCountsPerSecond32; getcounter.CounterName = "get/sec"; CCDC.Add(getcounter); PerformanceCounterCategory.Create("wawasoft.lrucache","lrucache",CCDC); } } } |