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# 数组性能问题的主要内容,如果未能解决你的问题,请参考以下文章

代理服务器的 C# 性能(与 C++ 相比)

C++ 数组 vs C# ptr 速度混淆

C++ 地图查找性能与 PHP 数组查找性能

C# 长度为 1 的数组与单值、性能和内存开销

C#中的大数组算术

非托管 C# 与 C++ [关闭]