代码之家  ›  专栏  ›  技术社区  ›  Charles

缓存IEnumerable<T>实现的性能

  •  4
  • Charles  · 技术社区  · 17 年前

    [编辑]

    Reactive Framework 使用 System.Linq.EnumerableEx.MemoizeAll() 扩展方法。

    在内部, MemoizeAll() System.Linq.EnumerableEx.MemoizeAllEnumerable<T> (发现于系统交互组件),这与我的 ThreadSafeCachedEnumerable<T>

    下面是一个精心设计的示例,它非常缓慢地打印可枚举(数字1-10)的内容,然后第二次快速打印内容(因为它缓存了值):

    // Create an Enumerable<int> containing numbers 1-10, using Thread.Sleep() to simulate work
    var slowEnum = EnumerableEx.Generate(1, currentNum => (currentNum <= 10), currentNum => currentNum, previousNum => { Thread.Sleep(250); return previousNum + 1; });
    
    // This decorates the slow enumerable with one that will cache each value.
    var cachedEnum = slowEnum.MemoizeAll();
    
    // Print the numbers
    foreach (var num in cachedEnum.Repeat(2))
    {
        Console.WriteLine(num);
    }
    

    [/编辑]

    你好,多线程大师,

    /// <summary>
    /// Wraps an IEnumerable&lt;T&gt; and provides a thread-safe means of caching the values."/>
    /// </summary>
    /// <typeparam name="T"></typeparam>
    class ThreadSafeCachedEnumerable<T> : IEnumerable<T>
    {
        // An enumerator from the original IEnumerable<T>
        private IEnumerator<T> enumerator;
    
        // The items we have already cached (from this.enumerator)
        private IList<T> cachedItems = new List<T>();
    
        public ThreadSafeCachedEnumerable(IEnumerable<T> enumerable)
        {
            this.enumerator = enumerable.GetEnumerator();
        }
    
        #region IEnumerable<T> Members
    
        public IEnumerator<T> GetEnumerator()
        {
            // The index into the sequence
            int currentIndex = 0;
    
            // We will break with yield break 
            while (true)
            {
                // The currentIndex will never be decremented,
                // so we can check without locking first
                if (currentIndex < this.cachedItems.Count)
                {
                    var current = this.cachedItems[currentIndex];
                    currentIndex += 1;
                    yield return current;
                }
                else
                {
                    // If !(currentIndex < this.cachedItems.Count),
                    // we need to synchronize access to this.enumerator
                    lock (enumerator)
                    {
                        // See if we have more cached items ...
                        if (currentIndex < this.cachedItems.Count)
                        {
                            var current = this.cachedItems[currentIndex];
                            currentIndex += 1;
                            yield return current;
                        }
                        else
                        {
                            // ... otherwise, we'll need to get the next item from this.enumerator.MoveNext()
                            if (this.enumerator.MoveNext())
                            {
                                // capture the current item and cache it, then increment the currentIndex
                                var current = this.enumerator.Current;
                                this.cachedItems.Add(current);
                                currentIndex += 1;
                                yield return current;
                            }
                            else
                            {
                                // We reached the end of the enumerator - we're done
                                yield break;
                            }
                        }
                    }
                }
            }
        }
    
        #endregion
    
        #region IEnumerable Members
    
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
    
        #endregion
    }
    


    我只是“锁”(此枚举器)“当缓存中没有更多的项目时,以防另一个线程正要添加另一个项目(我假设在此枚举器从两个线程是一个坏主意)。

    在检索以前缓存的项时,性能非常好,但是在第一次获取许多项时,性能开始下降(由于不断锁定)。有什么提高绩效的建议吗?

    谢谢!

    2 回复  |  直到 16 年前
        1
  •  7
  •   Michiel Buddingh    17 年前
        2
  •  2
  •   Bradley Grainger    17 年前