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

平行。用于BigInteger计算的输出与For循环不同

  •  2
  • CocoaMix86  · 技术社区  · 6 年前

    下面的循环运行从base95到base10的转换。我要处理几千位数字,所以需要大整数。 inst 是base95字符串。

    Parallel.For(0, inst.Length, x => 
         {
            result += BigInteger.Pow(95, x) * (inst[x] - 32);
         });
    

    for 循环会。

    但是,一旦我开始增加base95字符串的大小,并行程序的输出就会被抛出。我需要与base95字符串1500+字符,甚至高达30000。常客 对于 循环可以很好地计算结果。

    Parallel.For 循环速度仍然比 对于 循环?

    1 回复  |  直到 6 年前
        1
  •  9
  •   TheGeneral    6 年前

    只是线程不安全。至于为什么它没有腐蚀更小的字符串,我不确定。可能吧 第三方物流 只是觉得工作负载不值得额外的线程。不过,我确实验证了您的结果,它确实会对较大的字符串产生不一致的结果。

    唯一的解决方案是使其线程安全。一种廉价而恶劣的方法是 lock Interlocked BigInteger .

    BigInteger result = 0;
    object sync = new object();
    
    Parallel.For(
       0,
       inst.Length,
       x =>
          {
             var temp = BigInteger.Pow(95, x) * (inst[x] - 32);
             lock (sync)
                result += temp;
          });
    

    它不是完美的所有锁定,但它仍然比一个正常的速度 for

    另一种方法是使用for重载,这样每个线程只锁定一次。

    Parallel.For(
       0,
       inst.Length,
       () => new BigInteger(0),
       (x, state, subTotal) => subTotal + BigInteger.Pow(95, x) * (inst[x] - 32),
       integer =>
          {
             lock (sync)
                result += integer;
          });
    

    基准

    所以我很无聊,这是你的基准点

    GC.Collect GC.WaitForPendingFinalizers 在每次测试前运行,以给出更清晰的结果。所有的结果都经过了相互对照的测试,以证明它们是准确的。 Scale

    安装程序

    ----------------------------------------------------------------------------
    Mode             : Release (64Bit)
    Test Framework   : .NET Framework 4.7.1 (CLR 4.0.30319.42000)
    ----------------------------------------------------------------------------
    Operating System : Microsoft Windows 10 Pro
    Version          : 10.0.17134
    ----------------------------------------------------------------------------
    CPU Name         : Intel(R) Core(TM) i7-3770K CPU @ 3.50GHz
    Description      : Intel64 Family 6 Model 58 Stepping 9
    Cores (Threads)  : 4 (8)      : Architecture  : x64
    Clock Speed      : 3901 MHz   : Bus Speed     : 100 MHz
    L2Cache          : 1 MB       : L3Cache       : 8 MB
    ----------------------------------------------------------------------------
    

    --- Random characters -----------------------------------------------------------------
    | Value          |    Average |    Fastest |    Cycles | Garbage | Test |        Gain |
    --- Scale 10 ----------------------------------------------------------- Time 0.259 ---
    | for            |   5.442 µs |   4.968 µs |  21.794 K | 0.000 B | Base |      0.00 % |
    | ParallelResult |  32.451 µs |  30.397 µs | 116.808 K | 0.000 B | Pass |   -496.25 % |
    | ParallelLock   |  35.551 µs |  32.443 µs | 127.966 K | 0.000 B | Pass |   -553.22 % |
    | AsParallel     | 141.457 µs | 118.959 µs | 398.676 K | 0.000 B | Pass | -2,499.13 % |
    --- Scale 100 ---------------------------------------------------------- Time 0.298 ---
    | ParallelResult |  93.261 µs |  80.085 µs | 329.450 K | 0.000 B | Pass |     11.36 % |
    | ParallelLock   | 103.912 µs |  84.470 µs | 366.599 K | 0.000 B | Pass |      1.23 % |
    | for            | 105.210 µs |  93.823 µs | 371.025 K | 0.000 B | Base |      0.00 % |
    | AsParallel     | 183.538 µs | 159.002 µs | 488.534 K | 0.000 B | Pass |    -74.45 % |
    --- Scale 1,000 -------------------------------------------------------- Time 4.191 ---
    | AsParallel     |   5.701 ms |   4.932 ms |  15.479 M | 0.000 B | Pass |     65.83 % |
    | ParallelResult |   6.510 ms |   5.701 ms |  18.166 M | 0.000 B | Pass |     60.98 % |
    | ParallelLock   |   6.734 ms |   5.303 ms |  17.314 M | 0.000 B | Pass |     59.64 % |
    | for            |  16.685 ms |  15.640 ms |  58.183 M | 0.000 B | Base |      0.00 % |
    --- Scale 10,000 ------------------------------------------------------ Time 34.805 ---
    | AsParallel     |    6.205 s |    4.767 s |  19.202 B | 0.000 B | Pass |     47.20 % |
    | ParallelResult |    6.286 s |    5.891 s |  14.752 B | 0.000 B | Pass |     46.51 % |
    | ParallelLock   |    6.290 s |    5.202 s |   9.982 B | 0.000 B | Pass |     46.48 % |
    | for            |   11.752 s |   11.436 s |  41.136 B | 0.000 B | Base |      0.00 % |
    ---------------------------------------------------------------------------------------
    

    平行锁

    [Test("ParallelLock", "", true)]
    public BigInteger Test1(string input, int scale)
    {
       BigInteger result = 0;
       object sync = new object();
    
       Parallel.For(
          0,
          input.Length,
          x =>
             {
                var temp = BigInteger.Pow(95, x) * (input[x] - 32);
                lock (sync)
                   result += temp;
             });
    
       return result;
    }
    

    平行结果

    [Test("ParallelResult", "", false)]
    public BigInteger Test2(string input, int scale)
    {
       BigInteger result = 0;
       object sync = new object();
       Parallel.For(
          0,
          input.Length,
          () => new BigInteger(0),
          (x, state, subTotal) => subTotal + BigInteger.Pow(95, x) * (input[x] - 32),
          integer =>
             {
                lock (sync)
                   result += integer;
             });
       return result;
    }
    

    gdir

    [Test("AsParallel", "", false)]
    public BigInteger Test4(string input, int scale)
    {
       return Enumerable.Range(0, input.Length)
                        .AsParallel()
                        .Aggregate(
                            new BigInteger(0),
                            (subtotal, x) => subtotal + BigInteger.Pow(95, x) * (input[x] - 32),
                            (total, thisThread) => total + thisThread,
                            (finalSum) => finalSum);;
    }
    

    对于

    [Test("for", "", false)]
    public BigInteger Test3(string input, int scale)
    {       
       BigInteger result = 0;
       for (int i = 0; i < input.Length; i++)
       {
          result += BigInteger.Pow(95, i) * (input[i] - 32);
       }
       return result;
    }
    

    输入

    public static string StringOfChar(int scale)
    {
       var list = Enumerable.Range(1, scale)
                            .Select(x => (char)(_rand.Next(32)+32))
                            .ToArray();
       return string.Join("", list);
    } 
    

    验证

    private static bool Validation(BigInteger result, BigInteger baseLine)
    {
       return result == baseLine;
    }
    

    摘要

    平行将给你一个性能提升,你能锁定的越少理论上就越好,然而可能有很多因素为什么结果会像他们那样。它的结果似乎过载工作得很好,但与较大的工作负载非常相似,我真的不知道为什么。请注意,我没有使用并行选项,您可以对它进行更多的调整,以实现您的解决方案