#c #gzip #iostream #protocol-buffers
#c #gzip #iostream #протокол-буферы
Вопрос:
после нескольких дней экспериментов с буферами протокола я попытался сжать файлы. С Python это довольно просто сделать и не требует никакой игры с потоками.
Поскольку большая часть нашего кода написана на C , я хотел бы сжимать / распаковывать файлы на том же языке. Я пробовал библиотеку boost gzip, но не смог заставить ее работать (без сжатия):
int writeEventCollection(HEP::MyProtoBufClass* protobuf, std::string filename, unsigned int compressionLevel) {
ofstream file(filename.c_str(), ios_base::out | ios_base::binary);
filtering_streambuf<output> out;
out.push(gzip_compressor(compressionLevel));
out.push(file);
if (!protobuf->SerializeToOstream(amp;file)) {//serialising to wrong stream I asume
cerr << "Failed to write ProtoBuf." << endl;
return -1;
}
return 0;
}
Я искал примеры использования GZIPOutputStream и GZIPInputStream с буферами протокола, но не смог найти рабочий
пример.
Как вы, наверное, уже заметили, я в лучшем случае новичок в потоках и был бы очень признателен за полностью рабочий пример, как в http://code.google.com/apis/protocolbuffers/docs/cpptutorial.html (У меня есть моя адресная книга, как мне сохранить ее в gziped-файле?)
Заранее благодарю.
РЕДАКТИРОВАТЬ: Рабочие примеры.
Пример 1 после ответа здесь на StackOverflow
int writeEventCollection(shared_ptr<HEP::EventCollection> eCollection,
std::string filename, unsigned int compressionLevel) {
filtering_ostream out;
out.push(gzip_compressor(compressionLevel));
out.push(file_sink(filename, ios_base::out | ios_base::binary));
if (!eCollection->SerializeToOstream(amp;out)) {
cerr << "Failed to write event collection." << endl;
return -1;
}
return 0;
}
Пример 2 следующий ответ в группе обсуждения Protobuf в Google:
int writeEventCollection2(shared_ptr<HEP::EventCollection>
eCollection, std::string filename,
unsigned int compressionLevel) {
using namespace google::protobuf::io;
int filedescriptor = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC,
S_IREAD | S_IWRITE);
if (filedescriptor == -1) {
throw "open failed on output file";
}
google::protobuf::io::FileOutputStream file_stream(filedescriptor);
GzipOutputStream::Options options;
options.format = GzipOutputStream::GZIP;
options.compression_level = compressionLevel;
google::protobuf::io::GzipOutputStream gzip_stream(amp;file_stream,
options);
if (!eCollection->SerializeToZeroCopyStream(amp;gzip_stream)) {
cerr << "Failed to write event collection." << endl;
return -1;
}
close(filedescriptor);
return 0;
}
Некоторые комментарии по производительности (чтение текущего формата и запись файлов ProtoBuf 11146):
Пример 1:
real 13m1.185s
user 11m18.500s
sys 0m13.430s
CPU usage: 65-70%
Size of test sample: 4.2 GB (uncompressed 7.7 GB, our current compressed format: 7.7 GB)
Пример 2:
real 12m37.061s
user 10m55.460s
sys 0m11.900s
CPU usage: 90-100%
Size of test sample: 3.9 GB
Похоже, что метод Google использует процессор более эффективно, работает немного быстрее (хотя я ожидаю, что это будет в пределах точности) и создает на ~ 7% меньший набор данных с той же настройкой сжатия.
Комментарии:
1. Обратите внимание, что основная проблема здесь не связана с буферами протокола — любой пример «write / chain gzip через поток» должен работать.
Ответ №1:
Ваше предположение верно: опубликованный вами код не работает, потому что вы пишете непосредственно в ofstream
, а не через filtering_streambuf
. Чтобы сделать эту работу, вы можете использовать filtering_ostream
вместо:
ofstream file(filename.c_str(), ios_base::out | ios_base::binary);
filtering_ostream out;
out.push(gzip_compressor(compressionLevel));
out.push(file);
if (!protobuf->SerializeToOstream(amp;out)) {
// ... etc.
}
Или более кратко, используя file_sink
:
filtering_ostream out;
out.push(gzip_compressor(compressionLevel));
out.push(file_sink(filename, ios_base::out | ios_base::binary));
if (!protobuf->SerializeToOstream(amp;out)) {
// ... etc.
}
Надеюсь, это поможет!