Я узнал, что memset(ptr, 0, nbytes)
работает очень быстро, но есть ли более быстрый способ (по крайней мере, на x86)?
Я предполагаю, что memset использует mov
, однако при обнулении памяти большинство компиляторов используют xor
, поскольку это быстрее, правильно? edit1: Неправильно, поскольку GregS указал, что работает только с регистрами. О чем я думал?
И я спросил человека, который знал больше об ассемблере, чтобы посмотреть на stdlib, и он сказал мне, что на x86 memset не в полной мере использует 32-битные регистры. Однако в то время я очень устал, поэтому я не совсем уверен, что правильно понял.
edit2: Я снова просмотрел этот вопрос и провел небольшое тестирование. Вот что я тестировал:
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <sys/time.h>
#define TIME(body) do { \
struct timeval t1, t2; double elapsed; \
gettimeofday(&t1, NULL); \
body \
gettimeofday(&t2, NULL); \
elapsed = (t2.tv_sec - t1.tv_sec) * 1000.0 + (t2.tv_usec - t1.tv_usec) / 1000.0; \
printf("%s\n --- %f ---\n", #body, elapsed); } while(0) \
#define SIZE 0x1000000
void zero_1(void* buff, size_t size)
{
size_t i;
char* foo = buff;
for (i = 0; i < size; i++)
foo[i] = 0;
}
/* I foolishly assume size_t has register width */
void zero_sizet(void* buff, size_t size)
{
size_t i;
char* bar;
size_t* foo = buff;
for (i = 0; i < size / sizeof(size_t); i++)
foo[i] = 0;
// fixes bug pointed out by tristopia
bar = (char*)buff + size - size % sizeof(size_t);
for (i = 0; i < size % sizeof(size_t); i++)
bar[i] = 0;
}
int main()
{
char* buffer = malloc(SIZE);
TIME(
memset(buffer, 0, SIZE);
);
TIME(
zero_1(buffer, SIZE);
);
TIME(
zero_sizet(buffer, SIZE);
);
return 0;
}
результаты:
zero_1 является самым медленным, за исключением -O3. zero_sizet является самым быстрым с примерно равной производительностью по -O1, -O2 и -O3. memset всегда был медленнее, чем zero_sizet. (в два раза медленнее для -O3). одна вещь интересна тем, что при -O3 zero_1 был столь же быстрым, как и zero_sizet. однако дизассемблированная функция имела примерно в четыре раза больше инструкций (я думаю, что это вызвано разворачиванием цикла). Кроме того, я попытался оптимизировать zero_sizet дальше, но компилятор всегда меня превзошел, но здесь нет ничего удивительного.
Теперь, когда побеждает memset, предыдущие результаты были искажены кешем CPU. (все тесты выполнялись на Linux) Дальнейшее тестирование необходимо. Я попробую следующий ассемблер:)
edit3: исправлена ошибка в тестовом коде, результаты теста не затронуты
edit4: Во время разворачивания демонтированной среды VS2010 C я заметил, что memset
имеет оптимизированную для SSE процедуру для нуля. Это будет трудно превзойти.