Программа для конвертации файлов однотипных записей
От: mezhaka  
Дата: 10.11.10 19:19
Оценка:
Был бы рад, если бы кто-нибудь покритиковал эту маленькую программку.
Нужно было сконвертировать большой файл состоящий из double в файл состоящий из float. По идее эту програмку можно использовать для конвертации любых простых POD струкур. Первая версия этой программы открывала поток, читала по одному значению и сразу его писала в другой поток. Конвертация трёхгигового файла занимала:

$ time double2float tmp.double tmp.float

real    1m24.239s
user    1m9.987s
sys     0m8.628s

Для сравнения, копирование файла занимает столько:

$ time cp input.vel.double tmp.double

real 0m13.322s
user 0m0.521s
sys 0m10.723s[/code]

Эта версия с буффером отрабатывает заметно быстрее:
[code]
$ time double2float tmp.double tmp.float

real    0m9.680s
user    0m1.511s
sys     0m7.625s


Вот код:
#include <algorithm>
#include <cassert>
#include <fstream>
#include <functional>
#include <iostream>
#include <string>
#include <vector>

template<typename UnaryFunction>
void buffered_binary_stream_transform(std::ifstream& input, std::ofstream& output, UnaryFunction op, size_t buffer_size_bytes)
{
    typedef typename UnaryFunction::argument_type argument_type;
    typedef typename UnaryFunction::result_type result_type;

    const size_t argument_type_size = sizeof(argument_type);
    assert(buffer_size_bytes >= argument_type_size);

    const size_t result_type_size = sizeof(result_type);
    const typename std::vector<argument_type>::size_type buffer_size_elements = buffer_size_bytes / argument_type_size; //might truncate actual buffer_size_bytes 
    const int bytes_to_read = argument_type_size * buffer_size_elements;
    std::vector<argument_type> elements_to_transform(buffer_size_elements);
    std::vector<result_type> transformed_elements(buffer_size_elements);
  
    while(true) {
        input.read(reinterpret_cast<char*>(&elements_to_transform[0]), bytes_to_read);
        const std::streamsize bytes_read = input.gcount();
        if (bytes_read == 0) break;

        const std::streamsize elements_read = bytes_read / argument_type_size;
        const std::streamsize bytes_to_write = result_type_size * elements_read;
        std::transform(elements_to_transform.begin(),
                       elements_to_transform.begin() + elements_read,
                       transformed_elements.begin(),
                       op);
        output.write(reinterpret_cast<const char*>(&transformed_elements[0]), bytes_to_write);
    }   
}

struct Double2FloatConverter : public std::unary_function<double, float>
{
    result_type operator() (const argument_type& to_convert)
    {
        return static_cast<result_type>(to_convert);
    }
};

int main (int args, char** argv) 
{
    if (! (args == 4 || args == 3 ) ) 
    {
        std::cerr << "Wrong number of args" << std::endl << "usage: " << argv[0] 
             << " input_file output_file" << std::endl;
        exit(EXIT_FAILURE);
    }

    std::string input_file_name(argv[1]);
    std::ifstream input(input_file_name.c_str(), std::ios::binary);
    if(!input) 
    { 
        std::cerr << "Can't open input file \"" << input_file_name << "\"" << std::endl;
        exit(EXIT_FAILURE);
    }

    std::string outputFileName(argv[2]);
    if (outputFileName == input_file_name)
    {
        std::cerr << "Can't write to the same file I am reading from\n";
        exit(EXIT_FAILURE);
    }
    std::ofstream output(outputFileName.c_str(), std::ios::binary);
    if(!output) 
    {
        std::cerr << "Can't open output file \"" << input_file_name.c_str() << "\"" << std::endl;
        exit(EXIT_FAILURE);
    }

    const int buffer_size = 4*1024*1024;

    buffered_binary_stream_transform(input, output, Double2FloatConverter(), buffer_size);
}


Есть ли смысл передавать элементарные типы данных по константной ссылке, как это делается в Double2FloatConverter::operator() ?

Функтор наследует от unary_function и этого типа требует шаблон и я не знаю хорошо это или плохо. Выходит, что с этим требованием в buffered_binary_stream_transform нельзя использовать функтор, который не наследует от unary_function.
Re: Программа для конвертации файлов однотипных записей
От: jazzer Россия Skype: enerjazzer
Дата: 11.11.10 05:09
Оценка: 1 (1) +1
Здравствуйте, mezhaka, Вы писали:

M>Был бы рад, если бы кто-нибудь покритиковал эту маленькую программку.

M>Нужно было сконвертировать большой файл состоящий из double в файл состоящий из float.
Я бы просто смапил оба файла в память, благо размер входа и выхода известен, и написал бы алгоритм целиком в терминах памяти, причем каким-нть tbb::parallel_for, чтоб она в несколько потоков это делала по непересекающимся страницам памяти.
А работу с буферизацией файлов оставил бы целиком ядру.
Программа бы на пять строчек получилась
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[2]: Программа для конвертации файлов однотипных записей
От: Аноним  
Дата: 11.11.10 10:11
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Здравствуйте, mezhaka, Вы писали:


M>>Был бы рад, если бы кто-нибудь покритиковал эту маленькую программку.

M>>Нужно было сконвертировать большой файл состоящий из double в файл состоящий из float.
J>Я бы просто смапил оба файла в память, благо размер входа и выхода известен, и написал бы алгоритм целиком в терминах памяти, причем каким-нть tbb::parallel_for, чтоб она в несколько потоков это делала по непересекающимся страницам памяти.
J>А работу с буферизацией файлов оставил бы целиком ядру.
J>Программа бы на пять строчек получилась

Класс, спасибо за совет.
А может что-нибудь порекоммендуете для memory maped файлов (в смысле библиотеку какую-то)? И какой они дают интерфейс потом? Т.е. будет ли это например как будто у меня два больших буфера или это будет какой-то поток из которого надо будет читать?

Работает ли tbb без интеловского компилятора?
с++ io
Re[3]: Программа для конвертации файлов однотипных записей
От: Viper_Craft  
Дата: 11.11.10 10:26
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, jazzer, Вы писали:


J>>Здравствуйте, mezhaka, Вы писали:


M>>>Был бы рад, если бы кто-нибудь покритиковал эту маленькую программку.

M>>>Нужно было сконвертировать большой файл состоящий из double в файл состоящий из float.
J>>Я бы просто смапил оба файла в память, благо размер входа и выхода известен, и написал бы алгоритм целиком в терминах памяти, причем каким-нть tbb::parallel_for, чтоб она в несколько потоков это делала по непересекающимся страницам памяти.
J>>А работу с буферизацией файлов оставил бы целиком ядру.
J>>Программа бы на пять строчек получилась

А>Класс, спасибо за совет.

А>А может что-нибудь порекоммендуете для memory maped файлов (в смысле библиотеку какую-то)? И какой они дают интерфейс потом? Т.е. будет ли это например как будто у меня два больших буфера или это будет какой-то поток из которого надо будет читать?

А>Работает ли tbb без интеловского компилятора?


есть еще OpenMP, работает на VC, gcc
Re[3]: Программа для конвертации файлов однотипных записей
От: Sergey Chadov Россия  
Дата: 11.11.10 10:48
Оценка:
Здравствуйте, Аноним, Вы писали:


А>Работает ли tbb без интеловского компилятора?


Да.
Re[3]: Программа для конвертации файлов однотипных записей
От: jazzer Россия Skype: enerjazzer
Дата: 11.11.10 13:32
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, jazzer, Вы писали:


А>Класс, спасибо за совет.

А>А может что-нибудь порекоммендуете для memory maped файлов (в смысле библиотеку какую-то)?
man mmap
http://opennet.ru/man.shtml?topic=mmap&amp;category=2&amp;russian=0

А>И какой они дают интерфейс потом?

А>Т.е. будет ли это например как будто у меня два больших буфера или это будет какой-то поток из которого надо будет читать?
Просто два больших буфера: каждый mmap вернет указатель, используй один как double* для чтения, а другой — как float* для записи:
const size_t n = ...;
double* pd = (double*)mmap( ..., n*sizeof(double), ... );
float* pf = (float*)mmap( ..., n*sizeof(double), ... );
for (size_t i = 0; i<n; i++)
  pf[i] = pd[i];
munmap( pd, n*sizeof(double) );
munmap( pf, n*sizeof(float) );

Это весь код, если делать без tbb

А>Работает ли tbb без интеловского компилятора?

да
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[4]: Программа для конвертации файлов однотипных записей
От: Serg27  
Дата: 12.11.10 08:22
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Просто два больших буфера: каждый mmap вернет указатель, используй один как double* для чтения, а другой — как float* для записи:

J>
J>const size_t n = ...;
J>double* pd = (double*)mmap( ..., n*sizeof(double), ... );
J>float* pf = (float*)mmap( ..., n*sizeof(double), ... );
J>for (size_t i = 0; i<n; i++)
J>  pf[i] = pd[i];
J>munmap( pd, n*sizeof(double) );
J>munmap( pf, n*sizeof(float) );
J>

J>Это весь код, если делать без tbb

Этот код не будет работать на 32 разрядных системах, если размер файла будет гигабайтным. Причина — исчерпание виртуальной памяти процесса. В это случае придется мапировать файл кусками. Т.е. код несколько усложнится.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.