Программе на C ++ не хватает памяти для больших данных - PullRequest
0 голосов
/ 14 июля 2011

Я пытаюсь решить проблему в написанной мною программе C ++.У меня практически не хватает памяти.Программа представляет собой кеш-симулятор.Существует файл, в котором предварительно собраны адреса памяти, например:

Тип адреса потока Размер Указатель инструкции
0 0x7fff60000000 1 8 0x7f058c482af3

Таких записей может быть 100-500 миллиардов.Сначала я пытаюсь прочитать все эти записи и сохранить их в векторе.Также во время чтения я создаю набор этих адресов (используя карту) и сохраняю порядковые номера определенного адреса.Порядковый номер просто означает позицию записи адреса в файле (один адрес можно увидеть несколько раз).При больших входах программа завершается с ошибкой при ошибке bad_alloc около 30-миллионной записи.Я предполагаю, что у меня заканчивается память.Посоветуйте пожалуйста как можно обойти проблему.Есть ли альтернативный способ обработки такого рода больших данных.Большое спасибо!Простите за длинный пост.Я хотел дать некоторый контекст и фактический код, который я пишу.

Ниже приведен соответствующий код.ParseTaceFile () читает каждую строку и вызывает StoreTokens (), который получает адрес и размер, и вызывает AddAddress (), который фактически сохраняет адрес в векторе и на карте.Объявление класса также приведено ниже.Первый блок try в AddAddress () фактически генерирует исключение bad_alloc.

void AddressList::ParseTraceFile(const char* filename) {
  std::ifstream in_file;
  std::cerr << "Reading Address Trace File..." << std::endl;
  in_file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
  char *contents = NULL;
  try {
    in_file.open(filename, std::ifstream::in | std::ifstream::binary);
    in_file.seekg(0, std::ifstream::end);
    std::streampos length(in_file.tellg());
    if (length < 0) {
      std::cerr << "Can not read input file length" << std::endl;
      throw ExitException(1);
    }
    contents = (new char[length]);
    in_file.seekg(0, std::ifstream::beg);
    in_file.read(contents, length);
    in_file.close();
    uint64_t linecount = 0, i = 0, lastline = 0, startline = 0;
    while (i < static_cast<uint64_t>(length)) {
      if ((contents[i] == '\n') or (contents[i] == EOF)) {
        contents[i] = '\0';
        lastline = startline;
        startline = i + 1;
        ++linecount;
        if (linecount > 1) {
          StoreTokens((contents + lastline), &linecount);
        }
      }
      ++i;
    }
  } catch (std::bad_alloc& e) {
    delete [] contents;
    std::cerr << "error allocating memory while parsing" << std::endl;
    throw;
  } catch (std::ifstream::failure &exc1) {
    if (!in_file.eof()) {
      delete[] contents;
      std::cerr << "error in reading address trace file" << exc1.what()
          << std::endl;
      throw ExitException(1);
    }
  }
  std::cerr << "Done" << std::endl;
}
//=========================================================    
void AddressList::StoreTokens(char* line, uint64_t * const linecount) {
  uint64_t address, size;
  char *token = strtok(line, " \t");
  uint8_t tokencount = 0;
  while (NULL != token) {
    ++tokencount;
    switch (tokencount) {
    case 1:
      break;
    case 2:
      address = strtoul(token, NULL, 16);
      break;
    case 3:
      break;
    case 4:
      size = strtoul(token, NULL, 0);
      break;
    case 5:
      break;
    default:
      break;
    }
    token = strtok(NULL, " \t");
  }
  AddAddress(address, size);
}
//================================================================
void AddressList::AddAddress(const uint64_t& byteaddr, const uint64_t& size) {

  //allocate memory for the address vector
  try {
    if ((sequence_no_ % kReserveCount) == 0) address_list_.reserve(kReserveCount);

  } catch (std::bad_alloc& e) {
    std::cerr
        << "error allocating memory for address trace vector, address count"
        << sequence_no_ << std::endl;
    throw;
  }
  uint64_t offset = byteaddr & (CacheParam::Instance()->LineSize() - 1);
  //lineaddress = byteaddr >> CacheParam::Instance()->BitsForLine();
  // this try block is for allocating memory for the address set and the queue it holds
  try {
    // splitter
    uint64_t templinesize = 0;
    do {
      Address temp_addr(byteaddr + templinesize);
      address_list_.push_back(temp_addr);
      address_set_[temp_addr.LineAddress()].push(sequence_no_++);
      templinesize = templinesize + CacheParam::Instance()->LineSize();
    } while (size + offset > templinesize);
  } catch (std::bad_alloc& e) {
    address_list_.pop_back();
    std::cerr
    << "error allocating memory for address trace set, address count"
    << sequence_no_ << std::endl;
    throw;
  }
 }

//======================================================
typedef std::queue<uint64_t> TimeStampQueue;
typedef std::map<uint64_t, TimeStampQueue> AddressSet;
class AddressList {
public:
  AddressList(const char* tracefilename);
  bool Simulate(uint64_t *hit_count, uint64_t* miss_count);
  ~AddressList();

private:
  void AddAddress(const uint64_t& byteaddr, const uint64_t& size);
  void ParseTraceFile(const char* filename);
  void StoreTokens(char* line, uint64_t * const linecount);

  std::vector<Address> address_list_;
  AddressSet address_set_;
  uint64_t sequence_no_;
  CacheMemory cache_;

  AddressList (const AddressList&);
  AddressList& operator=(const AddressList&);
};

Вывод выглядит так:

Файл конфигурации чтения кэша ...

Параметры кэшачитать ...

чтение файла трассировки адресов ...

ошибка выделения памяти для набора трассировки адресов, счетчик адресов 30000000

ошибка выделения памяти при разборе

Ответы [ 3 ]

4 голосов
/ 14 июля 2011

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

1 голос
/ 14 июля 2011

Карта сортирует свои входные данные во время заполнения, чтобы оптимизировать время поиска и обеспечить отсортированный вывод. Похоже, вы не используете функцию поиска, поэтому оптимальной стратегией является сортировка списка другим методом. Сортировка слиянием отлично подходит для сортировки коллекций, которые не помещаются в память. Даже если вы выполняете поиск, двоичный поиск в отсортированном файле будет быстрее, чем простой подход, если каждая запись имеет фиксированный размер.

0 голосов
/ 14 июля 2011

Простите за то, что я могу сказать, что может быть очевидным, но необходимость эффективного хранения и запроса больших объемов данных - это точная причина, по которой были изобретены базы данных. Они уже решили все эти проблемы лучше, чем вы или я могли бы придумать за разумное время. Не нужно изобретать велосипед.

...