Будет ли компилятор оптимизировать для l oop в соответствии с шириной шины? - PullRequest
3 голосов
/ 30 мая 2020

Допустим, я хочу установить массив символов с разными значениями, но для простоты:

char buff[1024];
...
for (int i = 0; i < 1024; i++) buff[i] = NULL;

Собирается ли компилятор оптимизировать это, чтобы соответствовать ширине шины? Или мне следует сделать это вручную:

char buff[1024];
...
size_t empty = NULL;
for (int i = 0; i < 1024 / sizeof(size_t); i++)
    memcpy(buff + i * sizeof(size_t), &empty, sizeof(size_t));

Я не уверен, что sizeof(size_t) - это ширина шины.

Я провел некоторые измерения, я думаю, это просто подтверждает заявленные моменты:

#define TIMES 512
#define SIZE 4194304

int main(void) {

    char *buff = new char[SIZE];

    int times = TIMES;

    clock_t begin = clock();

    void *pattern = (void*)0xffeeddcc;

    while (times--) {

        ... some for loop ...
    };

    clock_t end = clock();

    delete[] buff;

    std::cout << ((float)(end - begin) / CLOCKS_PER_SEC) << " s elapsed.\n";

    return 0;
};

Установить символ по символу:

for (int i = 0; i < SIZE; i++) buff[i] = i % 0xff;

Среднее прошедшее время: 13.6284 s

Установить фиксированный размер за раз (ширина шины):

for (int i = 0; i < SIZE / sizeof(void*); i++) {
    void* sub = (void*)(((i * sizeof(void*)) % 0xff) + (((i * sizeof(size_t) + 1) % 0xff) << 8) + (((i * sizeof(void*) + 2) % 0xff) << 16) + (((i * sizeof(void*) + 3) % 0xff) << 24));

    memcpy(buff + i * sizeof(void*), &sub, sizeof(void*));
};

Среднее затраченное время: 19.4352 s

Символ шаблона по символу:

for (int i = 0; i < SIZE; i++) buff[i] = ((char*)&pattern)[i % sizeof(void*)];

Среднее затраченное время: 17.1696 s

Фиксированный размер шаблона ( ширина шины):

for (int i = 0; i < SIZE / sizeof(void*); i++) memcpy(buff + i * sizeof(void*), &pattern, sizeof(void*));

Среднее затраченное время: 5.6976 s

Я не знаю, были ли все эти измерения необходимы XD

Сделано с 2 ГГц, 2-ядерный ЦП (Intel Core i3-5005U с частотой 2,00 ГГц).

1 Ответ

1 голос
/ 30 мая 2020

Если вы устанавливаете для каждого char одно и то же значение, просто вызовите memset.

Компиляторы (а именно g cc и clang) распознают циклы, такие как

for (int i = 0; i < 1024; i++) buff[i] = 0xff;

Clang превращает его в вызов memset; gcc использует инструкции, устанавливая по одному слову за раз: https://gcc.godbolt.org/z/ovRTPU. Но вы получите тот же вывод сборки, сделав вызов memset (обычно это функция, распознаваемая компилятором (например, memcpy)).

Если вы устанавливаете буфер с хранилищем stati c на 0, вам не нужно ничего делать, потому что он уже будет обнулен к моменту загрузки программы.

(Кстати, использование NULL для 0 не очень хорошая идея . По крайней мере, в C, NULL может быть (void*)0, который не будет назначен целочисленному типу без предупреждений / ошибок.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...