更快的争夺数百万数字

时间:2017-06-03 04:30:08

标签: arrays vb.net algorithm performance shuffle

我需要争夺巨大的整数数组(从1,000,000到80,000,000个元素)。

这些数组是顺序创建的(1到n步骤1),我需要确保没有一个序列号丢失 - 因此我无法创建"随机数"阵列。

因此,我使用单个FOR-NEXT创建它们(它需要非常短的时间,大约2ms)并且我使用Fisher-Yates-Durnstenfeld shuffle算法,如下所示。

首先我创建数组:

 dim Huge(0 to 80,000,000) as Integer
 For x as integer = 1 to 79,999,999
     Huge(x) = x
 Next

创建它需要很少的时间......

之后,我争抢数字:

    Shuffle(Huge)

其中Shuffle是在并行/多任务环境中调用的例程。因此,我需要锁定Fisher-Yates-Durstenfeld程序中使用的RANDOM函数(在交换值和位置中提供良好的熵):

Fisher-Yates Shuffle

Friend Sub Shuffle(ByRef Buffer As Integer())
  For max As Integer = Buffer.Length - 1 To 1 Step -1
      ExtractRandomItem(Buffer, max)
  Next
End Sub

Friend Sub ExtractRandomItem(ByRef buffer As Integer(), max As Integer)
  Dim random As Integer = GetRandomNumber(max + 1)
  If random = max Then
     random -= 1
  End If

  Dim temp As Integer = buffer(max)
  buffer(max) = buffer(random)
  buffer(random) = temp
End Sub

Friend Function GetRandomNumber(max As Integer) As Integer
  Dim _random As New System.Random
  SyncLock _random
      Return _random.Next(1,max)
  End SyncLock
End Function

我的问题

这个随机播放例程非常适合随机加扰数组,但是,如果仅需要2ms来加扰8192个元素,对于 24,000,000个元素需要32秒 - 这需要太多时间。

你知道如何改进这个过程并加快速度吗?

或者您是否知道另一种非确定性的随机播放算法比我的工作更快?

注意:我已经尝试过PARALLEL.FOR,无论如何我都没有表现出任何好处(实际上只有几毫秒)

而且我并非真的需要"随机的"洗牌,但只是一种非确定性的排序。

感谢您的帮助

更新

由于Jdweng和Hans的相关评论,我觉得预约很有意思:

  • 该例程同时使用2个数组(输入和输出),每个数组可能有32个兆元素(每个元素大约3300万个),这会导致.NET分配近1Gb RAM。它不是我的选择,而是系统要求,有时候。

  • 因此,我需要避免创建一个新的数组来将SWAP操作替换为in - >出手...

更新II

有些人建议采用良好做法,但他们没有资格解决问题。让我来讨论一下:

  • 使用不同的数组:此过程可以节省一些逐个操作,避免交换(3个操作)以使用阵列A和B之间的移动。但是它需要将RAM加倍请求(两个相同的矩阵)。它不是使用庞大的矩阵而不是NET-Framework内存分配模式(需要空闲的连续内存块)的选项。

  • 使用MergedShuffle算法:它似乎是最理想的解决方案,但我非常担心将shuffle与矩阵的各个部分分开",因为这个模式基于它们的中间点在矩阵的片段上工作。我需要更深入地研究它以了解整个过程。

  • 在主阵列上使用并行:它不是一个选项,因为shuffle只符合强制范围 - 换句话说,我不会有最后一个元素随阵列的初始元素混乱。它可以被看作是一种确定性模式(不是一种选择)。

1 个答案:

答案 0 :(得分:0)

您的大部分糟糕表现都是因为您为每个随机数创建了一个新的随机数生成器。我只增加了7倍的速度。我还将所有代码合并到一个函数中,以最大化.net的优化。最终结果的速度提高了不到10倍(我的32位Win7虚拟机上的80,000,000个项目为117秒到12)。

    onClickLink = (pageNumber, event) => {
            this.setState({ pageNumber });
        }

    render() {
            <ul className="pure-menu-list">
                <li className="pure-menu-item"><a href="#" className="pure-menu-link" onClick=onClickLink.bind(null, 2)>Page 2</a></li>
             </ul>


            <div className="pure-u-1-1">
                <p>arrayOfPageComponents[this.state.pageNumber]</p>
            </div>
        }

请注意,随机数生成器不需要锁定,因为每次调用Shuffle都有自己的副本,并且不会在线程之间共享。

70%的时间花在从Buffer(随机)读取值上。这是因为它可能不在CPU缓存中并且必须达到主存储器而读取缓冲区(最大值)仅为0.5%,因为它按顺序读取并且CPU可以预先读取它缓存。 20%是在计算随机数,所以使用更快的发生器,你可以节省更多的时间,但不会太多。

最大的胜利将是一个没有为每个元素随机访问数组的算法。我在问题评论中遵循了Hans Passant的建议并用Google搜索fiddle。它似乎就是这样一种算法(多次顺序访问而不是随机单次传递)。除了允许分区外,它不需要更多RAM。

我做了原型设计,我看到加速20%,80M项目转换为Fisher-Yates的2.5M项目。