Странное потребление памяти функциями чтения C fread / C ++, основанными на данных sysinfo Linux - PullRequest
0 голосов
/ 19 мая 2018

Окей, у меня странное (на мой взгляд) поведение моей программы, которое теперь сводится к простому чтению 3 массивов из довольно больших (приблизительно 24 ГБ и 48 ГБ) двоичных файлов.Структура этих файлов довольно проста, они содержат небольшой заголовок и 3 массива после: типа int, int и float, все 3 размера N, где N очень большое: 2147483648 для файла 28 ГБ и 4294967296 для 48 ГБone.

Чтобы отследить потребление памяти, я использую простую функцию, основанную на sysinfo Linux, чтобы определить, сколько свободной памяти у меня есть на каждом этапе моей программы (например, после того, как я выделил массивы дляхранить данные и при чтении файла).Вот код функции:

#include <sys/sysinfo.h>
size_t get_free_memory_in_MB()
{
    struct sysinfo info;
    sysinfo(&info);
    return info.freeram / (1024 * 1024);
}

Теперь прямо к проблеме: странная часть заключается в том, что после чтения каждого из 3 массивов из файла с использованием стандартной функции C fread или функции чтения C ++ (нене имеет значения), и, проверяя, сколько свободной памяти у нас есть после чтения, я вижу, что объем свободной памяти сильно сокращается (примерно на edge_count * sizeof (int) для следующего примера).

fread(src_ids, sizeof(int), edges_count, graph_file);
cout << "1 test: " << get_free_memory_in_MB() << " MB" << endl;

Таким образом, после прочтения всего файла мое потребление памяти по данным sysinfo почти в 2 раза больше, чем ожидалось.Чтобы лучше проиллюстрировать проблему, я приведу код всей функции вместе с ее выводом;Пожалуйста, прочитайте его, он очень маленький и лучше проиллюстрирует проблему.

bool load_from_edges_list_bin_file(string _file_name)
{
    bool directed = true;
    int vertices_count = 1;
    long long int edges_count = 0;

    // open the file
    FILE *graph_file = fopen(_file_name.c_str(), "r");
    if(graph_file == NULL)
        return false;

    // just reading a simple header here
    fread(reinterpret_cast<char*>(&directed), sizeof(bool), 1, graph_file);
    fread(reinterpret_cast<char*>(&vertices_count), sizeof(int), 1, graph_file);
    fread(reinterpret_cast<char*>(&edges_count), sizeof(long long), 1, graph_file);

    cout << "edges count: " << edges_count << endl;
    cout << "Before graph alloc free memory: " << get_free_memory_in_MB() << " MB" << endl;

    // allocate the arrays to store the result
    int *src_ids = new int[edges_count];
    int *dst_ids = new int[edges_count];
    _TEdgeWeight *weights = new _TEdgeWeight[edges_count];

    cout << "After graph alloc free memory: " << get_free_memory_in_MB() << " MB" << endl;

    memset(src_ids, 0, edges_count * sizeof(int));
    memset(dst_ids, 0, edges_count * sizeof(int));
    memset(weights, 0, edges_count * sizeof(_TEdgeWeight));

    cout << "After memset: " << get_free_memory_in_MB() << " MB" << endl;

    // add edges from file
    fread(src_ids, sizeof(int), edges_count, graph_file);
    cout << "1 test: " << get_free_memory_in_MB() << " MB" << endl;

    fread(dst_ids, sizeof(int), edges_count, graph_file);
    cout << "2 test: " << get_free_memory_in_MB() << " MB" << endl;

    fread(weights, sizeof(_TEdgeWeight), edges_count, graph_file);
    cout << "3 test: " << get_free_memory_in_MB() << " MB" << endl;

    cout << "After actual load: " << get_free_memory_in_MB() << " MB" << endl;

    delete []src_ids;
    delete []dst_ids;
    delete []weights;

    cout << "After we removed the graph load: " << get_free_memory_in_MB() << " MB" << endl;

    fclose(graph_file);

    cout << "After we closed the file: " << get_free_memory_in_MB() << " MB" << endl;

    return true;
}

Итак, ничего сложного.Прямо к выводу (с некоторыми комментариями сформировать меня после //).Во-первых, для файла на 24 ГБ:

Loading graph...
edges count: 2147483648
Before graph alloc free memory: 91480 MB 
After graph alloc free memory: 91480 MB // allocated memory here, but noting changed, why?
After memset: 66857 MB // ok, we put some data into the memory (memset) and consumed exactly 24 GB, seems correct
1 test: 57658 MB // first read and we have lost 9 GB...
2 test: 48409 MB // -9 GB again...
3 test: 39161 MB // and once more...
After actual load: 39161 MB // we lost in total 27 GB during the reads. How???
After we removed the graph load: 63783 MB // removed the arrays from memory and freed the memory we have allocated
// 24 GB freed, but 27 are still consumed somewhere
After we closed the file: 63788 MB // closing the file doesn't help
Complete!
After we quit the function: 63788 MB // quitting the function doesn't help too.

Аналогично для файла на 48 ГБ:

edges count: 4294967296
Before graph alloc free memory: 91485 MB
After graph alloc free memory: 91485 MB
After memset: 42236 MB
1 test: 23784 MB
2 test: 5280 MB
3 test: 490 MB
After actual load: 490 MB
After we removed the graph load: 49737 MB
After we closed the file: 49741 MB
Complete!
After we quit the function: 49741 MB

Итак, что происходит внутри моей программы?

1) Почему во время чтения теряется так много памяти (как с использованием fread из C, так и из потоков файлов из c ++)?

2) Почему закрытие файла не освобождает потребляемую память?

3) Может быть, sysinfo показывает неверную информацию?

4) Может ли эта проблема быть связана с фрагментацией памяти?

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

Спасибо, что прочитали это!

1 Ответ

0 голосов
/ 19 мая 2018

Это почти наверняка дисковый (/ page) кеш.Когда вы читаете файл, операционная система сохраняет часть или все содержимое в памяти, тем самым уменьшая объем свободной памяти.Это необходимо для оптимизации чтения в будущем.

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

Вы сможете подтвердить это, отследив значение параметра bufferram в структуре sysinfo (https://www.systutorials.com/docs/linux/man/2-sysinfo/), или просмотрев выходные данные команды free -m до и после запуска вашей программы.

Для получения более подробной информации об этом см. Следующий ответ: https://superuser.com/questions/980820/what-is-the-difference-between-memfree-and-memavailable-in-proc-meminfo

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