如何在C#和C++中测量经过的时间

17

我有一个简单的C#和C++代码,可以计算点积的总和。

C#代码如下:

using System;

namespace DotPerfTestCS
{
    class Program
    {
        struct Point3D
        {
            public double X, Y, Z;

            public Point3D(double x, double y, double z)
            {
                X = x;
                Y = y;
                Z = z;
            }
        }

        static void RunTest()
        {
            unchecked
            {
                const int numPoints = 100000;
                const int numIters = 100000000;

                Point3D[] pts = new Point3D[numPoints];
                for (int i = 0; i < numPoints; i++) pts[i] = new Point3D(i, i + 1, i + 2);

                var begin = DateTime.Now;
                double sum = 0.0;
                var u = new Point3D(1, 2, 3);
                for (int i = 0; i < numIters; i++)
                {
                    var v = pts[i % numPoints];
                    sum += u.X * v.X + u.Y * v.Y + u.Z * v.Z;
                }
                var end = DateTime.Now;
                Console.WriteLine("Sum: {0} Time elapsed: {1} ms", sum, (end - begin).TotalMilliseconds);
            }
        }

        static void Main(string[] args)
        {
            for (int i = 0; i < 5; i++) RunTest();
        }
    }
}

而且C++是

#include <iostream>
#include <vector>
#include <time.h>

using namespace std;

typedef struct point3d
{
    double x, y, z;

    point3d(double x, double y, double z)
    {
        this->x = x;
        this->y = y;
        this->z = z;
    }
} point3d_t;

double diffclock(clock_t clock1,clock_t clock2)
{
    double diffticks=clock1-clock2;
    double diffms=(diffticks*10)/CLOCKS_PER_SEC;
    return diffms;
}

void runTest()
{
    const int numPoints = 100000;
    const int numIters = 100000000;

    vector<point3d_t> pts;
    for (int i = 0; i < numPoints; i++) pts.push_back(point3d_t(i, i + 1, i + 2));

    auto begin = clock();
    double sum = 0.0, dum = 0.0;
    point3d_t u(1, 2, 3);
    for (int i = 0; i < numIters; i++) 
    {
        point3d_t v = pts[i % numPoints];
        sum += u.x * v.x + u.y * v.y + u.z * v.z;
    }
    auto end = clock();
    cout << "Sum: " << sum << " Time elapsed: " << double(diffclock(end,begin)) << " ms" << endl;

}

int main()
{
    for (int i = 0; i < 5; i++) runTest();
    return 0;
}

C#版本(启用优化的Release x86版本,x64更慢)的输出结果为:

Sum: 30000500000000 Time elapsed: 551.0299 ms 
Sum: 30000500000000 Time elapsed: 551.0315 ms 
Sum: 30000500000000 Time elapsed: 552.0294 ms
Sum: 30000500000000 Time elapsed: 551.0316 ms 
Sum: 30000500000000 Time elapsed: 550.0315 ms

使用C++(默认的VS2010发布版本设置)会产生如下结果:

Sum: 3.00005e+013 Time elapsed: 4.27 ms
Sum: 3.00005e+013 Time elapsed: 4.27 ms
Sum: 3.00005e+013 Time elapsed: 4.25 ms
Sum: 3.00005e+013 Time elapsed: 4.25 ms
Sum: 3.00005e+013 Time elapsed: 4.25 ms

我本来期望C#代码会慢一些,但是慢了130倍对我来说太多了。请问有人能解释一下发生了什么吗?

编辑

我不是C++程序员,只是从互联网上随便找了一个diffclock代码而没有真正检查它是否正确。

使用std::difftime,C++的结果是

Sum: 3.00005e+013 Time elapsed: 457 ms
Sum: 3.00005e+013 Time elapsed: 452 ms
Sum: 3.00005e+013 Time elapsed: 451 ms
Sum: 3.00005e+013 Time elapsed: 451 ms
Sum: 3.00005e+013 Time elapsed: 451 ms

这似乎是正确的。


4
在C#示例中,你的测试考虑了JITer成本。这通常是不必要的。运行方法一次以排除JIT,然后再运行测试并比较数字。 - JaredPar
7
在计时方面,不应使用 DateTime.Now,建议使用 Stopwatch 类。尽管我怀疑这是造成差异的原因。 - DeCaf
3
@Ed S. 我那里没有计算平均值…… - Dave
2
你是在没有附加调试器的情况下以发布模式运行它吗?(从Visual Studio使用CTRL+F5或直接从控制台运行) - xanatos
1
我提出一个新的标题:“C#和C++之间存在巨大的可理解性差距”。 - agent-j
显示剩余13条评论
3个回答

13

你的diffclock代码是错误的。

如果你将你的C++代码更改为使用std::clockstd::difftime,它似乎可以显示实际运行时间:

#include <iostream>
#include <vector>
#include <ctime>

using namespace std;

typedef struct point3d
{
    double x, y, z;

    point3d(double x, double y, double z)
    {
        this->x = x;
        this->y = y;
        this->z = z;
    }
} point3d_t;

void runTest()
{
    const int numPoints = 100000;
    const int numIters = 100000000;

    vector<point3d_t> pts;
    for (int i = 0; i < numPoints; i++) pts.push_back(point3d_t(i, i + 1, i + 2));

    auto begin = clock();
    double sum = 0.0, dum = 0.0;
    point3d_t u(1, 2, 3);
    for (int i = 0; i < numIters; i++) 
    {
        point3d_t v = pts[i % numPoints];
        sum += u.x * v.x + u.y * v.y + u.z * v.z;
    }
    auto end = clock();
    cout << "Sum: " << sum << " Time elapsed: " << double(std::difftime(end,begin)) << " ms" << endl;

}

int main()
{
    for (int i = 0; i < 5; i++) runTest();
    return 0;
}

结果:

Sum: 3.00005e+013 Time elapsed: 346 ms
Sum: 3.00005e+013 Time elapsed: 344 ms
Sum: 3.00005e+013 Time elapsed: 346 ms
Sum: 3.00005e+013 Time elapsed: 347 ms
Sum: 3.00005e+013 Time elapsed: 347 ms

这是在默认发布模式下,在vs2010之外运行应用程序。

编辑

正如其他人指出的那样,在C++中使用 clock() 不是计时函数最准确的方式(类似于C#中,Stopwatch 比 DateTime 更好)。

如果你正在使用 Windows,你可以始终使用 QueryPerformanceCounter 进行高分辨率计时。


2
啊,谢谢。我猜这就是当你从互联网上复制粘贴一些代码而没有思考它的时候会发生的事情。 - Dave

6

我相信你会发现你的diffclock实现会产生十分之一秒的时间差,而不是毫秒(假设CLOCKS_PER_SECOND命名准确)。纠正这个问题后,C#实现的速度大约慢了30%,这似乎是合适的。


是的,没错。所有关于JIT的讨论可能占30%,但绝不会达到130倍。 - Mark Ransom

0

最明显的原因可能是JIT,但一旦验证不是原因,我有另一个解释。

"new Point3D"出现了100000次。这是100000个堆分配,稍后会被释放。在C++版本中,vector也是基于堆的,这意味着当它增长时,会进行重新分配。但是当vector增长时,每次增加的点数远远超过一个point3d_t。我预计C++版本只需要30次左右的重新分配调用。


那也是我的第一个猜想,但分配都是在时间开始之前(除了 var u = new Point3D(1, 2, 3);,但一个分配不应该导致那么大的偏差)。 - Brendan Long
1
Point3d是一个结构体,因此它并不是真正的堆分配。数组在堆中,但那是单个分配。 - agent-j
但这不是计时的一部分。 - DeCaf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接