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 操作系统下的运行结果如下:
Windows 操作系统下的运行结果如下:
将以上结果列表如下:
操作系统 | 编译器 | 运行时间(秒) | 相对比率 | |
---|---|---|---|---|
(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 缺省情况下为什么不启用优化编译。是不是优化编译有可能在某种情况下编译出不正确的程序?
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?