绑定检查很昂贵
(>运行时开销的x2倍)
我从我的一位教授那里得到了这一点。我很困惑。
据我所知,程序中最耗时的部分是IO(来自网络和硬盘)。
但是用C或C ++检查边界并不总是与那两个输入源相关。
例如,我使用C将一个buff的内容复制到另一个buff memcpy(dest, src, length(src))
。在此之前,我检查大小 src
为了防止堆溢出。我可以成像的进动是:获取起始地址 src
和 \x00
字节 src
(在汇编语言的视图中,我复制了内容 src
逐个查看字节是否等效 \x00
)。获得2地址后,只需减去它们的长度即可 src
。我读了内容 src
从记忆中。我们都知道从记忆中读取东西很快。
我刚刚运行了一个程序,启用了迭代器边界检查。
运行时间从789毫秒增加到2608毫秒。
所以,是的,这很重要。不是所有的时间,但肯定比从来没有。
特别是,绑定检查的迭代器需要至少两倍于简单指针的存储空间,而且,不容易优化。从理论上讲,它们既简单又有效,但实际上你根本不想做你不需要的工作。
哦,我提到了 编 时间也从7.72秒增加到13.21秒?
对于你们中间的许多非信徒来说......一个微型的例子 没有边界检查的0.92秒 和 1.96秒。
因为有很多人持怀疑态度 一切, 包含 vector
效率......这是另一个:
#include <cstdio>
#include <ctime>
template<class T> struct Vector
{
T *b, *e;
Vector(size_t n) : b(new T[n]), e(b + n) { }
T &operator[](size_t i) { return b[i]; }
T &at(size_t i) { if (i >= e - b) { throw "invalid"; } return b[i]; }
};
#define at operator[] // Comment this out to enable bounds-checking
int main(int argc, char **argv)
{
Vector<size_t> v(1 << 16);
for (size_t *p = v.b; p != v.e; ++p) { *p = 1; }
clock_t begin = clock();
for (int j = 0; j < 1 << 12; ++j)
{
for (size_t i = 8, n = v.e - v.b; i < n; ++i)
{
v.at(i) += v.at(i - 8);
v.at(i) ^= v.at(i - 7);
v.at(i) -= v.at(i - 6);
v.at(i) ^= v.at(i - 5);
v.at(i) += v.at(i - 4);
v.at(i) ^= v.at(i - 3);
v.at(i) -= v.at(i - 2);
v.at(i) ^= v.at(i - 1);
}
}
clock_t end = clock();
fprintf(stderr, "%u\n", clock() - begin);
}
我刚刚运行了一个程序,启用了迭代器边界检查。
运行时间从789毫秒增加到2608毫秒。
所以,是的,这很重要。不是所有的时间,但肯定比从来没有。
特别是,绑定检查的迭代器需要至少两倍于简单指针的存储空间,而且,不容易优化。从理论上讲,它们既简单又有效,但实际上你根本不想做你不需要的工作。
哦,我提到了 编 时间也从7.72秒增加到13.21秒?
对于你们中间的许多非信徒来说......一个微型的例子 没有边界检查的0.92秒 和 1.96秒。
因为有很多人持怀疑态度 一切, 包含 vector
效率......这是另一个:
#include <cstdio>
#include <ctime>
template<class T> struct Vector
{
T *b, *e;
Vector(size_t n) : b(new T[n]), e(b + n) { }
T &operator[](size_t i) { return b[i]; }
T &at(size_t i) { if (i >= e - b) { throw "invalid"; } return b[i]; }
};
#define at operator[] // Comment this out to enable bounds-checking
int main(int argc, char **argv)
{
Vector<size_t> v(1 << 16);
for (size_t *p = v.b; p != v.e; ++p) { *p = 1; }
clock_t begin = clock();
for (int j = 0; j < 1 << 12; ++j)
{
for (size_t i = 8, n = v.e - v.b; i < n; ++i)
{
v.at(i) += v.at(i - 8);
v.at(i) ^= v.at(i - 7);
v.at(i) -= v.at(i - 6);
v.at(i) ^= v.at(i - 5);
v.at(i) += v.at(i - 4);
v.at(i) ^= v.at(i - 3);
v.at(i) -= v.at(i - 2);
v.at(i) ^= v.at(i - 1);
}
}
clock_t end = clock();
fprintf(stderr, "%u\n", clock() - begin);
}
这曾经是直到20世纪80年代才是真实的。
利用现代代码生成和高度流水线的CPU架构,可以在零或非常少的额外执行成本的情况下完成边界检查。这是因为边界检查可以与内存提取并行进行。