【发布时间】:2015-04-08 18:08:15
【问题描述】:
我一直在做一些LeetCode problems,我注意到 C 解决方案比 C++ 中完全相同的解决方案快几倍。例如:
更新了几个更简单的例子:
给定一个排序数组和一个目标值,如果找到目标则返回索引。如果不是,则返回按顺序插入的索引。您可以假设数组中没有重复项。 (Link to question on LeetCode)
我的 C 语言解决方案在 3 毫秒内运行:
int searchInsert(int A[], int n, int target) {
int left = 0;
int right = n;
int mid = 0;
while (left<right) {
mid = (left + right) / 2;
if (A[mid]<target) {
left = mid + 1;
}
else if (A[mid]>target) {
right = mid;
}
else {
return mid;
}
}
return left;
}
我的其他 C++ 解决方案,完全相同,但作为解决方案类的成员函数在 13 毫秒内运行:
class Solution {
public:
int searchInsert(int A[], int n, int target) {
int left = 0;
int right = n;
int mid = 0;
while (left<right) {
mid = (left + right) / 2;
if (A[mid]<target) {
left = mid + 1;
}
else if (A[mid]>target) {
right = mid;
}
else {
return mid;
}
}
return left;
}
};
更简单的例子:
反转整数的数字。如果结果将溢出,则返回 0。 (Link to question on LeetCode)
C 版本在 6 毫秒内运行:
int reverse(int x) {
long rev = x % 10;
x /= 10;
while (x != 0) {
rev *= 10L;
rev += x % 10;
x /= 10;
if (rev>(-1U >> 1) || rev < (1 << 31)) {
return 0;
}
}
return rev;
}
和 C++ 版本完全一样,只是作为解决方案类的成员函数,运行时间为 19 毫秒:
class Solution {
public:
int reverse(int x) {
long rev = x % 10;
x /= 10;
while (x != 0) {
rev *= 10L;
rev += x % 10;
x /= 10;
if (rev>(-1U >> 1) || rev < (1 << 31)) {
return 0;
}
}
return rev;
}
};
我看到如果 LeetCode 测试系统没有在启用优化的情况下编译代码,那么在原始示例中使用向量的向量作为 2D 数组会有相当大的开销。但是上面更简单的例子不应该遇到这个问题,因为数据结构非常原始,尤其是在第二种情况下,你所拥有的只是长整数或整数算术。这仍然慢了三倍。
我开始认为 LeetCode 通常进行基准测试的方式可能会发生一些奇怪的事情,因为即使在整数反转问题的 C 版本中,仅替换行就会导致运行时间大幅增加 if (rev>(-1U >> 1) || rev INT_MAX || rev
现在,我想#include<limits.h> 可能与此有关,但这个简单的更改将执行时间从 6 毫秒提高到 19 毫秒似乎有点极端。
【问题讨论】:
-
因为您使用的是向量的向量,这意味着您的每一列(或行)都是一个单独的分配,可能位于也可能不在缓存友好的位置。
-
这些时间是单次运行还是多次运行的平均值?
-
你优化了吗?一些 C++ 编译器在向量关闭时启用范围检查,例如在调试版本中。
-
您使用什么编译器选项?为什么将 C++ 解决方案包装在一个类中,您是否在计时器中包含了类初始化?说到这里,你是如何安排执行时间的?
-
分配方案不同吗? C 中的单个块与 C++ 中的多个块(向量)。无论如何,您应该更喜欢单个内存块(没有向量的向量)。