Close

18.10.2016

О том как Windows лучше Linux. Или почему Erlang и GO еще долго будут в тренде

Надо быть честным с собой

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

Вот о каких системах, примерно, речь http://kkovacs.eu/cassandra-vs-mongodb-vs-couchdb-vs-redis  (Couch, Riak — erlang; etcd — go).

Довольно часто можно видеть по реализации, что на асинхронность ставок мало, особенно при работе с данными на диске.

Это странно, ведь по логике, побеждать должны те, у кого хорошая работа с асинхронностью… (за исключением роста scale out), 

а не те, у кого хорошее управление потоками.

БД, типа Postgres, тоже использует много IO потоков.

Я тут попробовал сделать хорошую асинхронную работу с данными на диске под Linux и пришёл к удивительным выводам… Кажется, я понял, в чем тут причина.

Оказывается, в Linux с асинхронщиной и диском совсем всё плохо. (читать далее)

Linux Disk  —  совсем всё плохо (с асинхронщиной)

в Linux(слава создателям!) есть аналог kqueue, это eppol    epool7    epool_wait2 

(для regular file не подходит)

Для socket fd  это работает отлично, а вот для файлов на диске есть трудности.

Часто мы видим in-memory хранилища «разной формы», а вот дисковые библотеки хранения, которые работают в области БД, это предмет соревнований «крутых контор одиночек».

Очень сложно сделать так, чтобы с ростом объёмов данных показатели не ухудшались. В чем же причина ?

Я попробовал немного покопать.

Что такое асинхронные файловые операции ?   Они, потенциально, должны  повысить эффективность программ также, как асинхронные сетевые операции сейчас повышают производительность сетевых приложений.

Это просто: операция запускается в ядро и сразу возврат, позже ядро уведомляет программу о результате эффективным способом.

Самый эффективный способ уведомлять программу в User space из kernel space- это epool для Linux  и IOCP для windows.

Все знают библиотеку libuv — основу Node.js. лидера по комбинации асинхронных удобств для программиста.  Её асинхронные файловые операции основаны на aio_хххх вызовах.

Это «то, самое aio_  api»:

http://www.gnu.org/software/libc/manual/html_node/Asynchronous-I_002fO.html

Например, так на Lua можно её использовать:

local uv = require('luv')


local fname = arg[1] and arg[1] or arg[0]

uv.fs_open(fname, 'r', tonumber('644', 8), function(err,fd)
    if err then
        print("error opening file:"..err)
    else
        local stat = uv.fs_fstat(fd)
        local off = 0
        local block = 10
        
        local function on_read(err,chunk)
            if(err) then
                print("Read error: "..err);
            elseif #chunk==0 then
                uv.fs_close(fd)
            else
                off = block + off
                uv.fs_write(1,chunk,-1,function(err,chunk)
                    if err then
                        print("Write error: "..err)
                    else
                        uv.fs_read(fd, block, off, on_read)
                    end
                end)
            end
        end
        uv.fs_read(fd, block, off, on_read)
    end
end)



uv.run('default')
uv.loop_close()

A вот ограничения aio для Linux  .

What Works?

  • AIO read and write on raw (and O_DIRECT on blockdev)
  • AIO read and write on files opened with O_DIRECT on ext2, ext3, jfs, xfs

Здесь важно, что чтение и запись идут без буферизации. 

Для блочных устройств не на всех FS. (зависит от драйвера?)

Т.е. это именно непосредственно сброс подготовленных данных на диск flush. Планирование ввода-вывода должно быть тогда в программе.

Идём дальше.

What Does Not Work?

  • AIO read and write on files opened without O_DIRECT (i.e. normal buffered filesystem AIO). On ext2, ext3, jfs, xfs and nfs, these do not return an explicit error, but quietly default to synchronous or rather non-AIO behaviour (i.e io_submit waits for I/O to complete in these cases). For most other filesystems, -EINVAL is reported.
  • AIO fsync (not supported for any filesystem)
  • AIO read and write on sockets (doesn’t return an explicit error, but quietly defaults to synchronous or rather non-AIO behavior)
  • AIO read and write on pipes (reports -EINVAL)
  • Not all devices (including TTY) support AIO (typically return -EINVAL)

Ну, скажем, aio для сокетов нам не нужен — есть варианты и без него.

fsync не нужен тоже, ведь он уже в read|write with O_DIRECT.

Для pipe есть workaround «splice», о нём ниже. (решить не писать об этом «хаке»)

Но, в целом, бедновато, а без O_DIRECT вообще не стоит пробовать. Естественно,  такие вещи должны горячо обсуждаться, и так оно и есть.

На SO обсуждают проблему: (хороший ответ)

http://stackoverflow.com/questions/13407542/is-there-really-no-asynchronous-block-i-o-on-linux

И вот она главная причина, оказывается:

  1. posix aio is implemented in userland in glibc, using threads, so no, it’s not true AIO. Linux AIO (io_submit) is something I want to hear more about, but I’ve seen nobody actually use it for anything, which to me means there be dragons there. That’s part of what this question is trying to suss out. sendfile() has nothing to do with asynchronous disk-based I/O. I’d be happy to accept your answer if it actually contributes to a solution — but notice that I already mentioned io_submit in my question. – Jon Watte
  2. Linux AIO is not unused… For example, InnoDB (MySQL) use io_submit
  3. For disk i/o, we have posix aio, linux aio, sendfile and friends.
    1. Что вносит как раз только путаницу
  4. Linux does provide async block I/O at the kernel level… And if you asked who are the users of these API, it is the kernel itself…
  5. io_submit несовместимый с posix aio, который кажись работает https://www.opennet.ru/man.shtml?topic=io_submit&category=2&russian=0 (надо уметь этим пользоваться, см. ссылку SO выше)
    1. «io_submit» and friends are still not available in glibc (see io_submit manpages), which I have verified in my Ubuntu 14.04, but this API is linux-specific.
  6. signals Linux aio / io model http://personal.denison.edu/~bressoud/cs375-s13/supplements/linux_altIO.pdf

Исчерпывающий ответ по статусу async IO в Linux

http://stackoverflow.com/questions/87892/what-is-the-status-of-posix-asynchronous-i-o-aio?rq=1

libuv, libev, libevent 

Очень быстры, хотите доказательства?

https://ghc.haskell.org/trac/ghc/ticket/8400

ghc haskell RTS хотят использовать его! Очень респектушистый знак.

НО они кроссплатформенны. Остаётся лишь надеяться, что для каждой ОС эти либы выберут наилучший способ опроса и в будущем, когда статус aio поменяется в лучшую сторону, они автоматически начнут пользоваться более эффективными способами.

Какое складывается ощущение «типа выводы»

Поскольку для нормального file aio  Linux ядро и glibс оба должны быть новых версий, либо надо бы собрать и включить и убедиться… а людям надо асинхронные операции…

Но по умолчанию они в glibc реализованы через thread потоки (пул).

Я думаю:

Программы, которые стремятся по простому получить file aio, используя libuv и подобное (event loop, «one» thread), ПО ПОНЯТНЫМ ПРИЧИНАМ ПРОИГРЫВАЮТ программам, которые сразу делают ставку на эффективное управление потоками и  выполнять IO в параллельных процессах (Erlang, Go, RTS) поддержанными в языках. 

Это не по причине «потоки рулят,  loop — отстой», а по причине aio_ сделан через похх пул потоков, т.е. костыльно.

Насколько я знаю, в Windows IOCP нет такой проблемы, а ведь там ещё есть и Fibers !!! Который я напрасно жду в линуксах.

Как же мне, линуксойду, трудно это осознавать! Пока пингвин топчется на льдине, «система», в которой самая массовая обработка уведомлений привязана к ОКНАМ (сейчас ситуация поменялась), не сдаётся, да ведь «Не то слово» ! — Прискорбно 😉

Это, кстати, камень в наш огород, «свободные разработчики».

Что же тогда нам остаётся делать ?

Изучать

1) linux aio_   io_setup with examples

2) учиться пользоваться тем, что есть

3) прочитать signals Linux aio / io model http://personal.denison.edu/~bressoud/cs375-s13/supplements/linux_altIO.pdf

On Linux 2.4 and earlier, signal-driven I/O can be employed with file descriptors for sockets, terminals, pseudoterminals, and certain other types of devices. Linux 2.6 additionally allows signal-driven I/O to be employed with pipes and FIFOs. Since Linux 2.6.25, signal-driven I/O can also be used with inotify file descriptors.

4) искать альтернативы, или делать их.

 

UPD1

Asynchronous I/O in Windows for Unix Programmers

 

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *