C++ 与 C# 数组性能问题
Posted
技术标签:
【中文标题】C++ 与 C# 数组性能问题【英文标题】:C++ vs. C# Array Performance issues 【发布时间】:2017-08-02 04:03:49 【问题描述】:所以,我遇到了一种情况,我必须处理巨大的(多维)数组,只是想知道 C# 还是 C++ 的性能会更好。 (注意:我是 C++ 的初学者,所以不要期望太多关于它如何工作的知识!)。我认为就数组而言,两种语言的性能相似,也许 C++ 可能会稍微好一点,但是:结果告诉其他故事:
1012ms in C++ @32Bit
1020ms in C++ @32Bit
1002ms in C++ @32Bit
1155ms in C++ @64Bit
1098ms in C++ @64Bit
1122ms in C++ @64Bit
1136ms in C++ @64Bit
523ms in C# @32-Bit
545ms in C# @32-Bit
537ms in C# @32-Bit
536ms in C# @32-Bit
473ms in C# @64-Bit
501ms in C# @64-Bit
470ms in C# @64-Bit
498ms in C# @64-Bit
我在 x86 和 x64 架构上执行了一次测试运行。这里有两件事:为什么 C# 的性能几乎是 C++ 的两倍?为什么 C# 在 x64-Mode 和 C++ 在 x86-Mode 中实际上更快?!?我真的没想到会发生这种情况。
正如我所说,我目前在 C++ 编程方面没有那么丰富的经验,但我尽我所能在 C++ 中重现我的 C# 代码。
代码如下: C#
for (int j = 0; j < 4; j++)
Stopwatch sw = new Stopwatch();
sw.Start();
struct1[] s1 = new struct1[20000000];
int length = 20000000;
for (int i = 0; i < length; i++)
s1[i] = new struct1();
s1[i].samplechar = 'c';
s1[i].sampleInt = i * 2;
s1[i].sampledouble = Math.Sqrt(i);
sw.Stop();
GC.Collect();
Console.WriteLine(sw.ElapsedMilliseconds + "ms in C# @...-Bit");
与结构1:
public struct struct1
public int sampleInt;
public double sampledouble;
public char samplechar;
C++:
for (int j = 0; j < 4; j++)
auto begin = std::chrono::high_resolution_clock::now();
struct1* s1 = new struct1[20000000];
int length = 20000000;
for (int i = 0; i < length; i++)
s1[i].sampleChar = 'c';
s1[i].sampleInt = i * 2;
s1[i].sampleDouble = sqrt(i);
auto end = std::chrono::high_resolution_clock::now();
std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() << "ms in C++ @64Bit" << std::endl;
free(s1);
结构1:
struct struct1
public:
int sampleInt;
int sampleDouble;
char sampleChar;
;
注意:我没有将垃圾收集/免费包括在性能测量中,因为 - 就我而言 - 只要程序正在运行,就会存在一个巨大的数组。而且我认为很明显,除非你想杀死你的机器,否则这种大小的数组会被频繁地创建/删除......
注 2:另一个令人困惑的事情:虽然 C++ 消耗大约 250MB 的 RAM,但 C# 需要 500MB。但为什么呢?
提前感谢您的任何解释。也许我只是弄错了,而失败就在显示器后面,但我虽然对我得到这些结果的原因很感兴趣。
编辑:我在 Windows 10 上运行 Visual Studio 2017RC。C++ 优化已禁用 (/Od)
【问题讨论】:
您是否在启用优化的情况下进行编译? 当有人问“哪个更快?”时的必填链接ericlippert.com/2012/12/17/performance-rant 旁注:如果您使用new[]
创建数组,您应该使用delete[] s1;
而不是free(s1)
销毁它(尽管在c++11 中您不应该使用这些)
“编辑:我在 Windows 10 上运行 Visual Studio 2017RC。C++ 优化已禁用 (/Od)”,这就是你的答案。
优化被禁用 (/Od) -- 因此你的数字和发现毫无意义。在您发布优化构建的结果之前,无需讨论“哪个更快”。
【参考方案1】:
使用正确的优化和编译器设置,C++ 版本应该至少与 C# 版本一样快。请记住,您测量的时间并不完全相同(看看您是否可以在 C++ 中使用 StopWatch
尝试它?) - 请参阅此问题以获取更多详细信息:resolution of std::chrono::high_resolution_clock doesn't correspond to measurements - 这很可能是来源您的不同时间安排(特别是为什么 C++ 实现的测量时间更长)。 StopWatch
比您在 std::chrono::high_resolution_clock
的 VisualStudio/Windows 实现中使用的更准确。
另外请记住,您实际上并不是在处理多维数组(例如 C# 用语中的 int[,]
)——您只是在处理一个结构数组。对于它的价值,我的回忆是,实际多维数组的 CLR 实现并不是为大型集合的性能而构建的——它可能构建一个更快的(如果实际上你想要多维数组而不仅仅是结构数组)。
这里更重要的可能是能够更严格地控制内存管理 - 如果您要分配大型结构数组,则可以使用 C 或 C++ 比在垃圾收集环境。您还可以更轻松/自然地使用指针算术和内存复制 - 尽管这在 C# 中的 unsafe
上下文中也是完全可能的。与safe
上下文中的“普通”C# 实现相比,这可能会带来一些速度性能提升。
【讨论】:
【参考方案2】:除了丹菲尔德的回答:
注 2:另一个令人困惑的事情:虽然 C++ 消耗大约 250MB 的 RAM,但 C# 占用 500MB。但为什么呢?
在 C++ 中,您使用 int,int,char
,它(至少在 32 位系统上)将被填充为 4、4、4 字节(例如,结构的打包设置为 1 字节)。
在 C# 中,您使用int,double,char
,它将被填充到 8、8、8(或者可能是 4?)字节。第一个 int 被填充以在 8 个字节上对齐双精度。如果使用double,int,char
,则结构应该更小(可能是 16 个字节)。
虽然我不是 C# 专家,但我不明白为什么你在循环中使用 s1[i] = new struct1();
而已经有一个 struct1
的数组? (但我可能错了或遗漏了什么)。
另外,在测试数组性能时,我不会在循环中使用 sqrt()
,因为该函数比遍历项目更昂贵。
当然,在测试性能时应该启用优化。
使用循环注册甚至可以更快:
for (int i = 0; i < length; i+=4)
s1[i].sampleInt = i * 2;
s1[i].sampleDouble = i * 4;
s1[i].sampleChar = 'c'; // reordered in order of struct
// not sure if it matters
s1[i+1].sampleInt = (i+1) * 2;
s1[i+1].sampleDouble = (i+1) * 4;
s1[i+1].sampleChar = 'c';
// maybe 2 or 3 would be better than 4
s1[i+2].sampleInt = (i+2) * 2;
s1[i+2].sampleDouble = (i+2) * 4;
s1[i+2].sampleChar = 'c';
s1[i+3].sampleInt = (i+3) * 2;
s1[i+3].sampleDouble = (i+3) * 4;
s1[i+3].sampleChar = 'c';
对比
for (int i = 0; i < length; i++)
s1[i].sampleChar = 'c';
s1[i].sampleInt = i * 2;
s1[i].sampleDouble = i * 4;
【讨论】:
以上是关于C++ 与 C# 数组性能问题的主要内容,如果未能解决你的问题,请参考以下文章