【问题标题】:Weird Execution Times奇怪的执行时间
【发布时间】:2017-11-26 20:29:16
【问题描述】:

问题在于不同输入大小的执行时间序列中存在一些不连续性。 具体来说,我一直在尝试这段代码:

long double a[2000][2000];
int iter = 0;
int main(int argc, char const *argv[]){
    istringstream is(argv[1]);
    int N;
    is >> N;
    for(int i = 0; i <= N; ++i){
        for (int J = 0; J <= N; ++J){
            a[i][J]  = (rand()%3+1)*(rand()%4+1);
        }
    }
    clock_t clk= clock();
    for(int k = 0; k < N; ++k){
        for(int i = k+1; i < N; ++i){
            a[i][k] = a[i][k]/a[k][k];
        }
        for(int i = k+1; i < N; ++i){
            for(int j = k+1; j < N; ++j){
                iter++;
                a[i][j] = a[i][j] - a[i][k]*a[k][j];
            }
        }
    }
    clk = clock() - clk;
    cout << "Time: " << ((double)clk)/CLOCKS_PER_SEC << "\n";
    cout << iter << endl;
}

使用 g++ 5.4.1 进行 C++14 编译。

我尝试了 N 的各种值的代码。然而,在 N = 500 附近发生了一些非常奇怪的事情。执行时间如下所列。 (这些是不同 N 值的代码输出。

N = 200 : 0.022136
N = 300 : 0.06792
N = 400 : 0.149622
N = 500 : 11.8341
N = 600 : 0.508186
N = 700 : 0.805481
N = 800 : 1.2062
N = 900 : 1.7092
N = 1000 : 2.35809

我尝试了 N = 500 很多次,并且还在另一台机器上尝试了类似的结果。

我们有大约 500 个:

N = 494 : 0.282626
N = 495 : 0.284564
N = 496 : 11.5308
N = 497 : 0.288031
N = 498 : 0.289903
N = 499 : 11.9615
N = 500 : 12.4032
N = 501 : 0.293737
N = 502 : 0.295729
N = 503 : 0.297859
N = 504 : 12.4154
N = 505 : 0.301002
N = 506 : 0.304718
N = 507 : 12.4385

为什么会这样?

【问题讨论】:

  • aiter 声明为...
  • 我可以确认上面的结果是准确的。也许迭代器变量被调用更多?
  • 这可能是由于缓存的影响。我现在没有时间详细检查所有代码,但可能值得一看 igoro.com/archive/gallery-of-processor-cache-effects(特别是示例 5)
  • @AdityaJain,如果您使用的是 linux,请尝试使用 perf。它应该给你很多指标。包括缓存争用。
  • 可以在ideone上转载(ideone.com/FGA8UJ):400、600、800都可以。 500 由于超时而失败。难以置信!

标签: c++ compiler-optimization execution-time


【解决方案1】:

您的程序可能存在浮点溢出和在某些情况下导致 NaN 的操作(如果计算结果为无穷大/NaN,那么它会扩散到您的算法中,因此几乎所有数字都变为无穷大/NaN。这取决于 @ 987654322@ 的输出。如果使用 srand() 更改种子,N=500 的情况可能不会减慢)。

而且,因为您使用 long double,所以编译后的程序使用 FPU(如果您为 FPU 而不是 SSE 编译,您也可以使用 floatdouble 重现此情况)。看起来,FPU 处理无限数比正常数慢得多。

您可以使用此 sn-p 轻松重现此问题:

int main() {
    volatile long double z = 2;

    for (int i=0; i<10000000; i++) {
        z *= z;
    }

    return z;
}

如果z 使用 2,此程序运行缓慢(z 会溢出)。如果你用 1 替换它,它会变得很快(z 不会溢出)。

您可以在此处阅读更多信息:https://randomascii.wordpress.com/2012/05/20/thats-not-normalthe-performance-of-odd-floats/

以下是相关部分:

对 x87 FPU 的性能影响

英特尔的 x87 单元在这些 NaN 和无穷大上的性能是 很糟糕。 [...] 即使在今天,在 SandyBridge 处理器上,x87 FPU 在 NaN 和无穷大上造成约 370 比 1 的减速

【讨论】:

  • 我不明白这如何解释 sn-p 中显示的具体数字...
  • @immortal:为什么?
  • 因为你的解释对于某个阈值的任何 N 都应该是正确的,而不是 500 ……为什么 500 是特殊的?为什么 700 或 800 不发生这种情况?请注意,提到这一点的 cmets 特别是 500 可重现
  • @immortal:很多数字都会出现这种情况,而不仅仅是 500。
  • @immortal:您需要重新阅读问题本身,您还会看到其他示例,而不仅仅是 500 个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-09
相关资源
最近更新 更多