Добрый день.
Пишу утилиту для работы с данными на диске. По самым разным причинам, мне приходится открывать файлы через O_DIRECT (с фоллбэком на буферизованный ввод/вывод, если это не возможно).
Но так как я работаю с большим количеством файлов одновременно, хотелось бы делать это асинхронно. Платформа, на которой я работаю — Linux, соотв. здесь есть Linux AIO. Но хочется взять что-нибудь готовое, так как возможно, придется портировать утилиту под windows. Я немного поизучал данный предмет и пришел к выводу, что ничего готового — нет. Boost.Asio — не может работать с файлами (точнее может, но только под windows). libuv вроде как не использует aio, а реализует внутри себя пулл потоков, на котором запускается выполнение всех операций (это подтверждается тем, что в libuv абсолютно все операции над файлами выполняются асинхронно).
Может я где-то ошибся и где-то есть она — библиотека для асинхронного файлового ввода-вывода?
Здравствуйте, chaotic-kotik, Вы писали:
CK>Пишу утилиту для работы с данными на диске. По самым разным причинам, мне приходится открывать файлы через O_DIRECT (с фоллбэком на буферизованный ввод/вывод, если это не возможно).
Насчет библиотеки не посоветую, но про сам AIO немного расскажу.
Во-первых, в линухе есть два AIO: POSIX AIO и нативный AIO. API у них совершенно разный. Нативный, это тот, который делается через io_submit()/io_getevents(), а посиксный — это тот, который делается через aio_read(), aio_write() и т.п.
Нативный AIO, он правда является настоящим асинхронным дисковым вводом-выводом, а посиксный лишь эмулирует его, раскидывая запросы по некоторому количеству внутренних потоков. Во всяком случае, так оно было, когда я последний раз на него смотрел.
Кроме того, мне попадалась библиотечка, которая реализует позиксный API поверх нативного AIO, и в моем подсознании происхождение этой библиотечки у меня настойчиво ассоциируется с фирмой Oracle. Но она производит впечатление умирающего проекта, поэтому с ней я бы связываться не стал.
Во-вторых, асинхронный дисковый ввод-вывод имеет смысл только если удастся накидать много запросов, чтобы система могла их отсортировать в удобном для диска порядке, чтобы системе было из чего выбирать. Но это имеет смысл только если система очень хорошо понимает, как работает данный конкретный физический диск (что совсем нетривиально, потому что логическая геометрия диска, как она видна системе, весьма отдаленно соотносится с его реальной физической геометрией). Кроме того, работа в режиме "прочитаем 100 мегабайт из первого файла, потом из второго, потом из третьего" дает в итоге гораздо большую производительность, чем одновременное чтение из каждого файла понемногу.
В общем, надо экспериментировать, дает ли асинхронный ввод-вывод какой-либо выигрыш в вашей конкретной задаче, или наоборот, проигрывает, или получаются близкие результаты. Причем у скедулера ввода-вывода есть свои ручки для настройки, экспериментировать надо, крутя эти ручки. И для SSD ответ будет совершенно другим, чем для механического диска.
Это соображение, кстати, к венде тоже относится. Причем может оказаться, что ответ на вопрос о выборе между синхронным и асинхронным вводом-выводом будет разный, в зависимости от системы.
В третьих, у нативного AIO интерфейс такой, что, на первый взгляд, его невозможно совместить в одном потоке с poll/select/eventfd. Потому что один поток может ждать либо io_getevents(), либо poll(), но не одновременно. Но если некоторое время подумать головой, и потом встать на уши, то совместить удается. Если кому интересно, могу отдельным письмом рассказать, как это сделать.
Здравствуйте, Pzz, Вы писали:
Pzz>Нативный AIO, он правда является настоящим асинхронным дисковым вводом-выводом, а посиксный лишь эмулирует его, раскидывая запросы по некоторому количеству внутренних потоков. Во всяком случае, так оно было, когда я последний раз на него смотрел.
Ничего не изменилось.
Pzz>Во-вторых, асинхронный дисковый ввод-вывод имеет смысл только если удастся накидать много запросов, чтобы система могла их отсортировать в удобном для диска порядке, чтобы системе было из чего выбирать. Но это имеет смысл только если система очень хорошо понимает, как работает данный конкретный физический диск (что совсем нетривиально, потому что логическая геометрия диска, как она видна системе, весьма отдаленно соотносится с его реальной физической геометрией). Кроме того, работа в режиме "прочитаем 100 мегабайт из первого файла, потом из второго, потом из третьего" дает в итоге гораздо большую производительность, чем одновременное чтение из каждого файла понемногу.
Pzz>В общем, надо экспериментировать, дает ли асинхронный ввод-вывод какой-либо выигрыш в вашей конкретной задаче, или наоборот, проигрывает, или получаются близкие результаты. Причем у скедулера ввода-вывода есть свои ручки для настройки, экспериментировать надо, крутя эти ручки. И для SSD ответ будет совершенно другим, чем для механического диска.
Я делаю оптимизацию под SSD + есть кое какие требования, которые проще всего реализовать через O_DIRECT+асинхронность.
Pzz>Это соображение, кстати, к венде тоже относится. Причем может оказаться, что ответ на вопрос о выборе между синхронным и асинхронным вводом-выводом будет разный, в зависимости от системы.
Виндовая реализация если и будет, то только для опытов. Чтобы пользователь мог установить на свою машину, посмотреть как оно работает и поучиться писать конфиги. Там высокая производительность не так важна.
Pzz>В третьих, у нативного AIO интерфейс такой, что, на первый взгляд, его невозможно совместить в одном потоке с poll/select/eventfd. Потому что один поток может ждать либо io_getevents(), либо poll(), но не одновременно. Но если некоторое время подумать головой, и потом встать на уши, то совместить удается. Если кому интересно, могу отдельным письмом рассказать, как это сделать.
Мне интересно, т.к. подозреваю что буду писать свой велосипед. Нельзя просто в тему запостить?
Здравствуйте, chaotic-kotik, Вы писали:
Pzz>>В общем, надо экспериментировать, дает ли асинхронный ввод-вывод какой-либо выигрыш в вашей конкретной задаче, или наоборот, проигрывает, или получаются близкие результаты. Причем у скедулера ввода-вывода есть свои ручки для настройки, экспериментировать надо, крутя эти ручки. И для SSD ответ будет совершенно другим, чем для механического диска.
CK>Я делаю оптимизацию под SSD + есть кое какие требования, которые проще всего реализовать через O_DIRECT+асинхронность.
Мне кажется, для SSD использование асинхронного ввода-вывода не даст сколь либо ощутимой оптимизации.
Pzz>>В третьих, у нативного AIO интерфейс такой, что, на первый взгляд, его невозможно совместить в одном потоке с poll/select/eventfd. Потому что один поток может ждать либо io_getevents(), либо poll(), но не одновременно. Но если некоторое время подумать головой, и потом встать на уши, то совместить удается. Если кому интересно, могу отдельным письмом рассказать, как это сделать.
CK>Мне интересно, т.к. подозреваю что буду писать свой велосипед. Нельзя просто в тему запостить?
1) Под ниткой здесь и далее я имею ввиду ту нитку, в которой крутится event loop, а под сокетом — файловый дескриптор, которым нам интересен на предмет poll() (но он может быть и не сокетом, а пайпом и т.п.). Можно сделать несколько таких ниток, но каждый сокет должен быть привязан к своей нитке.
2) В нитке маскируем SIGIO (man pthread_sigmask)
3) Все файловые сокеты, которые мы собираемся poll()'ить, настраиваем так, чтобы от них приходил SIGIO. См. man fcntl на предмет F_SETSIG, F_SETOWN_EX, O_ASYNC и O_NONBLOCK. Я писал это под более старое ядро, чем сегодняшние, поэтому работающего куска кода не покажу, но в мане вроде все просто. В общем, надо, чтобы приходил SIGIO, причем конкретно той нитке, которая будет ждать, а не первой попавшейся
5) Обработчик SIGIO делает siglongjmp(jmp_buf, 1);
6) Поскольку в случае прихода сигнала мы не можем знать, успели ли мы вызвать io_getevents(), то значение, которое эта функция возвращает, для нас бессмысленно, и мы вынуждены сканировать массив struct io_event после вызова io_getevents(), заполнив его нулями до вызова, чтобы понять, успел ли он вернуть сколько-нибудь событий.
7) Если после выхода из ожидания got_signal = true, разбираемся с сокетами используя любой механизм (select/poll/epoll) с нулевым таймаутом.
Здравствуйте, chaotic-kotik, Вы писали:
CK>Я делаю оптимизацию под SSD + есть кое какие требования, которые проще всего реализовать через O_DIRECT+асинхронность.
думаю, что асинхронное общение имеет смысл делать, если кроме IO у вас еще есть что поделать : отвечать на внешние запросы от юзера, отрисовывать гуй
если вы плотно работаете с диском и, к примеру, тупо перегоняете толстые файлы с места на место, то асинхронность может наоборот навредить как в плане скорости работы, так и в плане усложнения всей программы
Здравствуйте, chaotic-kotik, Вы писали:
Pzz>>Это соображение, кстати, к венде тоже относится. Причем может оказаться, что ответ на вопрос о выборе между синхронным и асинхронным вводом-выводом будет разный, в зависимости от системы.
CK>Виндовая реализация если и будет, то только для опытов. Чтобы пользователь мог установить на свою машину, посмотреть как оно работает и поучиться писать конфиги. Там высокая производительность не так важна.
Здравствуйте, Pzz, Вы писали:
Pzz>В третьих, у нативного AIO интерфейс такой, что, на первый взгляд, его невозможно совместить в одном потоке с poll/select/eventfd. Потому что один поток может ждать либо io_getevents(), либо poll(), но не одновременно. Но если некоторое время подумать головой, и потом встать на уши, то совместить удается. Если кому интересно, могу отдельным письмом рассказать, как это сделать.
Это не правда — он совмещается с epoll совершенно нормально.
Здравствуйте, placement_new, Вы писали:
Pzz>>В третьих, у нативного AIO интерфейс такой, что, на первый взгляд, его невозможно совместить в одном потоке с poll/select/eventfd. Потому что один поток может ждать либо io_getevents(), либо poll(), но не одновременно. Но если некоторое время подумать головой, и потом встать на уши, то совместить удается. Если кому интересно, могу отдельным письмом рассказать, как это сделать.
_>Это не правда — он совмещается с epoll совершенно нормально.
Здравствуйте, Pzz, Вы писали:
Pzz>Здравствуйте, placement_new, Вы писали:
Pzz>>>В третьих, у нативного AIO интерфейс такой, что, на первый взгляд, его невозможно совместить в одном потоке с poll/select/eventfd. Потому что один поток может ждать либо io_getevents(), либо poll(), но не одновременно. Но если некоторое время подумать головой, и потом встать на уши, то совместить удается. Если кому интересно, могу отдельным письмом рассказать, как это сделать.
_>>Это не правда — он совмещается с epoll совершенно нормально.
Pzz>И как же?
Здравствуйте, Pzz, Вы писали:
CK>>Я делаю оптимизацию под SSD + есть кое какие требования, которые проще всего реализовать через O_DIRECT+асинхронность.
Pzz>Мне кажется, для SSD использование асинхронного ввода-вывода не даст сколь либо ощутимой оптимизации.
Я пробовал. По моим измерениям таки дает. Правда я использовал uv.
Pzz>1) Под ниткой здесь и далее я имею ввиду ту нитку, в которой крутится event loop, а под сокетом — файловый дескриптор, которым нам интересен на предмет poll() (но он может быть и не сокетом, а пайпом и т.п.). Можно сделать несколько таких ниток, но каждый сокет должен быть привязан к своей нитке.
Pzz>2) В нитке маскируем SIGIO (man pthread_sigmask)
Pzz>3) Все файловые сокеты, которые мы собираемся poll()'ить, настраиваем так, чтобы от них приходил SIGIO. См. man fcntl на предмет F_SETSIG, F_SETOWN_EX, O_ASYNC и O_NONBLOCK. Я писал это под более старое ядро, чем сегодняшние, поэтому работающего куска кода не покажу, но в мане вроде все просто. В общем, надо, чтобы приходил SIGIO, причем конкретно той нитке, которая будет ждать, а не первой попавшейся
Pzz>4) Ожидание выглядит следующим образом:
Pzz>
Pzz>5) Обработчик SIGIO делает siglongjmp(jmp_buf, 1);
Pzz>6) Поскольку в случае прихода сигнала мы не можем знать, успели ли мы вызвать io_getevents(), то значение, которое эта функция возвращает, для нас бессмысленно, и мы вынуждены сканировать массив struct io_event после вызова io_getevents(), заполнив его нулями до вызова, чтобы понять, успел ли он вернуть сколько-нибудь событий.
Pzz>7) Если после выхода из ожидания got_signal = true, разбираемся с сокетами используя любой механизм (select/poll/epoll) с нулевым таймаутом.
Pzz>Как-то примерно так...
Здравствуйте, uzhas, Вы писали:
U>думаю, что асинхронное общение имеет смысл делать, если кроме IO у вас еще есть что поделать : отвечать на внешние запросы от юзера, отрисовывать гуй
As a rule of thumb — с SSD частенько бывает выгодно работать асинхронно, так как у него очень много iops. Если легко можно утилизировать его пропускную способность без асинхронщины (например нужно тупо записать или прочитать много данных) то это можно сделать синхронно, но если все сложнее (random I/O), то единственный способ его утилизировать — выполнять много всего одновременно полностью забивая очередь запросами.
U>если вы плотно работаете с диском и, к примеру, тупо перегоняете толстые файлы с места на место, то асинхронность может наоборот навредить как в плане скорости работы, так и в плане усложнения всей программы
Тупо копировать файлы проще всего синхронно и крупными блоками, да. Если асинхронно читать в одном месте и писать в другое, то все будет медленнее работать из-за read/write interference (есть такой эффект на SSD).
Че-то я такой штучки в своем линухе не вижу. Это классно, конечно, что они ее добавили, но на практике приходится быть совместимым с ядрами какой-то разумной степени древности.
Чтоб не делать постоянно io_getevents, создаётся дескриптор fd с eventfd (для бородатых версий нужно вызывать eventfd2), в каждом iocb OR-ится флаг IOCB_FLAG_RESFD, а поле aio_resfd инициалиируется полученным fd, далее этот fd может политься с epoll/poll/select, с подпиской на нужные события (POLLIN/POLLOUT), как только хоть один из переданных iocb готов для обработки, уведомление об этом прилетит на epoll_wait/poll через fd.
Здравствуйте, Pzz, Вы писали:
Pzz>Здравствуйте, placement_new, Вы писали:
_>>Через io_set_eventfd _>>https://git.fedorahosted.org/cgit/libaio.git/tree/src/libaio.h
Pzz>Че-то я такой штучки в своем линухе не вижу. Это классно, конечно, что они ее добавили, но на практике приходится быть совместимым с ядрами какой-то разумной степени древности.
Эта ссылка, что я привел, на libaio — библиотека-враппер над kernel AIO. Сама функциональность должна быть в 2.6. smeeld хорошо все расписал.
Здравствуйте, chaotic-kotik, Вы писали:
CK>Здравствуйте, placement_new, Вы писали:
_>>Что не так с FB? Код там получше много что я видел в российских компаниях.
CK>experimental же
Аа, я думаю это скорее к самому kernel AIO относится. Сама реализация нормальная.
Здравствуйте, chaotic-kotik, Вы писали:
CK>... Boost.Asio — не может работать с файлами (точнее может, но только под windows).
с каких это парофф? вроде всегда был и posix::stream_descriptor, и windows::stream_handle. даже использовал несколько раз.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)