C# “改善”;洗牌;效率

C# “改善”;洗牌;效率,c#,extension-methods,C#,Extension Methods,现在,我正在使用以下代码创建一个随机播放扩展: public static class SiteItemExtensions { public static void Shuffle<T>(this IList<T> list) { var rng = new Random(); int n = list.Count; while (n > 1) { n--;

现在,我正在使用以下代码创建一个随机播放扩展:

public static class SiteItemExtensions
{
    public static void Shuffle<T>(this IList<T> list)
    {
        var rng = new Random();
        int n = list.Count;
        while (n > 1)
        {
            n--;
            int k = rng.Next(n + 1);
            T value = list[k];
            list[k] = list[n];
            list[n] = value;
        }
    }
}
公共静态类SiteItemExtensions
{
公共静态无效洗牌(此IList列表)
{
var rng=新随机数();
int n=list.Count;
而(n>1)
{
n--;
int k=下一个(n+1);
T值=列表[k];
列表[k]=列表[n];
列表[n]=值;
}
}
}

我正在寻找一种更快、更有效的方法。现在,使用Stopwatch类,洗牌100000000个项目大约需要20秒。有人有什么想法可以让它更快吗?

这突出了现代计算机设计中经常被忽视的一个方面。通过一个愚蠢的改变,速度可以提高3倍以上:

            int k = 0; rng.Next(n + 1);  // silly change
现在在内部循环中有更多的语句,但速度要快得多。您看到的是CPU缓存的效果。该算法的缓存局部性非常差,要从数组中读取的下一个元素已经在缓存中的几率非常低。这需要花费昂贵的代价才能到达速度较慢的外部缓存和速度非常慢的内存总线。以后需要的数组元素被加载到缓存中的几率非常高。但是当需要的时候它们仍然存在的几率非常低,你的列表太大了,不适合缓存


无法解决此问题,这是算法设计中固有的问题。然而,使用实际的列表大小是一个显而易见的解决方案。在一个包含1000个元素的列表上运行100000次,速度是原来的3倍。

您已经超出了CPU缓存的能力,大部分时间都在等待RAM

通过逐步减少元素的数量,我得到了以下结果(在
列表上)

注意10^6和10^7之间的大减速。我将元素的数量增加了10倍,但时间增加了20倍。这可能是我的CPU无法再将阵列的大部分放入第二级(也是最后一级)缓存的地方

顺便说一句,您可以在方法签名中用
List
替换
IList
,并避免使用
[]
,从而获得一秒或两秒(但失去通用性):

IList<T>:   16.0429005 s
List<T>:    14.3529349 s
…所以你可能已经尽可能快了


<>(注释:C和C++基准都是在调试器之外的各个发布配置下完成的)。

如果需要统一的分发,我看来是最优的。为什么要洗牌1亿个项目?你能把这个数字降下来吗?@b战神:这里给出的算法不能产生均匀分布。一个简单的计数参数证明了这一点。此算法只选择了2^32个可能的序列,因为Random采用32位种子。但是当n超过32时,有超过2^32个可能的洗牌。因此,有一些洗牌不会由该算法产生,因此分布不均匀。不,这是Fisher-Yates洗牌:@HansPassant:不,我是说这里介绍的算法。如果随机性源的熵不足,Knuth洗牌不会给出无偏洗牌。因为这里的随机性源最多有32位的熵,它不可能给出超过32项的无偏洗牌,因为32!>2^32Is
intk=0;rng.Next(n+1)
您真正想要的是什么?那么速度是否会提高,因为存储指令每次使用的缓存值为0,而不是
rng.Next(n+1)
中的失败查找?(我意识到这与OP的问题无关,但我真的被这个结果迷住了。)这是因为它现在一直使用相同的内存地址,而不是随机的。所以这个值保证在快速缓存中。啊。。。因此,您会评论由于列表的大小而导致的缓存位置不佳。谢谢你的解释。哦,不,这个算法糟糕的缓存局部性是已知的。列表的大小决定了它的性能。在适合缓存的小列表中,愚蠢的版本与常规版本的速度一样快。
IList<T>:   16.0429005 s
List<T>:    14.3529349 s
17.947 s