Realloc vs (malloc + memset) - C (СИ)
Формулировка задачи:
На одном форуме мне сказали, что realloc работает чуть ли не в полтора раза медленнее связки malloc + memset. Вот решил проверить (и ужаснуться, как выяснилось позже). Программы простые, единственный параметр командной строки задает начальный размер массива. Может, конечно, я где в коде накосячил, так что гляньте свежим взглядом заодно.
Вот код:
malloc + memcpy
realloc
результаты такие (у меня FreeBSD 9.1, amd64):
Если в коде косяков нет, то почему такая большая разница в производительности?
Кому не лень, для статистики, просьба запустить тесты у себя и выложить результат (с указанием ОС).
теперь realloc отдает разные адреса, но, даже с учетом вывода в файл, работает в неизвестно сколько раз быстрее (malloc падает при начальном размере в 36 int'ов, realloc при таких условиях отрабатывает за время 0,00 real 0,00 user 0,00 sys) Так что мой вопрос остается открытым!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned long int size;
int main(int argc, char* argv[])
{
int *buffer, *buffer2;
int crc = 0;
unsigned long int i, j;
if(argc != 2)
return 1;
size = atol(argv[1]);
buffer = (int*)malloc(size * sizeof(int));
if(buffer == 0)
{
puts("ERR1");
exit(1);
}
i = 0;
while(i < size)
{
crc += buffer[i];
++i;
}
for(j = 0; j < 10000; ++j)
{
buffer2 = (int*)malloc((j + size) * sizeof(int));
if(buffer2 == 0)
{
puts("ERR2");
exit(1);
}
memcpy(buffer2, buffer, (j + size - 1) * sizeof(int));
buffer = buffer2;
}
i = 0;
while(i < size + j - 1)
{
crc += buffer[i];
++i;
}
printf("%d %li\n", crc, size);
return 0;
}#include <stdio.h>
#include <stdlib.h>
unsigned long int size;
int main(int argc, char* argv[])
{
int *buffer;
int crc = 0;
unsigned long int i, j;
if(argc != 2)
return 1;
size = atol(argv[1]);
buffer = (int*)malloc(size * sizeof(int));
if(buffer == 0)
{
puts("ERR1");
exit(1);
}
i = 0;
while(i < size)
{
crc += buffer[i];
++i;
}
for(j = 0; j < 10000; ++j){
buffer = (int*)realloc(buffer, (j + size) * sizeof(int));
if(buffer == 0)
{
puts("ERR2");
exit(1);
}
}
i = 0;
while(i < size + j - 1)
{
crc += buffer[i];
++i;
}
printf("%d %li\n", crc, size);
return 0;
}$ gcc49 -Wall malloc_test.c -o malloc
$ gcc49 -Wall realloc_test.c -o realloc
$ time ./realloc 100000000
0 100000000
0,56 real 0,44 user 0,11 sys
$ time ./malloc 100000000
time: command terminated abnormally
3,05 real 1,18 user 1,60 sys
Killed
$ time ./realloc 10000
0 10000
0,01 real 0,00 user 0,00 sys
$ time ./malloc 10000
0 10000
1,36 real 0,57 user 0,78 sys
$ gcc49 -Wall -O4 malloc_test.c -o malloc
$ gcc49 -Wall -O4 realloc_test.c -o realloc
$ time ./realloc 100000000
0 100000000
0,42 real 0,14 user 0,27 sys
$ time ./malloc 100000000
time: command terminated abnormally
3,13 real 0,87 user 1,99 sys
Killed
$ time ./realloc 100000
0 100000
0,00 real 0,00 user 0,00 sys
$ time ./malloc 100000
0 100000
1,28 real 0,40 user 0,87 sys
realloc не заморачивался и отдавал память по старому адресу, чтобы ничего не копировать.
заменил рост буфера (вместо прибавления размера на один int сделал домножение размера)
for(j = 1; j < 10000; ++j){
buffer = (int*)realloc(buffer, (j * size) * sizeof(int));
printf("%p\n", buffer);
if(buffer == 0)
{
puts("ERR2");
exit(1);
}
}
для тестов, полная версия с домножением размера буфера
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned long int size;
int main(int argc, char* argv[])
{
int *buffer, *buffer2;
int crc = 0;
unsigned long int i, j;
if(argc != 2)
return 1;
size = atol(argv[1]);
buffer = (int*)malloc(size * sizeof(int));
if(buffer == 0)
{
puts("ERR1");
exit(1);
}
i = 0;
while(i < size)
{
crc += buffer[i];
++i;
}
for(j = 1; j < 10000; ++j)
{
buffer2 = (int*)malloc((j * size) * sizeof(int));
if(buffer2 == 0)
{
puts("ERR2");
exit(1);
}
memcpy(buffer2, buffer, (size * (j - 1)) * sizeof(int));
buffer = buffer2;
}
i = 0;
while(i < size * j - 1)
{
crc += buffer[i];
++i;
}
printf("%d %li\n", crc, size);
return 0;
}#include <stdio.h>
#include <stdlib.h>
unsigned long int size;
int main(int argc, char* argv[])
{
int *buffer;
int crc = 0;
unsigned long int i, j;
if(argc != 2)
return 1;
size = atol(argv[1]);
buffer = (int*)malloc(size * sizeof(int));
if(buffer == 0)
{
puts("ERR1");
exit(1);
}
i = 0;
while(i < size)
{
crc += buffer[i];
++i;
}
for(j = 1; j < 10000; ++j){
buffer = (int*)realloc(buffer, (j * size) * sizeof(int));
if(buffer == 0)
{
puts("ERR2");
exit(1);
}
}
i = 0;
while(i < size * j - 1)
{
crc += buffer[i];
++i;
}
printf("%d %li\n", crc, size);
return 0;
}Решение задачи: «Realloc vs (malloc + memset)»
textual
Листинг программы
for(j = 1; j < 10000; ++j){
buffer = (int*)realloc(buffer, (j * size) * sizeof(int));
if(buffer2 == buffer) ++h;
buffer2 = buffer;
if(buffer == 0)
{
puts("ERR2");
exit(1);
}
}
Объяснение кода листинга программы
В этом коде выполняется следующее:
- Выделяется память под массив int с помощью функции realloc.
- Если выделенная память равна нулю, то выводится сообщение об ошибке и программа завершается.
- Выполняется проверка выделенной памяти на равенство нулю.
- Если выделенная память равна нулю, то выводится сообщение об ошибке и программа завершается.
- Выполняется цикл, который повторяется 10000 раз.
- В каждой итерации цикла выполняется следующее:
- Выделяется память под массив int с помощью функции realloc.
- Если выделенная память равна нулю, то выводится сообщение об ошибке и программа завершается.
- Выполняется проверка выделенной памяти на равенство нулю.
- Если выделенная память равна нулю, то выводится сообщение об ошибке и программа завершается.
- Размер массива увеличивается на 1.
- Если размер массива равен 10000, то выполняется попытка выделить память под массив int с помощью функции realloc.
- Если выделенная память равна нулю, то выводится сообщение об ошибке и программа завершается.
- Выполняется проверка выделенной памяти на равенство нулю.
- Если выделенная память равна нулю, то выводится сообщение об ошибке и программа завершается.
- Размер массива увеличивается на 1.
- Если размер массива равен 10000, то выполняется попытка выделить память под массив int с помощью функции realloc.
- Если выделенная память равна нулю, то выводится сообщение об ошибке и программа завершается.
- Выполняется проверка выделенной памяти на равенство нулю.
- Если выделенная память равна нулю, то выводится сообщение об ошибке и программа завершается.