0 / 0 / 0
Регистрация: 18.01.2016
Сообщений: 15
1

Что не так с realloc?

01.11.2018, 09:08. Показов 2987. Ответов 18
Метки нет (Все метки)

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

В процессе тестирования выяснилось что реалок падает в рандомный промежуток времени, причем памяти ему достаточно, другое дело, что она, память, возможно сильно фрагментирована.

Язык программирования: си
Компилятор: сс (GNU 7.3.0)
Рабочее окружение: Cygwin64
Операционная система: Windows 10

В итоге написал небольшой кусочек кода, для проверки того, что происходит:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
#define COUNT    (100000)
#define SIZE       (100 * sizeof( size_t ))
 
int main()
{
    clock_t
    c = clock();
 
    size_t
    id,
    size = SIZE;
    
    size_t*
    items = NULL;
    
    printf( "\nTest start:" );
 
    for( id = 0; id < COUNT; id++ )
    {
        if ( size <= id ) size += SIZE;
        realloc( items, size * sizeof( size_t ) );
    }
 
    printf( "\nEnd task: %f sec\n", ( double )( clock() - c ) / CLK_TCK );
 
    free( items );
 
    return 0;
}
На этом странности не закончились, запускаю код на исполнение и получаю результат:

Test start:
End task: 0.031000 sec

Увеличиваю COUNT на порядок и получаю:

Test start:
End task: 19.688000 sec

т.е. падение производительности более чем в 600 раз!!!

При последующем увеличении значения COUNT еще в десять раз, у меня вырубается монитор и сбрасывается сессия, т.е. выкидывает из системы, а иногда вообще зависает комп.

Не ужели процесс выделения памяти в windows так плох??
Может ли кто нибудь подтвердить подобное поведение так же на других системах?
Прошу проверить и прокоментировать полученные результаты.
__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
01.11.2018, 09:08
Ответы с готовыми решениями:

Динамическая память. Что такое realloc?
Препод ни в какую не принимает ответы.Я начинающий(занимаюсь 1.5 месяца) Помогите пожалуйста,кто...

Что возвращает realloc, если не может увеличить?
b=(int*)realloc((void*p), Size); . И сохраняет ли при этом валидность память по указателю p?

Что быстрее/эффективнее malloc/realloc или new/delete?
Всем привет. Год программировал на СИ под микроконтроллеры... Начал изучать с++... Всё ново,...

Как сделать так что я мог умножать не на два числа а на 3,4,5 и так далее?
Вот код: #include &lt;iostream&gt; #include &lt;conio.h&gt; using namespace std; int main(){ float...

18
61 / 28 / 24
Регистрация: 28.09.2017
Сообщений: 399
01.11.2018, 09:26 2
Давай проанализируем твой код. COUNT = 100000, далее ты запускаешь цикл, то есть ты выделяешь память по 4 байта 100000 раз, причем ты выделяешь место под size переменных, и оно постоянно растет.
Посчитаем: size = SIZE, SIZE = 100 * sizeof(size_t) = 400. Далее в цикле условие строки 24 будет истинно, когда id % 400 == 0, то есть за цикл это произойдет 100000 / 400 = 250 раз. В финале программы size = 100000.
И далее моэно посчитать память которая выделяется на каждой итерации:
memory = 400 * 400 + 800 * 400 + 1200 * 400 + .... + 100000 = очень большое число.
Возможно я где то ошибся, но суть логики, надеюсь, понятна: ты просто забиваешь сво. оперативную память под завязку, поэтому программа и комп в целом начинают тупить.
0
0 / 0 / 0
Регистрация: 18.01.2016
Сообщений: 15
01.11.2018, 10:04  [ТС] 3
Возможно код вышел немного не удачным, написал на скорую руку в блокноте. Исчерпания памяти в диспетчере задач не наблюдал)))

Переписал реаллок на маллок, выходит все нормально. реаллок же падет уже на 10k запросх

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
 
#define COUNT      (100000)
#define SIZE       (100)
 
int main()
{
    clock_t
    c = clock();
 
    size_t
    id,
    size = SIZE,
    count = 0;
    
    size_t*
    items = NULL;
    
    size_t*
    items_temp;
    
    printf( "\nTest start:\n" );
 
    items = ( size_t* )malloc( size * sizeof( size_t ) );
    
    for( id = 0; id < COUNT; id++ )
    {
        if ( count >= size )
        {
            size += SIZE;
            //realloc( items, size * sizeof( size_t ) );
            items_temp = ( size_t* )malloc( size * sizeof( size_t ) );
            memcpy( items_temp, items, count * sizeof( size_t ) );
            free( items );
            items = items_temp;
        }
        
        //printf( "\rID: %010zu", id );
        
        items[ count ] = id;
        count++;        
    }
 
    printf( "\nEnd task: %f sec\n", ( double )( clock() - c ) / CLK_TCK );
 
    free( items );
 
    return 0;
}
Работает вплоть до 100M, дальше не проверял, долго ждать)
При увеличении значения COUNT наблюдается нелинейное падение производительнсти, но она не такое резкое.
0
16495 / 8988 / 2205
Регистрация: 30.01.2014
Сообщений: 15,603
01.11.2018, 10:14 4
piratkin,

C++
1
realloc( items, size * sizeof( size_t ) );
Вы же игнорируете возвращаемое значение. Realloc не гарантирует, что после перераспределения памяти указатель останется тем же самым. Как минимум, у вас в коде должно быть так:
C++
1
items = (size_t*)realloc( items, size * sizeof( size_t ) );
0
0 / 0 / 0
Регистрация: 18.01.2016
Сообщений: 15
01.11.2018, 10:23  [ТС] 5
Игнорирую, потому, что проверял, по какой-то причине NULL он не возвращает, а просто падает.
Это упрощенный пример, но могу добавить проверку для успокоения совести)

Кликните здесь для просмотра всего текста

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
 
#define COUNT      (10000)
#define SIZE       (100)
 
int main()
{
    clock_t
    c = clock();
 
    size_t
    id,
    size = SIZE,
    count = 0;
    
    size_t*
    items = NULL;
    
    size_t*
    items_temp;
    
    printf( "\nTest start:\n" );
 
    items = ( size_t* )malloc( size * sizeof( size_t ) );
    
    for( id = 0; id < COUNT; id++ )
    {
        if ( count >= size )
        {
            size += SIZE;
            if ( realloc( items, size * sizeof( size_t ) ) == NULL )
            {
                printf( "\nError Allocate memory!" );;
            }
            //items_temp = ( size_t* )malloc( size * sizeof( size_t ) );
            //memcpy( items_temp, items, count * sizeof( size_t ) );
            //free( items );
            //items = items_temp;
        }
        
        //printf( "\rID: %010zu", id );
        
        items[ count ] = id;
        count++;        
    }
 
    printf( "\nEnd task: %f sec\nTesting...", ( double )( clock() - c ) / CLK_TCK );
 
    for( id = 0; id < COUNT; id++ )
    {
        if ( items[ id ] != id )
        {
            printf( "\nBad id -> %zu", id );
            exit( EXIT_FAILURE );
        }
    }
    
    printf( "OK" );
    
    free( items );
 
    return 0;
}
0
Вездепух
Эксперт CЭксперт С++
10427 / 5697 / 1552
Регистрация: 18.10.2014
Сообщений: 14,059
01.11.2018, 10:28 6
Цитата Сообщение от piratkin Посмотреть сообщение
Игнорирую, потому, что проверял, по какой-то причине NULL он не возвращает, а просто падает.
Ну так после первого вызова такого realloc с потерей возвращаемого значения (как у вас) указатель items становится невалидным, т.е. превращается в бессмысленный мусор. Когда вы второй раз вызываете realloc с этим невалидным указателем - он падает.

Когда вы передаете в realloc бессмысленный мусор, realloc не может распознать тот факт, что это бессмысленный мусор. Поведение не определено. realloc просто падает. Это вы и наблюдаете.
0
16495 / 8988 / 2205
Регистрация: 30.01.2014
Сообщений: 15,603
01.11.2018, 10:30 7
piratkin, Повторю: realloc не гарантирует, что после перераспределения памяти указатель останется тем же самым. После первого realloc, потребовавшего выделения новой памяти, ваш указатель items становится невалидным, использование его в realloc на следующей итерации недопускается. У вас в коде ошибка, приводящая к UB.
0
0 / 0 / 0
Регистрация: 18.01.2016
Сообщений: 15
01.11.2018, 10:36  [ТС] 8
Согласен, был косяк, разобрался)
0
Диссидент
Эксперт C
27200 / 16954 / 3748
Регистрация: 24.12.2010
Сообщений: 38,141
01.11.2018, 10:49 9
Цитата Сообщение от piratkin Посмотреть сообщение
realloc( items, size * sizeof( size_t ) );
Ну конечно же! Нада типо
C++
1
item = realloc( items, size * sizeof( size_t ) );
да и как realloc может item изменить!? Это же Си-функция, все передается по значению.
Но я рад за вас, что проблема решена
0
0 / 0 / 0
Регистрация: 18.01.2016
Сообщений: 15
01.11.2018, 11:00  [ТС] 10
Это же Си-функция, все передается по значению
void * realloc( void * ptrmem, size_t size );
0
Диссидент
Эксперт C
27200 / 16954 / 3748
Регистрация: 24.12.2010
Сообщений: 38,141
01.11.2018, 11:11 11
piratkin, что вы хотите сказать? Да, передается указатель на область памяти. (в начале равный NULL, но речь не об этом) и эта память функции доступна. Так, функция может выделить другую память и туды переписать из той, старой. Но вот указатель на новую память - куды его девать? Как рассказать вызывающей программе, что все находится в новой памяти? Без возврата значения - никак! Через параметры - не получится. Ибо сама переменная *item Не доступна. Доступно только ее значение.
0
0 / 0 / 0
Регистрация: 18.01.2016
Сообщений: 15
01.11.2018, 11:25  [ТС] 12
да легко)

C
1
2
3
4
5
6
7
8
void * realloc( void * ptrmem, size_t size )
{
     //выделяем при необходимости новую память, переносим данные...
     //free( ptrmem ); // при необходимости 
    
    ptrmem = new_ptrmem;
    return new_ptrmem;
}
0
Диссидент
Эксперт C
27200 / 16954 / 3748
Регистрация: 24.12.2010
Сообщений: 38,141
01.11.2018, 11:39 13
piratkin, Попробуйте сами реализовать функцию reallocPirat через malloc. Только в таком виде у вас не получится. Надо будет
C
1
void * reallocPirat( void * ptrmem, size_t size, size_t oldsize )
Так как без хакерства получить старый объем не вытащить. Но транслятор-то знает, где он живет. Самая естественная реализация функции malloc - выделить в самом деле чуть больше памяти. На число + сама память. И тогда хакерский путь таков
C
1
2
void *p = malloc(size);
size_t oldsize = *((size_t *)((char *)p - siziof(size_t)));
Правда, никто не гарантирует, что реализация именно такова. Плюс могут быть игры с выравниваем. Поэтому лучше довериться realloc-у, четко представляя, как она работает.

Добавлено через 2 минуты
Цитата Сообщение от piratkin Посмотреть сообщение
//выделяем при необходимости новую память, переносим данные...
Вот тут, поподробнее, пожалуйста...
0
16495 / 8988 / 2205
Регистрация: 30.01.2014
Сообщений: 15,603
01.11.2018, 12:17 14
Цитата Сообщение от piratkin Посмотреть сообщение
да легко)
То-то я гляжу на форуме мало тем, где люди недоумевают почему снаружи функции ничего не поменялось

Это потому, что вы считаете указатели какой-то магией. Хотя на деле это точно также же переменные как и остальные.
Вот упростим ваш код для наглядности
C++
1
2
3
4
5
6
7
8
9
10
11
void foo(void * p)
{
    p = NULL;
}
 
int main()
{
    void * p = (void *)0xFF;
    foo(p);
    // чем равно p, нулю? как бы не так!
}
Теперь же перепишем код так:
C++
1
2
3
4
5
6
7
8
9
10
11
void foo(int p)
{
    p = 0;
}
 
int main()
{
    int p = 0xFF;
    foo(p);
    // чем равно p, нулю?
}
И мгновенно, все новички сразу же говорят: "ну там же копия значения int, поэтому ничего не изменилось!"
Да, а в первом случае, что, не копия значения указателя?
0
0 / 0 / 0
Регистрация: 18.01.2016
Сообщений: 15
01.11.2018, 12:26  [ТС] 15
Вопрос был в том можно ли изменить указатель? или я чего-то не понимаю?
Про получения старого обьема памяти рассуждать не имеет смысла, тк реалок этот обьем памяти получает.
0
16495 / 8988 / 2205
Регистрация: 30.01.2014
Сообщений: 15,603
01.11.2018, 12:30 16
Цитата Сообщение от piratkin Посмотреть сообщение
Вопрос был в том можно ли изменить указатель? или я чего-то не понимаю?
Это коментарий к вашему примеру:
Цитата Сообщение от piratkin Посмотреть сообщение
C++
1
2
3
4
5
6
7
void * realloc( void * ptrmem, size_t size )
{
//выделяем при необходимости новую память, переносим данные...
//free( ptrmem ); // при необходимости
ptrmem = new_ptrmem; //!!!
return new_ptrmem;
}
Конкретно к отмеченной строке.
А также к этой попытке поспорить:
Цитата Сообщение от piratkin Посмотреть сообщение
void * realloc( void * ptrmem, size_t size );
Указатель передается по значению (вам верно выше написали). Значение указателя снаружи функции не изменится, если написать в коде так, как вы написали выше.
0
0 / 0 / 0
Регистрация: 18.01.2016
Сообщений: 15
01.11.2018, 12:49  [ТС] 17
Указатель передается по значению
с этим согласен, но аргумент самой функции передается по указателю, а не по значению. Это все, что хотел сказать.

изменить указатель можно, но конечно надо передавать в таком случае адрес самого указателя, те невозможного тут ничего нет.

void foo(void ** p)
{
*p = NULL;
}

int main()
{
void * p = (void *)0xFF;
foo(&p);

}

но как вы "Байт" правильно заметили в реализации реалока сделать этого не получится, это я погорячился)
0
Диссидент
Эксперт C
27200 / 16954 / 3748
Регистрация: 24.12.2010
Сообщений: 38,141
01.11.2018, 16:18 18
Цитата Сообщение от piratkin Посмотреть сообщение
изменить указатель можно, но конечно надо передавать в таком случае адрес самого указателя, те невозможного тут ничего нет.
Совершенно верно! И функцию можно было бы сделать иначе
C
1
void realoc2(voip **pp, size_t size)
Но разработчики языка Си пошли тем путем, которым они пошли. Если вам хочется, вы можете дополнить их библиотеку (для себя) более удобными функциями.
0
3531 / 2190 / 401
Регистрация: 09.09.2017
Сообщений: 9,008
01.11.2018, 17:17 19
Только в качестве возвращаемого значения лучше поставить флаг ошибки, а то мало ли.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
01.11.2018, 17:17
Помогаю со студенческими работами здесь

Что в коде ни так? while не работает так, как ожидаю
Написанный код, как я думаю, должен выдавать цифры от 0 до 1000, столбиком. Но выдает начиная от...

Что-то не то с Майкрсофт визуал студио 2010 или я что-то не так делаю
Дело в том что при запуске вот этой программы: #include &lt;iostream&gt; using namespace std; int main...

Что в программе делает так, что процессор грузится на 100%?
Я не очень разбираюсь в С++, поэтому прошу вашей подсказки по поводу нагрузки на процессор. Вот...

Список: Что не так с выводом списка, потому что выводится какой-то мусор?
#include &lt;iostream&gt; using namespace std; struct point { int data;//информационное поле-это...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
19
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2023, CyberForum.ru