LINUX.ORG.RU

[C++][Хранение данных] свой бинарный формат vs sqlite vs что-то ещё

 ,


0

0

У меня есть некоторый класс. В них есть члены-переменные (не массивы) стандартных типов (int, double), в сумме где-то на 64 байта.

Имеется массив из (максимум) миллиона таких (как не сложно посчитать, в памяти он занимает около полусотни мегабайт). Надо уметь записывать этот массив в файл и считывать обратно. Без выборочного ввода и вывода. Скорость считывания особо не важна, но скорость записи точно важна.

Стоит ли делать свой бинарный формат ([int количество_элементов][поочерёдные члены первого элемента][поочерёдные члены первого элемента][...] + lzma-сжатие), или лучше использовать sqlite? Или что-нибудь ещё?

★★★★★

делал такое на sqlite(3млн записей) с выгрузкой в питон и потом вгрузкой обратно, всё пашет нормально, работает достаточно быстро.

true_admin ★★★★★
()
Ответ на: комментарий от anonymous

Насколько большие файлы можно mmapить? Ведь оно по идее будет в нерезиновом виртуальном адресном пространстве, хоть и не будет использовать физическую память. Или будет? Тогда идею в trash

vertexua ★★★★★
()
Ответ на: комментарий от anonymous

mmap точно не катит, не кроссплатформенно

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от iZEN

ну да, сериализация в бинарник рассматривается как один из вариантов... если буду делать, то вручную (boost'овское не катит) как это обычно делается?

class SomeClass {
    double a;
    int b;
    vector<WeirdClass*> somethingIDontNeedInFile;
    AnotherWeirdClass somethingElseIDontNeedInFile;
public:
    void WriteMeToFile(string* filename);
    ...
};

int main()
{
    vector<SomeClass> someArr;
    //Запись:
    //1. Заполняем этот массив миллионом элементов
    //2. Создаём файл
    //3. Для всех элементов массива запускаем WriteMeToFile(имяФайла).
    //Закрываем файл.
    //Считывание:
    //1. Открываем файл
    //2. Считываем с него всё и передаём на конструктор SomeClass, создавая по элементу массива
    //3. Закрываем файл.
}

Если в файле надо хранить разномастную информацию, например 3 массива разных классов, то просто надо делать так: количество элементов 1 массива, данные элементов 1 массива, количество элементов 2 массива, данные элементов 2 массива, количество элементов 3 массива, данные элементов 3 массива.

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от Obey-Kun

Х.з. Я не знаю как у вас на C++ делается сериализация.

iZEN ★★★★★
()
Ответ на: комментарий от vertexua

> Ведь оно по идее будет в нерезиновом виртуальном адресном пространстве

Если x86_64, то в резиновом.

Reset ★★★★★
()
Ответ на: комментарий от Obey-Kun

не знаю, оно без меня работает. Тыщ 20 независимых инсертов в секунду на селероне было. Проверить пять сек: for x in xrange(0,10**6): cursor.execute(«INSERT INTO test VALUES(?)», x)

(код не проверял)

true_admin ★★★★★
()
Ответ на: комментарий от Reset

Это очевидно. Просто вопрос в основном был, как это скажется на использовании физической памяти. Что будет если я в x64 сделаю mmap на файл размером в 200ГБ

vertexua ★★★★★
()
Ответ на: комментарий от vertexua

Сколько будет свободной столько и будет использовано. Если будешь идти по файлу линейно, то соответственно «окно» будет двигаться.

Reset ★★★★★
()
Ответ на: комментарий от Legioner

нет

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

также это нужно не для отката, а для собственно хранения поля, но это уже не особо важный аспект

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от Obey-Kun

Советую в начале файла сохранить размер массива, чтобы при чтении сразу сделать .reserve(size) плюс какая никакая проверка целостности файла.

функции сериализации лучше работать сразу с настроенным потоком, а не именем файла.

так что мыслишь в общем верно.

для пущей кросплатформенности не забыть конвертить все в биг-эндиан.

yatagarasu
()
Ответ на: комментарий от yatagarasu

для этого можно использовать берклей сокетные hton и ntoh

yatagarasu
()
Ответ на: комментарий от Obey-Kun

>Точно, для скорости лучше без него.

Для скорости - лучше с ним (например - LZO)

Led ★★★☆☆
()
Ответ на: комментарий от yatagarasu

> для пущей кросплатформенности не забыть конвертить все в биг-эндиан.

bigendian отмирает, поэтому конвертить надо в little-endian или вообще забить.

Reset ★★★★★
()

1. int меняешь на int32_t, int64_t
2. __attribute__((__packed__))
3. sizeof( your_class ) * count
4. fopen + fwrite
5. ???????
6. PROFIT!

так же и читать назад, главное не забудь в начале файла указать версию формата, чтоб если что вносить изменения, а также продумать хорошо структуру класса и набор его полей, чтоб менять формат как можно реже

lester ★★★★
()
Ответ на: комментарий от yatagarasu

network order не имеет отношения к протоколам, построенным поверх TCP, поэтому тут я могу какой угодно order использовать, хоть middle endian, а ситуация сейчас такова, что самый удобный это little endian.

Reset ★★★★★
()
Ответ на: комментарий от lester

поддержу, только надо #pragma pack использовать (gcc поддерживает!), чтоб на венде без ifdef'ов собирать.

Reset ★★★★★
()
Ответ на: комментарий от Reset

> network order не имеет отношения к протоколам, построенным поверх TCP, поэтому тут я могу какой угодно order использовать, хоть middle endian, а ситуация сейчас такова, что самый удобный это little endian.

охо. какая каша в голове. есть host order а есть network order и есть функции преобразования одного в другое. а тебе удобней все руками написать. ну-ну.

yatagarasu
()
Ответ на: комментарий от yatagarasu

Немного другие (нежели htonl/ntohl) макросы преобразования можно и самому написать.

Если у меня endianness протокола будет = host byteorder, то преобразования вообще не будет. А учитывая, что host byteorder сейчас в 99% случаев это little endian, то я вообще не вижу смысла менять порядок байт перед отправкой в сеть. Причины смены порядка байт на big endian (=network byteorder) перед отправкой сейчас носят исключительно религиозный характер.

Reset ★★★★★
()
Ответ на: комментарий от lester

> забыл сказать, что это для классов без наследования

у меня классы без наследования, т.к. их могла бы объединить только одна функция (и то с натяжкой), но я решил избегать virtual функций для выигрыша в скорости

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от lester

> sizeof( your_class ) * count

Мне класс целиком не надо записывать... только его часть — они у меня с избыточностью в некоторых аспектах сделаны для учеличения скорости вычислений. Как поступить? Видится два варианта: или сделать структуру, которая содержит только то, что нужно к записи и записывать её; или же писать без посредников просто то, что нужно. Вероятно, второе в скорости выигрышнее, я так и хотел сделать.

__attribute__((__packed__))

Если идти вторым путём (писать просто нужные члены классов), то оно нафиг не нужно. Да и кроссплатформенность пострадает. Хотя да, #pragma pack тут поможет, но опять же, нафиг надо, если переменные поочередно в заданном мной порядке писать.

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от shty

и да

The HDF5 technology suite includes:

A versatile data model that can represent very complex data objects and a wide variety of metadata.

A completely portable file format with no limit on the number or size of data objects in the collection.

A software library that runs on a range of computational platforms, from laptops to massively parallel systems, and implements a high-level API with C, C++, Fortran 90, and Java interfaces.

A rich set of integrated performance features that allow for access time and storage space optimizations.

Tools and applications for managing, manipulating, viewing, and analyzing the data in the collection.

и всё опенсорсно, бесплатно и кроссплатформенно :)

юзал сам, рекомендую

shty ★★★★★
()
Ответ на: комментарий от Obey-Kun

> Мне класс целиком не надо записывать... только его часть

выносишь необходимые данные в отдельный массив, в классе хранишь указатель на элемент этого массива, тогда можно и наследование испольовать с виртуальными методами

переменные поочередно в заданном мной порядке писать.


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

lester ★★★★
()
Ответ на: комментарий от Obey-Kun

если будешь пробовать несколько методов - выложи плз тут результаты по времени( только делай несколько замеров для каждого способа ), будет интересно увидеть разницу

lester ★★★★
()
Ответ на: комментарий от Reset

>А учитывая, что host byteorder сейчас в 99% случаев это little endian,

А я прошлым летом поймал проблемы с таким допущением. На IBM Pкакой-то пытался прогу запускать.

DonkeyHot ★★★★★
()
Ответ на: комментарий от lester

> тогда можно и наследование испольовать с виртуальными методами

я же говорю: я избегаю виртуальных методов ради выигрыша в производительности

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от lester

> ничего быстрее выгрузки области памяти нет - и быть не может

а файл разве останется переносимым с платформы на платформы?

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от Obey-Kun

> я же говорю: я избегаю виртуальных методов ради выигрыша в производительности

я тебя заставляю что-ли? :) просто указал техническую деталь

lester ★★★★
()
Ответ на: комментарий от Obey-Kun

> а файл разве останется переносимым с платформы на платформы?

с линукса на виндовс, например, для интеловской архитектуры - да

lester ★★★★
()
Ответ на: комментарий от lester

опять же - если сохранять надо часто, а переносить редко - пишешь отдельно импорт/экспорт в популярные форматы

lester ★★★★
()

Есть до фига библиотек для поддержки туплов (тот же Root, например). Именно это тебе и нужно.

anonymous
()
Ответ на: комментарий от lester

Кстати, если интересно, сравнивал скорость работы классов, созданных разными методами.

obey@damnbook virttest % g++ ./virtual.cpp  
obey@damnbook virttest % time ./a.out; time ./a.out; time ./a.out; time ./a.out; time ./a.out; time ./a.out
./a.out  8,42s user 0,66s system 98% cpu 9,209 total                                                       
./a.out  8,45s user 0,65s system 99% cpu 9,181 total                                                       
./a.out  8,41s user 0,67s system 98% cpu 9,179 total                                                       
./a.out  8,42s user 0,63s system 98% cpu 9,191 total                                                       
./a.out  8,35s user 0,75s system 94% cpu 9,645 total                                                       
./a.out  8,47s user 0,64s system 97% cpu 9,349 total                                                       

obey@damnbook virttest % g++ ./classes2.cpp
obey@damnbook virttest % time ./a.out; time ./a.out; time ./a.out; time ./a.out; time ./a.out; time ./a.out
./a.out  7,85s user 0,32s system 99% cpu 8,245 total                                                       
./a.out  7,83s user 0,33s system 96% cpu 8,429 total                                                       
./a.out  7,94s user 0,34s system 95% cpu 8,632 total                                                       
./a.out  7,77s user 0,40s system 98% cpu 8,260 total
./a.out  7,87s user 0,27s system 98% cpu 8,245 total
./a.out  7,82s user 0,38s system 96% cpu 8,484 total

obey@damnbook virttest % g++ ./classes.cpp
obey@damnbook virttest % time ./a.out; time ./a.out; time ./a.out; time ./a.out; time ./a.out; time ./a.out
./a.out  8,21s user 0,34s system 98% cpu 8,685 total
./a.out  7,65s user 0,36s system 99% cpu 8,084 total
./a.out  7,70s user 0,32s system 99% cpu 8,081 total
./a.out  7,70s user 0,29s system 98% cpu 8,098 total
./a.out  7,68s user 0,34s system 99% cpu 8,083 total
./a.out  7,74s user 0,29s system 95% cpu 8,388 total

obey@damnbook virttest % g++ ./normal.cpp
obey@damnbook virttest % time ./a.out; time ./a.out; time ./a.out; time ./a.out; time ./a.out; time ./a.out
./a.out  7,15s user 0,35s system 98% cpu 7,592 total
./a.out  7,23s user 0,29s system 98% cpu 7,652 total
./a.out  7,15s user 0,34s system 98% cpu 7,575 total
./a.out  7,14s user 0,33s system 98% cpu 7,547 total
./a.out  7,15s user 0,32s system 98% cpu 7,557 total
./a.out  7,11s user 0,34s system 98% cpu 7,565 total

virtual.cpp

#include <cmath>                           
#include <vector>                          
using namespace std;                       

class MyVirt {
  double myResult;
public:           
  virtual void doStuff(int i) {}
  void SaveResult(double inResult) { myResult = inResult; }
};                                                         

class Class1: public MyVirt {
public:                      
  void doStuff (int i) { SaveResult(sin(i)*sin(i)+2*cos(i)); }
};                                                            

class Class2: public MyVirt {
public:
  void doStuff (int i) { SaveResult(cos(i)*cos(i)+2*sin(i)); }
};

int main()
{
  vector<MyVirt> arr1;
  int numCycles=10000000;
  for (int i=0; i<numCycles; ++i) {
    Class1 a;
    a.doStuff(i);
    arr1.push_back(a);
  }
  for (int i=0; i<numCycles; ++i) {
    Class2 a;
    a.doStuff(i);
    arr1.push_back(a);
  }
}

classes2.cpp:

#include <cmath>                            
#include <vector>                           
using namespace std;                        

class Class1 {
  double myResult;
public:           
  void SaveResult(double inResult) { myResult=inResult; }
  void DoStuff (int i) { SaveResult(sin(i)*sin(i)+2*cos(i)); }
};                                                            

class Class2 {
  double myResult;
public:
  void SaveResult(double inResult) { myResult=inResult; }
  void DoStuff (int i) { SaveResult(cos(i)*cos(i)+2*sin(i)); }
};

int main()
{
  vector<Class1> arr1;
  vector<Class2> arr2;
  int numCycles=10000000;
  for (int i=0; i<numCycles; ++i) {
    Class1 a;
    a.DoStuff(i);
    arr1.push_back(a);
  }
  for (int i=0; i<numCycles; ++i) {
    Class2 a;
    a.DoStuff(i);
    arr2.push_back(a);
  }
}

classes.cpp:

#include <cmath>                           
#include <vector>                          
using namespace std;                       

class Class1 {
  double myResult;
public:           
  void doStuff (int i) { myResult=sin(i)*sin(i)+2*cos(i); }
};                                                         

class Class2 {
  double myResult;
public:
  void doStuff (int i) { myResult=cos(i)*cos(i)+2*sin(i); }
};

int main()
{
  vector<Class1> arr1;
  vector<Class2> arr2;
  int numCycles=10000000;
  for (int i=0; i<numCycles; ++i) {
    Class1 a;
    a.doStuff(i);
    arr1.push_back(a);
  }
  for (int i=0; i<numCycles; ++i) {
    Class2 a;
    a.doStuff(i);
    arr2.push_back(a);
  }
}

normal.cpp:

#include <cmath>
#include <vector>
using namespace std;

int main()
{
  vector<double> arr1;
  int numCycles=10000000;
  for (int i=0; i<numCycles; ++i) {
    double a;
    a=sin(i)*sin(i)+2*cos(i);
    arr1.push_back(a);
  }
  for (int i=0; i<numCycles; ++i) {
    double a;
    a=cos(i)*cos(i)+2*sin(i);
    arr1.push_back(a);
  }
}

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от lester

> опять же - если сохранять надо часто, а переносить редко - пишешь отдельно импорт/экспорт в популярные форматы

фублин, как-то я не подумал

для возможности отката во времени мне не нужна переносимость, ибо откат во времени всегда производится на той машине, где собственно считается модель... так что тут можно даже __attribute__((__packed__))/#pragma pack не использовать, а сохранять память как она есть

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

Obey-Kun ★★★★★
() автор топика
Ответ на: protobuf от ethercrow

Google uses Protocol Buffers for almost all of its internal RPC protocols and file formats.

Интересная штука. Я так понял, мне надо будет написать .proto с обрезками имеющихся классов (только то, что нужно сохранять), скомпилить это с помощью protoc, затем подключить хедеры и унаследовать, добавив функции и некоторые переменные.

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от elipse

> стоит глянуть и на metakit , это как одна из основ целой технологии для кit-пакетов tcl

выглядит мёртвой, с 2007 года нет обновлений

Obey-Kun ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.