测试程序

图灵社区:随机数(二)中,我们给出了一个 NrRandom 类,能够产生更好的伪随机序列。那么,它与 .NET Framework 中的 Random 类相比,性能如何呢?让我们写一个测试程序吧:

using System;
using System.Diagnostics;

namespace Skyiv.Tester
{
  static class NrRandomTester
  {
    static readonly Stopwatch timer = new Stopwatch();

    static void TestNext1(Random rand, int n)
    {
      long sum = 0;
      timer.Restart();
      for (var i = 0; i < n; i++) sum += rand.Next();
      timer.Stop();
      Console.WriteLine("{0,14} {1,6:F3} {2:N0}", rand.GetType(),
        timer.Elapsed.TotalSeconds, (double)sum/n);
    }

    static void TestNext2(Random rand, int n, int max)
    {
      long sum = 0;
      timer.Restart();
      for (var i = 0; i < n; i++) sum += rand.Next(max);
      timer.Stop();
      Console.WriteLine("{0,14} {1,6:F3} {2:N6}", rand.GetType(),
        timer.Elapsed.TotalSeconds, (double)sum/n);
    }

    static void TestNext3(Random rand, int n, int min, int max)
    {
      long sum = 0;
      timer.Restart();
      for (var i = 0; i < n; i++) sum += rand.Next(min, max);
      timer.Stop();
      Console.WriteLine("{0,14} {1,6:F3} {2,12:N3}", rand.GetType(),
        timer.Elapsed.TotalSeconds, (double)sum/n);
    }

    static void TestNextDouble(Random rand, int n)
    {
      double sum = 0;
      timer.Restart();
      for (var i = 0; i < n; i++) sum += rand.NextDouble();
      timer.Stop();
      Console.WriteLine("{0,14} {1,6:F3} {2:N10}", rand.GetType(),
        timer.Elapsed.TotalSeconds, sum/n);
    }

    static void TestNextBytes(Random rand, int n, int size)
    {
      long sum = 0;
      var bs = new byte[size];
      timer.Restart();
      for (var i = 0; i < n; i++) {
        rand.NextBytes(bs);
        foreach (var v in bs) sum += v;
      }
      timer.Stop();
      Console.WriteLine("{0,14} {1,6:F3} {2:N6}", rand.GetType(),
        timer.Elapsed.TotalSeconds, (double)sum/n/size);
    }

    static void Main()
    {
      Console.WriteLine("{0} {1}-bit", Environment.OSVersion,
        Environment.Is64BitOperatingSystem ? 64 : 32);
      Console.WriteLine("CLR {0}", Environment.Version);

      var r0 = new Random();
      var nr = new NrRandom();

      Console.WriteLine();  int n = 1000000000, max = int.MaxValue;
      Console.WriteLine("Next(): {0,27:N0}", (max-1)/2.0);
      TestNext1(r0, n); TestNext1(nr, n);

      Console.WriteLine(); max = 10000;
      Console.WriteLine("Next(max): {0,23:N6}", (max-1)/2.0);
      TestNext2(r0, n, max); TestNext2(nr, n, max);

      Console.WriteLine(); max = int.MaxValue; int min = 1 - max;
      Console.WriteLine("Next(min,max): {0,19:N3}", (max-1L+min)/2.0);
      TestNext3(r0, n, min, max); TestNext3(nr, n, min, max);

      Console.WriteLine();
      Console.WriteLine("NextDouble(): {0,20:N10}", 1/2.0);
      TestNextDouble(r0, n); TestNextDouble(nr, n);

      Console.WriteLine(); int size = 1000000, m = n / size;
      Console.WriteLine("NextBytes(buffer): {0,13:N6}", byte.MaxValue/2.0);
      TestNextBytes(r0, m, size); TestNextBytes(nr, m, size);
    }
  }
}

编译和运行

Linux 64-bit 操作系统

在 Arch Linux 64-bit 操作系统中编译和运行:

$ mcs NrRandomTester.cs NrRandom.cs
$ mono NrRandomTester.exe
Unix 4.6.3.1 64-bit
CLR 4.0.30319.42000

Next():               1,073,741,823
 System.Random 14.920 1,073,744,468
Skyiv.NrRandom 16.619 1,073,777,257

Next(max):            4,999.500000
 System.Random 19.831 4,999.476177
Skyiv.NrRandom 15.244 4,999.590640

Next(min,max):               0.000
 System.Random 41.936    8,672.938
Skyiv.NrRandom 16.405  -13,744.444

NextDouble():         0.5000000000
 System.Random 17.517 0.4999909586
Skyiv.NrRandom 14.077 0.5000026850

NextBytes(buffer):    127.500000
 System.Random 15.483 127.498822
Skyiv.NrRandom  6.291 127.503321

输出结果中:

方法名称:                期望的平均值
 System.Random 运行时间(秒) 实际的平均值
Skyiv.NrRandom 运行时间(秒) 实际的平均值

其中每种方法都生成 109 个随机数来进行测试。可以看出:

  • 对于 Next() 方法,NrRandom 类的性能略差于 Random 类。
  • 对于 Next(max) 方法,NrRandom 类的性能优于 Random 类。
  • 对于 Next(min,max) 方法,NrRandom 类的性能大大优于 Random 类。
  • 对于 NextDouble() 方法,NrRandom 类的性能优于 Random 类。
  • 对于 NextBytes(buffer) 方法,NrRandom 类的性能大大优于 Random 类。
  • 对于这两个类,各个方法生成的随机数的平均值都中规中矩。

Windows 64-bit 操作系统

Win2008

C> NrRandomTester.exe
Microsoft Windows NT 6.1.7601 Service Pack 1 64-bit
CLR 4.0.30319.42000

Next():               1,073,741,823
 System.Random 11.030 1,073,763,381
Skyiv.NrRandom 11.944 1,073,745,494

Next(max):            4,999.500000
 System.Random 16.585 4,999.546877
Skyiv.NrRandom 10.319 4,999.411534

Next(min,max):               0.000
 System.Random 31.271   37,382.863
Skyiv.NrRandom 11.942  -39,145.860

NextDouble():         0.5000000000
 System.Random 13.122 0.5000071087
Skyiv.NrRandom  9.548 0.4999977242

NextBytes(buffer):    127.500000
 System.Random 12.009 127.497139
Skyiv.NrRandom  7.090 127.503601

测试结果与 Linux 64-bit 操作系统一致。

Windows 32-bit 操作系统

Win7

C:> NrRandomTester.exe
Microsoft Windows NT 6.1.7601 Service Pack 1 32-bit
CLR 4.0.30319.42000

Next():               1,073,741,823
 System.Random 11.455 1,073,751,534
Skyiv.NrRandom 35.625 1,073,754,659

Next(max):            4,999.500000
 System.Random 18.431 4,999.490003
Skyiv.NrRandom 33.778 4,999.364559

Next(min,max):               0.000
 System.Random 50.525   17,712.270
Skyiv.NrRandom 46.544  -10,609.773

NextDouble():         0.5000000000
 System.Random 14.538 0.4999985216
Skyiv.NrRandom 26.370 0.4999974172

NextBytes(buffer):    127.500000
 System.Random 11.726 127.500430
Skyiv.NrRandom  9.558 127.500100

测试结果出乎意料,除了 Next(min,max)NextBytes(buffer) 方法,NrRandom 类的性能略优于 Random 类,其余方法,NrRandom 类的性能均大大差于 Random 类。我想,这是由于 NrRandom 类生成随机数时,使用 long(64-bit 整数)进行运算,而 32-bit 的操作系统的 64-bit 整数运算的性能低下造成的。