HDF5 использует все идентификаторы ресурсов для DataSpaces и выходов (C ++ API)

РЕДАКТИРОВАТЬ: Оказывается, в библиотеке HDF5 C ++ 1.8.15 есть (известная?) Утечка памяти в отношении DataSet.getSpace ().

Я написал простую функцию обхода, которая позволяет избежать утечки кода путем исправления ошибок в библиотеке C. В связи с этим отмечу проблему как решенную.

H5::DataSpace getSpace( H5::DataSet& ds )
{
  hid_t id2 = ds.getId();
  hid_t myspace = H5Dget_space(id2);
  H5::DataSpace origspace( myspace );
  H5Sclose( myspace );
  return origspace;
}

Это может быть связано с https://github.com/h5py/h5py/issues/480 что относится к питону.

В API HDF5 (C ++?) Наблюдается массовая утечка памяти, связанная с пространствами памяти.

РЕДАКТИРОВАТЬ: найдено http://lists.hdfgroup.org/pipermail/hdf-forum_lists.hdfgroup.org/2015-August/008854.html

Я много раз читал / записывал набор данных HDF5 во время типичного запуска моего программного обеспечения. Каждый раз, когда я читаю подмножество строк, я должен создать пространство памяти и файловое пространство (чтобы гиперпластинка выбирала подмножество данных для чтения). Они явно потребляют «идентификаторы ресурсов» за кулисами HDF5. Поскольку я делаю это много-много раз (с тех пор, как я добавляю строки в матрицу и читаю их), в конечном итоге они заканчиваются. Это происходит, даже если я правильно «закрываю» пространства данных. Черт возьми, это даже кажется, если я не создаю новые пространства данных, а вместо этого повторно использую пространство данных через setExtentSimple ().

Вот МИНИМАЛЬНЫЙ код игрушки для воспроизведения проблемы! Мне вообще даже не нужно добавлять / читать / писать! Просто создайте пространство, как если бы я собирался его прочитать, а затем БАМ. Черт возьми, я создаю только одну строку в пространстве данных и просто делаю вид, будто прочитаю ее кучу. Обратите внимание, что это потребляет МАССИВНУЮ память (утечка памяти!) Кажется, это не освобождает DataSpace каждый раз ... чего и следовало ожидать, когда memspace H5 :: DataSpace в функции выходит за рамки !! Он выходит с ошибкой, например:

HDF5-DIAG: Error detected in HDF5 (1.8.15-patch1) thread 0:
  #000: H5S.c line 1393 in H5Screate_simple(): unable to register dataspace ID
    major: Object atom
    minor: Unable to register new atom
  #001: H5I.c line 902 in H5I_register(): no IDs available in type
    major: Object atom
    minor: Out of IDs for group
terminate called after throwing an instance of 'H5::DataSpaceIException'

Скомпилировать с помощью:

    h5c++ -std=c++11 -O2 -g testhdf5id.cpp -o testhdf5id.exe                                                                                                                                    

Файл testhdf5id.cpp:

#include <H5Cpp.h>
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <string>
#include <vector>

#include <cstdint>

//Open a dataset. Write 1 row to it. Read every row one at a time. Do this a gazillion times.

//Because what the heck? Hopefully it won't cache it.


hsize_t currid;

void addrow( H5::DataSet& ds, const std::vector<double>& rowtowrite )
{
  //Get the space (since it may have grown in length since last time of course )
  H5::DataSpace origspace = ds.getSpace();

  //get the rank, even though I know it is 2
  int rank = origspace.getSimpleExtentNdims();

  //Get the actual dimensions of the ranks.
  hsize_t dims[rank];
  int ndims = origspace.getSimpleExtentDims( dims, NULL);

  //Want to ADD a row, so need to offset at row = nrows, and col = 0;
  hsize_t offset[rank] = { dims[0], 0 }; 
  hsize_t dims_toadd[rank] = { 1, rowtowrite.size() }; //will write 1 row, ncols columns.

  //Compute "new" size (extended by 1 row).
  hsize_t size[rank] = { dims[0]+dims_toadd[0], rowtowrite.size() };

  //Do the extension.
  ds.extend( size );

  //Get the new (extended) space, and select the hyperslab to write the row to.
  origspace = ds.getSpace();
  origspace.selectHyperslab( H5S_SELECT_SET, dims_toadd, offset );

  //Make the "memory" data space?
  H5::DataSpace toaddspace(rank, dims_toadd);

  ds.write(  rowtowrite.data(), H5::PredType::NATIVE_DOUBLE, toaddspace, origspace );

  //Can close toaddspace/origspace with no effect.
  //Can also close/open data set at the beginning of each time with no effect.
}

hsize_t getnrows( H5::DataSet& ds )
{
  H5::DataSpace origspace = ds.getSpace();
  int rank = origspace.getSimpleExtentNdims();
  hsize_t dims[rank];
  int ndims = origspace.getSimpleExtentDims( dims, NULL);
  hsize_t nrows=dims[0];
  hsize_t ncols=dims[1];

  return nrows;
}

std::vector<double> read1row( H5::DataSet& ds, const hsize_t& row )
{
  H5::DataSpace origspace = ds.getSpace();
  int rank = origspace.getSimpleExtentNdims();
  hsize_t dims[rank];
  int ndims = origspace.getSimpleExtentDims( dims, NULL);
  hsize_t nrows=dims[0];
  hsize_t ncols=dims[1];
  std::vector<double> returnvect( ncols );

  if(row >= nrows )
    {
      fprintf(stderr, "REV: ERROR, trying to read a row outside of matrix ([%lld] vs mat size [%lld])\n", row, nrows);
      exit(1);
    }

  hsize_t targrowoffset = row;
  hsize_t targcoloffset = 0;
  hsize_t dimsmem[rank] = {1,  ncols};
  H5::DataSpace memspace(rank, dimsmem);

  int id=memspace.getId();
  if(id==INT_MAX)
    {
      fprintf(stdout, "WOW, ID == INT_MAX, errortime?\n");
    }

  if( (row+1) % 100000 == 0 )
    {
      fprintf(stdout, "MEMSPACE ID [%d]\n", memspace.getId() );
    }

  hsize_t offset[rank] = { targrowoffset, targcoloffset };
  origspace.selectHyperslab( H5S_SELECT_SET, dimsmem, offset );
  ds.read( returnvect.data(), H5::PredType::NATIVE_DOUBLE, memspace, origspace );

  return returnvect;
}

void readallrows( H5::DataSet& ds )
{
  hsize_t nrows = getnrows( ds );
  //fprintf(stdout, "Read [%lld] rows\n", nrows );
  for(hsize_t r=0; r<nrows; ++r)
    {
      std::vector<double> gotvect = read1row( ds, r );
      if( gotvect[0] != (double)r )
    {
      fprintf(stderr, "ERROR IN READING...\n");
      exit(1);
    }
    }
  return;
}

std::vector<double> readlastrow( H5::DataSet& ds )
{
  H5::DataSpace origspace = ds.getSpace();
  int rank = origspace.getSimpleExtentNdims();
  hsize_t dims[rank];
  int ndims = origspace.getSimpleExtentDims( dims, NULL);
  hsize_t nrows=dims[0];
  hsize_t ncols=dims[1];
  std::vector<double> returnvect( ncols );



  hsize_t targrowoffset = nrows-1;
  hsize_t targcoloffset = 0;
  hsize_t dimsmem[rank] = {1,  ncols};
  H5::DataSpace memspace(rank, dimsmem);

  hsize_t offset[rank] = { targrowoffset, targcoloffset };
  origspace.selectHyperslab( H5S_SELECT_SET, dimsmem, offset );
  ds.read( returnvect.data(), H5::PredType::NATIVE_DOUBLE, memspace, origspace );

  return returnvect;
}


int fakereadlastrow( H5::DataSet& ds, const int& previd )
{
  H5::DataSpace origspace = ds.getSpace();
  int rank = origspace.getSimpleExtentNdims();
  hsize_t dims[rank];
  int ndims = origspace.getSimpleExtentDims( dims, NULL);
  hsize_t nrows=dims[0];
  hsize_t ncols=dims[1];
  std::vector<double> returnvect( ncols );



  hsize_t targrowoffset = nrows-1;
  hsize_t targcoloffset = 0;
  hsize_t dimsmem[rank] = {1,  ncols};
  H5::DataSpace memspace(rank, dimsmem);

  hsize_t offset[rank] = { targrowoffset, targcoloffset };
  origspace.selectHyperslab( H5S_SELECT_SET, dimsmem, offset );

  //REV: Would read here, but I don't for speed.
  //ds.read( returnvect.data(), H5::PredType::NATIVE_DOUBLE, memspace, origspace );
  int id =   memspace.getId();

  if(id % 1000000 == 0 )
    {
      fprintf(stdout, "PREV ID: [%d] now ID: [%d]\n", previd, id);
    }
  return id;
  //return returnvect;
}

int main()
{
  std::string fname = "testhdf5file.h5";
  H5::H5File f( fname, H5F_ACC_TRUNC );
  std::string dsetname = "dset1";

  const hsize_t nranks = 2;
  const hsize_t ncols = 20;
  const hsize_t nrows = 0; //start with zero rows.

  hsize_t  dims[nranks] = {nrows, ncols};
  hsize_t  max_dims[nranks] = {H5S_UNLIMITED, ncols};

  H5::DataSpace dataspace( nranks, dims, max_dims );
  H5::DSetCreatPropList prop; //could set properties, but whatever.

  const hsize_t nrows_chunk = 100; //Need to mess with CACHE size too!
  hsize_t  chunk_dims[nranks] = { nrows_chunk, ncols};
  prop.setChunk(nranks, chunk_dims);


  //Create the dataset 
  H5::DataSet ds = f.createDataSet( dsetname, H5::PredType::NATIVE_DOUBLE,
                    dataspace, prop);


  size_t nrowstoadd=1;

  for(size_t t=0; t<nrowstoadd; ++t)
    {
      std::vector<double> rowtowrite( ncols, (double)t );
      addrow( ds, rowtowrite );
    }

  int id=0;
  for(size_t t=0; t<10000000; ++t)
    {

      //readallrows( ds );

      //readlastrow();

      id = fakereadlastrow(ds, id);


      //f.flush(H5F_SCOPE_GLOBAL);
    }

  return 0;
}

person rveale    schedule 20.02.2016    source источник
comment
Обратите внимание на одно и то же поведение независимо от вызова memspace.close (), memspace. ~ H5 :: DataSpace () и т. Д. В конце каждого вызова fakereadlastrow ().   -  person rveale    schedule 20.02.2016
comment
Если я распечатаю каждый вызов, вы увидите, что идентификатор memspace увеличивается на 1 каждый раз, в то время как origspace остается неизменным (я получаю одно и то же пространство каждый раз, поэтому, похоже, он не создает новый идентификатор) PREV ID: [67113598] сейчас ID: [67113599] (origspace: [67108867]) PREV ID: [67113599] now ID: [67113600] (origspace: [67108867]) PREV ID: [67113600] now ID: [67113601] (origspace: [67108867]) ПРЕДЫДУЩИЙ ID: [67113601] теперь ID: [67113602] (исходное пространство: [67108867])   -  person rveale    schedule 20.02.2016


Ответы (1)


РЕДАКТИРОВАТЬ: Как упоминалось в правке на вопрос, это произошло из-за массивной утечки памяти в H5 :: DataSet :: getSpace (). Вот код обходного пути, который решает проблему (замените все вызовы dataset.getSpace () на getSpace (набор данных);

H5::DataSpace getSpace( H5::DataSet& ds )
{
  hid_t id2 = ds.getId();
  hid_t myspace = H5Dget_space(id2);
  H5::DataSpace origspace( myspace );
  H5Sclose( myspace );
  return origspace;
}
person rveale    schedule 20.02.2016