[win] запись в файл в фоновом потоке
От: Abyx Россия  
Дата: 30.04.11 10:54
Оценка:
Надо ускорить запись 200-300Мб данных в файл, для этого подойдет такое решение:
— в основном потоке данные будут быстро добавляться в очередь, во вспомогательном — медленно писаться на диск,
когда данные заканчиваются, основной поток информирует об этом вспомогательный, но не ждет конца записи.

Есть ли для этого какие-то готовые библиотеки\функции WinAPI ?
In Zen We Trust
Re: [win] запись в файл в фоновом потоке
От: uzhas Ниоткуда  
Дата: 30.04.11 10:56
Оценка: 1 (1)
Здравствуйте, Abyx, Вы писали:

A>Есть ли для этого какие-то готовые библиотеки\функции WinAPI ?

http://msdn.microsoft.com/en-us/library/aa365683(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/aa365198(v=vs.85).aspx
Re[2]: [win] запись в файл в фоновом потоке
От: Abyx Россия  
Дата: 30.04.11 11:07
Оценка:
Здравствуйте, uzhas, Вы писали:

A>>Есть ли для этого какие-то готовые библиотеки\функции WinAPI ?

U>http://msdn.microsoft.com/en-us/library/aa365683(v=vs.85).aspx
U>http://msdn.microsoft.com/en-us/library/aa365198(v=vs.85).aspx

да, я это читал, только как это применить к моей проблеме?
In Zen We Trust
Re: [win] запись в файл в фоновом потоке
От: Temnikov Россия  
Дата: 30.04.11 11:28
Оценка:
Здравствуйте, Abyx, Вы писали:

A>Надо ускорить запись 200-300Мб данных в файл, для этого подойдет такое решение:

A>- в основном потоке данные будут быстро добавляться в очередь, во вспомогательном — медленно писаться на диск,
A>когда данные заканчиваются, основной поток информирует об этом вспомогательный, но не ждет конца записи.

A>Есть ли для этого какие-то готовые библиотеки\функции WinAPI ?



// шаблонный класс реализующий очередь в паралельном потоке
template <typename T> 
class CObjectQueueImpl
{
public:
    CObjectQueueImpl() : m_qFrames()
    {
        DWORD dwThreadID;
        m_hShutdownEvent = ::CreateEvent(NULL, false, false, NULL);
        m_hPutEvent = ::CreateEvent(NULL, false, false, NULL);
        m_hThread = ::CreateThread(NULL, 0, ThrdProc, this, 0, &dwThreadID);
    }
    ~CObjectQueueImpl()
    {
        if(m_hShutdownEvent)
            SetEvent(m_hShutdownEvent);
        WaitForSingleObject(m_hThread, INFINITE);
        CloseHandle(m_hThread);
    }
    bool ExtractFromQueue(T& Obj)
    {    
        if(m_qFrames.empty())
            return false;
        m_cs.Lock();
        Obj = m_qFrames.front();
        m_qFrames.pop();
        m_cs.Unlock();
        return true;
    }

    bool PutToQueue(const T* pObj)
    {
        if(!m_hPutEvent)
            return false;
        m_cs.Lock();
        m_qFrames.push(*pObj);
        m_cs.Unlock();
        SetEvent(m_hPutEvent);
        return true;
    }
    virtual void OnQueueNewObject()=0;
private:
    void WorkProc() throw()
    {
        ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
        HANDLE Handles[] = {m_hShutdownEvent, m_hPutEvent};
        DWORD dwCount = sizeof(Handles)/sizeof(Handles[0]);
        while (::WaitForMultipleObjects(dwCount, Handles, FALSE, INFINITE) != WAIT_OBJECT_0)
        {
            ResetEvent(m_hPutEvent);
            OnQueueNewObject();
        }
        ::CloseHandle(m_hShutdownEvent);
        ::CloseHandle(m_hPutEvent);
        m_hShutdownEvent = m_hPutEvent = NULL;
        ::CoUninitialize();
    }

    static DWORD WINAPI ThrdProc(void* pv) throw()
    {
        CObjectQueueImpl<T>* p = static_cast<CObjectQueueImpl<T>*>(pv);
        p->WorkProc();
        return 0;
    }

    std::queue<T> m_qFrames;
    CComAutoCriticalSection m_cs;
    HANDLE m_hPutEvent;
    HANDLE m_hShutdownEvent;
    HANDLE m_hThread;
};


Наследуем класс там где необходимо иметь параллельный поток с обработкой:
public CObjectQueueImpl<XXX>, где ХХХ — тип данных который будет передаваться в поток.
Переопределяем в классе метод OnQueueNewObject — вызывается из параллельного потока когда в него поступает новый объект.

В методе OnQueueNewObject делаем:

void CLASSNAME::OnQueueNewObject()
{
    XXX Param;
    while(ExtractFromQueue(Param))
    {
           DO SOMETHONG
    }
}


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

Есть недостатки у данного класса, но допиливать уже нет необходимости в тех задачах где он используется у меня.
Re[2]: [win] использование
От: Temnikov Россия  
Дата: 30.04.11 11:31
Оценка:
Забыл добавить. Чтобы передать что то в параллельный поток необходимо вызвать метод PutToQueue.

XXX Param;
PutToQueue(&Param);
Re: upd: [win] запись в файл в фоновом потоке
От: Abyx Россия  
Дата: 30.04.11 11:46
Оценка:
Здравствуйте, Abyx, Вы писали:

A>Есть ли для этого какие-то готовые библиотеки\функции WinAPI ?


тег [win] в названии темы означает только что меня устроит платформозависимое решение,
но не означает что решение обязательно должно быть на голом WinAPI.

кроме того, в этой задаче мне важнее всего быстродействие добавления данных в основном потоке,
и у меня есть подозрения что любые решения использующие WinAPI будут проигрывать кроссплатформенным решениям не взаимодействующим с ОС.
In Zen We Trust
Re[2]: [win] запись в файл в фоновом потоке
От: Abyx Россия  
Дата: 30.04.11 11:50
Оценка:
Здравствуйте, Temnikov, Вы писали:
T> m_cs.Lock();
...
T> m_cs.Unlock();
T> SetEvent(m_hPutEvent);

есть подозрение что захват критической секции и установка события будут создавать огромный оверхед на добавление данных в очередь.
In Zen We Trust
Re[3]: [win] запись в файл в фоновом потоке
От: Temnikov Россия  
Дата: 30.04.11 11:58
Оценка:
A>Здравствуйте, Temnikov, Вы писали:
T>> m_cs.Lock();
A>...
T>> m_cs.Unlock();
T>> SetEvent(m_hPutEvent);

A>есть подозрение что захват критической секции и установка события будут создавать огромный оверхед на добавление данных в очередь.


Прямо таки огромный? Используйте lock-free контейнер, останется оверхед только от вызова события. Хотя можно и от него отказаться, если данные будут доставаться из очереди, постоянно в вечном цикле, только с проверкой завершения потока.
Вы ни слова не сказали какие требования предъявляются.
Re[2]: upd: [win] запись в файл в фоновом потоке
От: Аноним  
Дата: 30.04.11 12:12
Оценка:
A> кроме того, в этой задаче мне важнее всего быстродействие добавления данных в основном потоке,

возьмите наивный локфри стек отсюда http://en.wikipedia.org/wiki/ABA_problem. Для одного производителя и одного потребителя АБА не проявится, кода строк 15 там. Тормозов будет минимум.

Но вообще вместо фразы 'есть подозрения' обычно лучше написать бенчмарк.
Re[2]: upd: [win] запись в файл в фоновом потоке
От: ononim  
Дата: 30.04.11 12:15
Оценка:
A>кроме того, в этой задаче мне важнее всего быстродействие добавления данных в основном потоке,
A>и у меня есть подозрения что любые решения использующие WinAPI будут проигрывать кроссплатформенным решениям не взаимодействующим с ОС.
а вот здесь ваша логика в корне ошибочна
Как много веселых ребят, и все делают велосипед...
Re[3]: upd: [win] запись в файл в фоновом потоке
От: Аноним  
Дата: 30.04.11 12:20
Оценка:
Здравствуйте, ononim, Вы писали:

A>>кроме того, в этой задаче мне важнее всего быстродействие добавления данных в основном потоке,

A>>и у меня есть подозрения что любые решения использующие WinAPI будут проигрывать кроссплатформенным решениям не взаимодействующим с ОС.
O>а вот здесь ваша логика в корне ошибочна

нет там логики, есть подозрения
Re: [win] запись в файл в фоновом потоке
От: okman Беларусь https://searchinform.ru/
Дата: 30.04.11 12:52
Оценка:
Здравствуйте, Abyx.

А Вы точно ускоряете там где надо ?
Скорость записи на диск все равно постоянна, и не зависит от разделения логики
программы на сигнальный и пишущий потоки, ну разве что время отклика уменьшается.

Если запись в файл ведется интенсивно и маленькими порциями, то тут лучше
поработать над файловым кэшем, чтобы накапливать записи, а затем время от время
сбрасывать их на диск. Иногда это дает огромный прирост производительности.

И еще тут не зря посоветовали асинхронные операции и порты завершения ввода-вывода — в
Windows вряд ли существуют более эффективные и расширяемые механизмы для подобных задач.
Например, пишущий поток (или пул потоков) может брать поступившие данные, ставить их в
очередь ввода-вывода и сразу же переходить к следующим, не дожидаясь фактического
завершения операции. Работа с файлами асинхронна по своей природе, потому что
ее выполняет жесткий диск, а не процессор.

Могу посоветовать asio из boost — там вроде эти же самые механизмы и используются.
Re[3]: [win] запись в файл в фоновом потоке
От: uzhas Ниоткуда  
Дата: 30.04.11 12:56
Оценка: 1 (1)
Здравствуйте, Abyx, Вы писали:

A>да, я это читал, только как это применить к моей проблеме?

тогда мне не ясно что вам не ясно. там вполне доходчиво все объясняется
коротко : система взяла на себя функции обслуживать асинхронную запись и вместо создания своих потоков можно воспользоваться уже готовыми механизмами:

while (данные есть)
{

  1. писатель берет порцию данных (например, 100 Кб) и пихает в систему, используя асинхронное API
  2. система берет данные и обязуется их однажды записать на диск.
далее управление передается клиенту и он, не дожидаясь окончания записи, уходит в цикл
}

Wait(система все скинула на диск)

Re[4]: upd: [win] запись в файл в фоновом потоке
От: Abyx Россия  
Дата: 30.04.11 12:59
Оценка:
Здравствуйте, Аноним, Вы писали:

A>>>кроме того, в этой задаче мне важнее всего быстродействие добавления данных в основном потоке,

A>>>и у меня есть подозрения что любые решения использующие WinAPI будут проигрывать кроссплатформенным решениям не взаимодействующим с ОС.
O>>а вот здесь ваша логика в корне ошибочна

А>нет там логики, есть подозрения


я знаю что генерация данных для записи происходит очень быстро (сотни тактов) и я знаю что функции WinAPI в лучшем случае работают примерно столько же (критические секции — порядка сотни тактов; SetEvent, HeapAlloc — порядка тысячи тактов)

считайте что код, генерирующий данные выглядит так
for(int i = 0; i != 100*1000*1000; ++i)
{
    char buf[16];
    _itoa(rand(), buf, 16);
    write_data(buf, strlen(buf));
}


желательно чтобы время работы write_data было не более 10% от времени работы rand() + itoa()
In Zen We Trust
Re[4]: [win] запись в файл в фоновом потоке
От: Abyx Россия  
Дата: 30.04.11 13:07
Оценка:
Здравствуйте, uzhas, Вы писали:

U> писатель берет порцию данных (например, 100 Кб) и пихает в систему, используя асинхронное API

чтобы накопить эти 100 Кб, нужен какой-то код, наверное класс потока, где такой взять?

U>Wait(система все скинула на диск)

а вот этого в основном потоке не должно быть вообще, по условию задачи
In Zen We Trust
Re[5]: [win] запись в файл в фоновом потоке
От: uzhas Ниоткуда  
Дата: 30.04.11 13:20
Оценка:
Здравствуйте, Abyx, Вы писали:

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


U>> писатель берет порцию данных (например, 100 Кб) и пихает в систему, используя асинхронное API

A>чтобы накопить эти 100 Кб, нужен какой-то код, наверное класс потока, где такой взять?

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

U>>Wait(система все скинула на диск)

A>а вот этого в основном потоке не должно быть вообще, по условию задачи
Wait делается в самом-самом конце всей записывающей программы (когда заветные 500Mb будут отправлены на запись и данных для записи больше не будет)
вам нужно внимательнее все прочитать и осмыслить, в том числе документацию на msdn, куда ведут вышеупомянутые ссылки

зы: я даже боюсь вам советовать в boost::io_service из-за странных вопросов
Re[6]: [win] запись в файл в фоновом потоке
От: Abyx Россия  
Дата: 30.04.11 13:27
Оценка:
Здравствуйте, uzhas, Вы писали:

A>>чтобы накопить эти 100 Кб, нужен какой-то код, наверное класс потока, где такой взять?

U>в программе лишь один поток, второй не нужен, и класс потока вам не нужен
здесь под потоком имелся ввиду "stream"

U>>>Wait(система все скинула на диск)

A>>а вот этого в основном потоке не должно быть вообще, по условию задачи
U>Wait делается в самом-самом конце всей записывающей программы (когда заветные 500Mb будут отправлены на запись и данных для записи больше не будет)
не понял насчет "записывающей программы" %)
еще раз повторю, поток который выдает данные для записи, окончания записи ждать не должен. по условию задачи.
то что процесс перед завершением должен ждать завершения фоновых потоков — это отдельный вопрос.

U>зы: я даже боюсь вам советовать в boost::io_service из-за странных вопросов

такого нет. есть boost::asio::io_service
In Zen We Trust
Re[5]: upd: [win] запись в файл в фоновом потоке
От: okman Беларусь https://searchinform.ru/
Дата: 30.04.11 13:30
Оценка: +1
Здравствуйте, Abyx, Вы писали:

A>считайте что код, генерирующий данные выглядит так

A>
A>for(int i = 0; i != 100*1000*1000; ++i)
A>{
A>    char buf[16];
A>    _itoa(rand(), buf, 16);
A>    write_data(buf, strlen(buf));
A>}
A>


Если так, то здесь налицо проблема маленького буфера.
Никакие алгоритмы и распараллеливания не помогут, если данные пишутся в файл по нескольку
байт за один присест. Тут нужно расширять файловый буфер (хотя бы в виде обертки над WriteFile).
Положительный результат наблюдался неоднократно.
Re[7]: [win] запись в файл в фоновом потоке
От: uzhas Ниоткуда  
Дата: 30.04.11 13:50
Оценка:
Здравствуйте, Abyx, Вы писали:


A>еще раз повторю, поток который выдает данные для записи, окончания записи ждать не должен. по условию задачи.


то есть вам важна скорость генерации данных, а не запись их в файл
тогда вам нужна lock-free очередь для переброски данных из главного потока в дополнительный (пишущий)
у вас один писатель в очередь и один потребитель, поэтому задача очень упрощается
покопайте буст\гугл. WinAPI вряд ли вам поможет
Re[6]: upd: [win] запись в файл в фоновом потоке
От: Abyx Россия  
Дата: 30.04.11 14:04
Оценка:
Здравствуйте, okman, Вы писали:

O>Тут нужно расширять файловый буфер (хотя бы в виде обертки над WriteFile).

так в этом и есть вопрос — какая штука будет накапливать данные и потом в фоновом потоке писать в файл.
In Zen We Trust
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.