银河

SKYIV STUDIO

  博客园 :: 首页 :: 博问 :: 闪存 :: :: :: 订阅 订阅 :: 管理 ::

Milo Yip 园友上个月发表了“C# vs C++ 全局照明渲染性能比试”。该文说明在纯计算密集的应用中,C++ 程序比 C# 程序的速度快。现在让我们通过也是纯计算密集的计算圆周率到小数点后二十万位来进行性能比试,所用算法请参见我前不久发表的“计算圆周率”一文。

C# 程序

01:  using System;
02:  
03:  sealed class Pi
04:  {
05:    static readonly int DIGITS = 200000;
06:    static readonly int LEN = 10;
07:    static readonly long BASE = (long)Math.Pow(10, LEN);
08:    
09:    void Format(long[] pi)
10:    {
11:      long quotient = 0;
12:      for (var i = 0; i < pi.Length; i++)
13:      {
14:        var numerator = pi[i] + quotient;
15:        quotient = numerator / BASE;
16:        var remainder = numerator % BASE;
17:        if (remainder < 0)
18:        {
19:          remainder += BASE;
20:          quotient--;
21:        }
22:        pi[i] = remainder;
23:      }
24:    }
25:  
26:    int Divide(bool updateSum, bool positive, bool updateDividend,
27:      int digits, long[] sum, long[] dividend, long divisor)
28:    {
29:      long remainder = 0;
30:      for (var i = digits; i >= 0; i--)
31:      {
32:        var quotient = BASE * remainder + dividend[i];
33:        remainder = quotient % divisor;
34:        quotient /= divisor;
35:        if (updateDividend) dividend[i] = quotient;
36:        if (!updateSum) continue;
37:        if (positive) sum[i] += quotient;
38:        else sum[i] -= quotient;
39:      }
40:      if (updateDividend) while (digits > 0 && dividend[digits] == 0) digits--;
41:      return digits;
42:    }
43:  
44:    public long[] Compute(int digits)
45:    {
46:      int[] t0 = { 176, 57, 28, 239, -48, 682, 96, 12943 };
47:      var pi = new long[++digits + 1];
48:      var tmp = new long[digits + 1];
49:      for (var i = 0; i < t0.Length; i += 2)
50:      {
51:        Array.Clear(tmp, 0, tmp.Length);
52:        tmp[digits] = t0[i];
53:        var divisor = t0[i + 1];
54:        var digits2 = Divide(true, true, true, digits, pi, tmp, divisor);
55:        var positive = false;
56:        divisor *= divisor;
57:        for (var step = 3; digits2 > 0; positive = !positive, step += 2)
58:        {
59:          digits2 = Divide(false, true, true, digits2, null, tmp, divisor);
60:          digits2 = Divide(true, positive, false, digits2, pi, tmp, step);
61:        }
62:      }
63:      Format(pi);
64:      return pi;
65:    }
66:  
67:    static void Main()
68:    {
69:      var timer = System.Diagnostics.Stopwatch.StartNew();
70:      var pi = new Pi().Compute(DIGITS / LEN);
71:      timer.Stop();
72:      Console.Error.WriteLine("{0} seconds", timer.Elapsed.TotalSeconds);
73:      Console.Write(pi[pi.Length - 1] + ".");
74:      for (var i = pi.Length - 2; i > 0; i--) Console.Write(pi[i].ToString("D" + LEN));
75:    }
76:  }

C 程序

01:  #include <stdio.h>
02:  #include <stdlib.h>
03:  #include <string.h>
04:  #include <math.h>
05:  #include <time.h>
06:  
07:  typedef long long INT64;
08:  typedef int bool;
09:  const int true = 1;
10:  const int false = 0;
11:  
12:  const int DIGITS = 200000;
13:  const int LEN  = 10;
14:  INT64 BASE;
15:  
16:  void format(INT64 pi[], int digits)
17:  {
18:    INT64 numerator, remainder, quotient = 0;
19:    int i;
20:    for (i = 0; i < digits; i++)
21:    {
22:      numerator = pi[i] + quotient;
23:      quotient = numerator / BASE;
24:      remainder = numerator % BASE;
25:      if (remainder < 0)
26:      {
27:        remainder += BASE;
28:        quotient--;
29:      }
30:      pi[i] = remainder;
31:    }
32:  }
33:  
34:  int divide(bool updateSum, bool positive, bool updateDividend,
35:    int digits, INT64 sum[], INT64 dividend[], INT64 divisor)
36:  {
37:    INT64 quotient, remainder = 0;
38:    int i;
39:    for (i = digits; i >= 0; i--)
40:    {
41:      quotient = BASE * remainder + dividend[i];
42:      remainder = quotient % divisor;
43:      quotient /= divisor;
44:      if (updateDividend) dividend[i] = quotient;
45:      if (!updateSum) continue;
46:      if (positive) sum[i] += quotient;
47:      else sum[i] -= quotient;
48:    }
49:    if (updateDividend) while (digits > 0 && dividend[digits] == 0) digits--;
50:    return digits;
51:  }
52:  
53:  const INT64* compute(int digits)
54:  {
55:    int t0[] = { 176, 57, 28, 239, -48, 682, 96, 12943 };
56:    int i, step, digits2;
57:    bool positive;
58:    INT64 divisor;
59:    INT64* pi = (INT64*)calloc(++digits + 1, sizeof(INT64));
60:    INT64* tmp = (INT64*)calloc(digits + 1, sizeof(INT64));
61:    for (i = 0; i < sizeof(t0) / sizeof(t0[0]); i += 2)
62:    {
63:      memset(tmp, 0, sizeof(tmp[0]) * (digits + 1));
64:      tmp[digits] = t0[i];
65:      divisor = t0[i + 1];
66:      digits2 = divide(true, true, true, digits, pi, tmp, divisor);
67:      positive = false;
68:      divisor *= divisor;
69:      for (step = 3; digits2 > 0; positive = !positive, step += 2)
70:      {
71:        digits2 = divide(false, true, true, digits2, NULL, tmp, divisor);
72:        digits2 = divide(true, positive, false, digits2, pi, tmp, step);
73:      }
74:    }
75:    format(pi, digits);
76:    return pi;
77:  }
78:  
79:  int main()
80:  {
81:    int i = DIGITS / LEN;
82:    const INT64* pi;
83:    clock_t start = clock();
84:    BASE = (INT64)pow(10, LEN);
85:    pi = compute(i);
86:    fprintf(stderr, "%f seconds\n", (float)(clock() - start) / CLOCKS_PER_SEC);
87:    printf("%lld.", pi[i + 1]);
88:    for (; i > 0; i--) printf("%0*lld", LEN, pi[i]);
89:    return 0;
90:  }

运行结果

Linux 操作系统下的运行结果如下:

ben@ben-1520:~/work/pi$ mono --version
Mono JIT compiler version 2.4.4 (Debian 2.4.4~svn151842-1ubuntu4)
Copyright (C) 2002-2010 Novell, Inc and Contributors. www.mono-project.com
	TLS:           __thread
	GC:            Included Boehm (with typed GC)
	SIGSEGV:       altstack
	Notifications: epoll
	Architecture:  amd64
	Disabled:      none
ben@ben-1520:~/work/pi$ gmcs --version
Mono C# compiler version 2.4.4.0
ben@ben-1520:~/work/pi$ gcc --version
gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

ben@ben-1520:~/work/pi$ gmcs pi.cs && ./pi.exe > pi-1.txt
202.5676041 seconds
ben@ben-1520:~/work/pi$ gcc pi.c -lm && ./a.out > pi-2.txt
210.639999 seconds
ben@ben-1520:~/work/pi$ gcc -O3 pi.c -lm && ./a.out > pi-3.txt
144.570007 seconds
ben@ben-1520:~/work/pi$ diff pi-1.txt pi-2.txt
ben@ben-1520:~/work/pi$ diff pi-1.txt pi-3.txt
ben@ben-1520:~/work/pi$ 

Windows 操作系统下的运行结果如下:

E:\work\pi> csc /out:pi-1.exe pi.cs
Microsoft(R) Visual C# 2010 编译器 4.0.30319.1 版
版权所有(C) Microsoft Corporation。保留所有权利。


E:\work\pi> pi-1.exe > pi-1.txt
268.3892944 seconds

E:\work\pi> cl /Fepi-2.exe pi.c
用于 80x86 的 Microsoft (R) 32 位 C/C++ 优化编译器 16.00.30319.01 版
版权所有(C) Microsoft Corporation。保留所有权利。

pi.c
Microsoft (R) Incremental Linker Version 10.00.30319.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:pi-2.exe
pi.obj

E:\work\pi> pi-2.exe > pi-2.txt
238.161000 seconds

E:\work\pi> fc pi-1.txt pi-2.txt
正在比较文件 pi-1.txt 和 PI-2.TXT
FC: 找不到相异处


E:\work\pi>

将以上结果列表如下:

  操作系统 编译器 运行时间(秒) 相对比率
(a) Linux Mono C# compiler version 2.4.4.0 202.5676 0.00%
(b) Linux gnu C compiler version 4.4.3 210.6400 3.99%
(c) Windows Microsoft Visual C# 4.0.30319.1 268.3893 32.49%
(d) Windows Microsoft C/C++ 16.00.30319.01 238.1610 17.57%
(e) Linux gcc 4.4.3 (使用 -O3 参数优化编译) 144.5700 -28.63%

以上程序都是在同一台 Dell 笔记本电脑中运行的,其中 Windows 操作系统是本机的宿主操作系统,Linux 操作系统是虚拟机中的客户操作系统。可以看出,在 Linux 操作系统下,在这种纯计算密集的应用中,C 程序居然比 C# 程序运行速度还要慢。而在 Windows 操作系统中运行都比 Linux 操作系统要慢,但 C 程序的运行速度比 C# 程序要快。

运行环境

硬件: Dell Inspiron 1520, Intel® Core(TM)2 Duo CPU T7100 @ 1.80GHz (2CPUs), 2.0GB RAM

虚拟机软件: Oracle VM VirtualBox 3.2.6 r63112

宿主操作系统: Windows Vista Home Premium (6.0, 版本 6001) 32-bit

客户操作系统: Ubuntu 10.04 Desktop (64-bit)

更新

根据 baizx 园友在 5 楼和 12 楼的评论,增加了在 Linux 操作系统中使用 gcc 优化编译的测试,见表格(e)行。不知道为什么 gcc 缺省情况下为什么不启用优化编译。是不是优化编译有可能在某种情况下编译出不正确的程序?

posted on 2010-07-24 12:22  银河  阅读(5160)  评论(40编辑  收藏  举报