33

在 ACM 示例中,我必须为动态编程构建一个大表。我必须在每个单元格中存储两个整数,所以我决定使用std::pair<int, int>. 然而,分配一个巨大的数组需要 1.5 秒:

std::pair<int, int> table[1001][1001];

之后,我将此代码更改为

struct Cell {
    int first;
    int second;
}

Cell table[1001][1001];

分配时间为 0 秒。

是什么解释了这种巨大的时间差异?

4

5 回答 5

41

std::pair<int, int>::pair()构造函数使用默认值初始化字段(在 的情况下为零int)而你struct Cell没有(因为你只有一个自动生成的默认构造函数什么都不做)。

初始化需要写入每个字段,这需要大量相对耗时的内存访问。struct Cell相反,什么都不做,什么都不做会快一点。

于 2009-10-22T12:35:16.757 回答
29

到目前为止的答案并不能解释问题的全部严重性。

正如尖牙指出的那样,对解决方案将值初始化为零。正如 Lemurik 指出的那样,pair 解决方案不仅仅是初始化一个连续的内存块,而是为表中的每个元素调用 pair 构造函数。但是,即使这样也不能说明它需要 1.5 秒。其他事情正在发生。

这是我的逻辑:

假设您在一台古老的机器上运行,假设以 1.33ghz 运行,那么 1.5 秒就是 2e9 个时钟周期。你有 2e6 对要构造,所以不知何故,每个对构造函数都需要 1000 个周期。调用仅将两个整数设置为零的构造函数不需要 1000 个周期。我看不出缓存未命中如何使其花费那么长时间。如果这个数字少于 100 个周期,我会相信它。

我认为看看所有这些 CPU 周期的其他地方会很有趣。我使用了我能找到的最糟糕的最古老的 C++ 编译器,看看我是否能达到所需的浪费水平。该编译器是 VC++ v6。在调试模式下,它会做一些我不明白的事情。它有一个大循环,为表中的每个项目调用 pair 构造函数——这很公平。该构造函数将这两个值设置为零 - 足够公平。但在此之前,它将 68 字节区域中的所有字节设置为 0xcc。该区域就在大桌子开始之前。然后它用 0x28F61200 覆盖该区域的最后一个元素。对构造函数的每次调用都会重复此操作。大概这是编译器的某种簿记,因此它知道在运行时检查指针错误时初始化了哪些区域。一世'

无论如何,这将解释额外时间的去向。显然另一个编译器可能没有这么糟糕。当然,优化的发布版本也不会。

于 2009-10-23T23:49:09.187 回答
3

这些都是很好的猜测,但众所周知,猜测并不可靠。

我想说只是在那 1.5 秒内随机暂停它,但你必须非常快。如果您将每个维度增加约 3 倍,您可能会花费 10 多秒,因此更容易暂停。

或者,您可以在调试器下获取它,在 pair 构造函数代码中对其进行分解,然后单步查看它在做什么。

无论哪种方式,您都会得到问题的明确答案,而不仅仅是猜测。

于 2009-10-24T15:07:34.160 回答
1

我猜这是创建 std::pair 的方式。与仅分配内存范围相比,调用对构造函数 1001x1001 次的开销更大。

于 2009-10-22T12:45:08.983 回答
-1

这确实是一个很好的例子,说明一个人应该编写 C++ 并小心使用 STL。有什么想法吗?

我的项目正在开发一个 C&C++ 代码级基准测试工具,我们将在其中制作大量代码示例,以找出什么是“好”代码,什么是“坏”编码习惯。请参阅http://effodevel.googlecode.com以了解有关 C9B.M 的更多信息。规划。如果您经历过很多这样的案例,请加入该项目以帮助我们。

于 2009-10-22T12:47:27.610 回答