Чтоб не делать постоянно io_getevents, создаётся дескриптор fd с eventfd (для бородатых версий нужно вызывать eventfd2), в каждом iocb OR-ится флаг IOCB_FLAG_RESFD, а поле aio_resfd инициалиируется полученным fd, далее этот fd может политься с epoll/poll/select, с подпиской на нужные события (POLLIN/POLLOUT), как только хоть один из переданных iocb готов для обработки, уведомление об этом прилетит на epoll_wait/poll через fd.
Добрый день.
Пишу утилиту для работы с данными на диске. По самым разным причинам, мне приходится открывать файлы через 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).
Че-то я такой штучки в своем линухе не вижу. Это классно, конечно, что они ее добавили, но на практике приходится быть совместимым с ядрами какой-то разумной степени древности.
Здравствуйте, 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 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, niXman, Вы писали:
CK>>... Boost.Asio — не может работать с файлами (точнее может, но только под windows). X>с каких это парофф? вроде всегда был и posix::stream_descriptor, и windows::stream_handle. даже использовал несколько раз.
stream_descriptor не подходит, мне нужен случайный доступ а не потоковый, а posix::stream_descriptor подходит для пайпов и всего такого, но не для файлов, открытых через O_DIRECT, ну и я уверен что он там внутри linux aio не использует. Мне подходит windows::random_access_handle, но у него нет аналога в пространстве имен posix.
Здравствуйте, chaotic-kotik, Вы писали:
CK>Там упоминается Direct I/O. Этого достаточно, ИМО.
совершенно недостаточно. O_DIRECT можно использовать и на потоковом доступе.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, niXman, Вы писали:
X>совершенно недостаточно. O_DIRECT можно использовать и на потоковом доступе.
Ты можешь это утверждение чем-нибудь подкрепить?
Как минимум странно использовать потоковые дескрипторы в асинхронном режиме, т.к. операции все равно должны быть выстроены в очередь. So what's the point?
Здравствуйте, chaotic-kotik, Вы писали:
CK>Ты можешь это утверждение чем-нибудь подкрепить?
могу подкрепить ссылкой на ман(вдруг ты не читал), и еще вот этой. а так же, своим опытом использования этого флага.
из приведенных мною ссылок и опыта, могу смело утверждать, что ни "случайный", ни "потоковый" доступы к этому флагу не имеют никакого отношения. все что этот флаг "делает", так это Try to minimize cache effects, и что дополнительно нужно помнить про The O_DIRECT flag on its own makes an effort to transfer data synchronously, but does not give the guarantees of the O_SYNC flag that data and necessary metadata are transferred. To guarantee synchronous I/O, O_SYNC must be used in addition to O_DIRECT.
CK>Как минимум странно использовать потоковые дескрипторы в асинхронном режиме, т.к. операции все равно должны быть выстроены в очередь. So what's the point?
что вообще понимается под "потоковые дескрипторы"? мне кажется мы говорим о разном...
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
могу еще провести экскурс реализации для флагов O_DIRECT и O_SYNC внутри ядра, но не думаю что оно того стОит.
к тому же, не уверен, что для вянды есть какие-то аналоги этим флагам...
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, chaotic-kotik, Вы писали:
CK>Здравствуйте, niXman, Вы писали:
X>>совершенно недостаточно. O_DIRECT можно использовать и на потоковом доступе.
CK>Ты можешь это утверждение чем-нибудь подкрепить? CK>Как минимум странно использовать потоковые дескрипторы в асинхронном режиме, т.к. операции все равно должны быть выстроены в очередь. So what's the point?
O_DIRECT определяет только механизм ввода/вывода используемый драйвером ФС, кроме ФС данные проходят ещё через слой блочных устройств в ядре, саму передачу данных драйвером адаптера шины, на всё на это O_DIRECT никак не влияет. Но нотификация при выполнении всех операций будет прилетать при использовании O_DIRECT так же, как и при его неиспользовании.
Здравствуйте, niXman, Вы писали:
X>из приведенных мною ссылок и опыта, могу смело утверждать, что ни "случайный", ни "потоковый" доступы к этому флагу не имеют никакого отношения. все что этот флаг "делает", так это Try to minimize cache effects, и что дополнительно нужно помнить про The O_DIRECT flag on its own makes an effort to transfer data synchronously, but does not give the guarantees of the O_SYNC flag that data and necessary metadata are transferred. To guarantee synchronous I/O, O_SYNC must be used in addition to O_DIRECT.
Если бы ты по делу пользовался O_DIRECT, то вспомнил бы про другое
X>что вообще понимается под "потоковые дескрипторы"? мне кажется мы говорим о разном...
Потоковые это pipe, fifo, unix socket etc. Они не поддерживают seek. Если ты открываешь файл с флагом O_DIRECT то он поддерживает seek, в него конечно можно писать последовательно, но это не значит что файл это то же самое что и pipe.
Мне же нужно работать с файлом как с блочным устройством напрямую, в обход кеша и параллельно. Т.е. у меня есть несколько потоков, которые асинхронно записывают блоки фиксированного размера в файл. Я там выше писал что это — оптимизация под SSD, мне нужна асинхронность для того чтобы загрузить очередь команд eNVM SSD, иначе пропускную способность не получается утилизировать. Сделать это через boost::asio::posix::stream_descriptor нельзя, так как у него нет а) возможности указать offset для каждой операции записи б) при записи последовательно получить оффсет по которому произошла запись в) писать параллельно в несколько потоков (я имею ввиду не потоки ОС, а возможность отправить устройству несколько команд записи/чтения параллельно).
Здравствуйте, chaotic-kotik, Вы писали:
CK>Мне же нужно работать с файлом как с блочным устройством напрямую, в обход кеша и параллельно. Т.е. у меня есть несколько потоков, которые асинхронно записывают блоки фиксированного размера в файл. Я там выше писал что это — оптимизация под SSD, мне нужна асинхронность для того чтобы загрузить очередь команд eNVM SSD, иначе пропускную способность не получается утилизировать. Сделать это через boost::asio::posix::stream_descriptor нельзя, так как у него нет а) возможности указать offset для каждой операции записи б) при записи последовательно получить оффсет по которому произошла запись в) писать параллельно в несколько потоков (я имею ввиду не потоки ОС, а возможность отправить устройству несколько команд записи/чтения параллельно).
откуда эта задача вообще взялась? оО
в топике ничего об этом нет я же отвечал исключительно по информации описанной в топике.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, chaotic-kotik, Вы писали:
CK>в топике сказано что то что есть в boost.asio мне не подходит
не правда. в топике сказано, цитирую:
Boost.Asio — не может работать с файлами (точнее может, но только под windows).
я же указал на то, что это не соответствует действительности, и что asio предоставляет фозможность работать с файлами и в линукс.
ни о каких O_DIRECT, как и ни слова про seekable в топике нет.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, chaotic-kotik, Вы писали:
CK>про необходимость выравнивания
если вы берете на себя заботу о кеше — то и выравнивание становится вашей заботой.
CK>и работу в обход кэша например
т.е. вы хотите сказать, что я процитировал ман, и не заметил что в цитате говорится именно о этом? =)
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, niXman, Вы писали:
X>я же указал на то, что это не соответствует действительности, и что asio предоставляет фозможность работать с файлами и в линукс.
через жопу
X>ни о каких O_DIRECT, как и ни слова про seekable в топике нет.
Пишу утилиту для работы с данными на диске. По самым разным причинам, мне приходится открывать файлы через O_DIRECT (с фоллбэком на буферизованный ввод/вывод, если это не возможно).
ну и обсуждение состоит не только из первого комментария