Проверяет ли границы на C или С++?

Связанная проверка стоит дорого ( > x2 раза превышение времени выполнения)

Я получил этот пункт выше от одного из моих профессоров. Я смущен этим. Как я знаю, наиболее трудоемкой частью программы является IO (из сети и с жестких дисков).

Но проверка границ на C или С++ не всегда связана с этими 2 источниками. Например, я копирую содержимое одного баффа другому в C с помощью memcpy(dest, src, length(src)). До этого я проверяю размер src, чтобы предотвратить переполнение кучи. Прецессией, которую я могу сделать, является: Получить начальный адрес src и байт \x00 в src (в представлении языка ассемблера я копирую содержимое src один за другим и вижу, если байт эквивалентно с \x00). Получив 2-й адрес, просто вычтите их, чтобы получить длину src. Я прочитал содержимое src из памяти. Мы все знаем, что чтение вещей из памяти происходит быстро.

Ответ 1

Я только что запускал программу, которую я проверил с проверкой границ итератора.

Время работы от 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);
}

2.09 секунд против 0.88 секунд.

Ответ 2

Это было правдой до 80-х годов.

С современной генерацией кода и высококонвейерными архитектурами процессоров проверка границ может быть выполнена за нулевую или очень небольшую дополнительную стоимость исполнения. Это связано с тем, что проверка границ может происходить параллельно с извлечением памяти.