Настраивается, путём записи в /proc/$pid/oom_score_adj. Правда, если вы какой-то процесс настроите так чтобы он не убивался (это тоже возможно), но он сойдёт с ума и сожрёт всю память — может быть намного хуже.
И уж точно если вы делаете его неубиваемым — нужно обрабатывать ситуации когда он не может получить память, иначе это не имеет смысла (разве что он не содержит данных которые нужно сохранять).
Единственный способ предоставить процессу заведомо достаточное количество памяти — это жёстко её выделить изначально, а это не всегда целесообразно — к примеру, это может быть процесс который обычно требует 10 Мб для работы, но изредка ему нужно (на короткое время) 100/200/500 Мб — если в этот самый момент когда оно нужно памяти нет, всё же лучше это обработать (к примеру, приостановить работу пока не появится). Выделять ему сразу потенциальный максимум — это просто бесполезная трата ресурса, своп тоже не всегда имеется (или может быть медленным до непрактичности).
просто проектировать программу так, чтобы данные никогда не повреждались при падении
И как вы это сделаете без обработки ситуации "караул, память не дали"? К примеру, если у вас есть внутренние буфера, ещё не отправленные куда нужно с помощью write()/send()/etc, они банально пропадут, незакрытые транзакции БД могут откатится а могут и нет (зависит от БД и режима), и ещё много вещей которые зависнут в памяти приложения при жёстком завале, или даже просто внешнее состояние которое останется "грязным".
Если программа убивается извне, это тоже можно (и нужно) обрабатывать, кроме, разумеется, SIGKILL — но в последнем случае вы в принципе не сможете спроектировать приложение так чтобы данные не терялись пока эти данные хранятся в самом приложении, точнее, в выделенной ему памяти.
OOM киллер убивает не того кто просит память а того кто занимает больше всего памяти, при этом учитывая его важность для системы (OOM score).
Т.е. если ваш процесс не самый толстый в системе и к тому же имеет пониженный OOM score, то шанс его убийства довольно низок, хотя и остаётся открытым вопрос что случится если убитый процесс важен для работы вашего (или системы в целом).
В любом случае лучше попытаться (если возможно) аккуратно завершить работу в случае если память не дали (сбросить буфера, закрыть соединения, отменить транзакцию etc) чем просто умереть и потерять данные, на этот случай даже можно предусмотреть аварийный пул памяти (запрашиваемый в самом начале работы) которая может потребоваться на случай её нехватки в процессе нормальной работы.
Мысль приходит когда у вас 10+ терабайт данных — на RAID с SSD вы просто разоритесь, в то время как RAID на HDD вполне справляется с нагрузкой за более разумные деньги — бюджет не всегда безразмерен, особенно когда из своего кармана.
Чтобы буфер переполнился нужно загнать туда 30-60 гб одним куском — это уж очень нагруженная база получается (а вот тут можно вспомнить про commit_delay и размеры транзакций), хотя даже мой CS3030 пишет 500 MiB/s после заполнения кэша, что всё равно в два раза быстрее DC HDD.
скорее всего, особого смысла в этой оптимизации нет
Вероятно, для ряда задач всё же он есть, но не в общем.
сейчас протестировал нормальный DC NVME — 30-40 мкс на fsync
Если мы перенесём эксперименты на HDD (не SSD) — разница проявится в полную силу. К сожалению, не везде и не всегда можно впихнуть SSD, к тому же не всегда они DC.
за счёт того, что fsync на DC дисках намного быстрее, в результате оно уже может перестать быть узким местом.
Независимо от скорости — два обращения к накопителю дороже одного, хотя бы потому что ядру нужно дождаться выполнения каждого по очереди (если пишет один процесс — а в postgres только один пишет в журнал, ибо всё должно быть последовательно). Насколько дороже — зависит от размера блока и их количества.
сколько у вас pg_test_fsync показывает?
Вполне ожидаемое замедление
5 seconds per test
O_DIRECT supported on this platform for open_datasync and open_sync.
Compare file sync methods using one 8kB write:
(in wal_sync_method preference order, except fdatasync is Linux's default)
open_datasync 643,253 ops/sec 1555 usecs/op
fdatasync 664,511 ops/sec 1505 usecs/op
fsync 438,914 ops/sec 2278 usecs/op
fsync_writethrough n/a
open_sync 430,532 ops/sec 2323 usecs/op
Compare file sync methods using two 8kB writes:
(in wal_sync_method preference order, except fdatasync is Linux's default)
open_datasync 320,054 ops/sec 3124 usecs/op
fdatasync 653,392 ops/sec 1530 usecs/op
fsync 400,675 ops/sec 2496 usecs/op
fsync_writethrough n/a
open_sync 203,359 ops/sec 4917 usecs/op
Compare open_sync with different write sizes:
(This is designed to compare the cost of writing 16kB in different write
open_sync sizes.)
1 * 16kB open_sync write 413,544 ops/sec 2418 usecs/op
2 * 8kB open_sync writes 197,709 ops/sec 5058 usecs/op
4 * 4kB open_sync writes 99,996 ops/sec 10000 usecs/op
8 * 2kB open_sync writes 46,859 ops/sec 21341 usecs/op
16 * 1kB open_sync writes 23,062 ops/sec 43361 usecs/op
Test if fsync on non-write file descriptor is honored:
(If the times are similar, fsync() can sync data written on a different
descriptor.)
write, fsync, close 430,991 ops/sec 2320 usecs/op
write, close, fsync 401,975 ops/sec 2488 usecs/op
Non-sync'ed 8kB writes:
write 183465,059 ops/sec 5 usecs/op
речь же изначально шла про «если он получит 8 команд на 8 блоков по 128K вместо одной на 1M» (ну разве что я себе позволил 1М на 4М заменить, но это не критично).
Видимо, я недостаточно ясно выразился — подразумевалось 8 команд параллельно, иначе смысл такого разбиения теряется, в вашем же варианте каждый поток читает их последовательно (причём гарантированно каждое чтение — как минимум одна команда накопителю, благодаря O_DIRECT). Причём, это было в контексте записи, хотя со чтением тоже помогает.
ваше же изначальное предложение в переносе на seq scan было примерно таково: запускаем 8 воркеров, первый читает первую запись, второй вторую
Примерно.
Так что я вернулся в вашему коду и кажется нашёл в чём проблема...
Каюсь, я был настолько "возмущен" наличем там for() что даже не стал анализировать pread(), оказалось — зря:
Вы в каждом блоке читаете bs + offset байт, я думаю это не совсем то что нужно, если учесть что offset сильно зависит от номера потока и становится очень немаленьким, т.е. вы не читаете каждый кусок последовательно блоками одного размера — что и приводит к ужасной производительности, т.к. offset ненулевой для числа потоков > 1.
Если убрать offset из размера буфера, чтение в один поток медленнее (~ 0.64s) чем в более чем один (~ 0.54s).
Но если вас интересуют "оригинальные" результаты (которые, на мой взгляд, лишены смысла из-за переменного размера блока зависящего от номера потока) — то получается всё те же ~ 0.64s для одного потока (ясный пень) но уже ощутимо медленнее для 2 (~ 0.81s) и уж тем более 8 (аж 2.40s).
а почему не стали читать устройство?
Почитал (в начале, где с вероятностью 99% нет пустышек) — результаты не изменились.
Не могу представить за счёт чего. Если у меня есть 10 параллельных транзакций, то запись из по очереди с fsync будет по определению медленней чем всех сразу с одним fsync, на любом диске который изобретён, потому что записать много маленьких блоков всегда дороже чем один большой.
Выигрыша может не быть разве что на сравнительно маленьких транзакциях, до 1-2 мегабайт, тогда всё в кэш дисков валится, но у меня они бывают и в несколько десятков мегабайт.
А тут я решил чуть-чуть поэкспериментировать...
Насчёт вашего super-fio есть пара замечаний:
смешивается выполнение fork() с операциями чтения, хоть и немного но может повлиять на обработку — в идеале это должны быть либо threads либо AIO, причём в первом случае лучше начинать их одновременно (по семафору, к примеру);
каждый процесс читает в цикле N блоков (и каждое чтение — запрос к накопителю), хотя всего-то нужно разбить один большой на много частей и читать каждую независимо — т.е. for() там совершенно лишний и pread() должен выглядеть как pread(fd, buf, bs, offset); — и всё.
К сожалению, время на чтение даже одного мегабайта уж очень мало в случае NVMe, так что сделал его 1 GiB и проверил — получилось 0.37s/0.24s (минимальное время) при одном потоке, и 0.35s/0.32s при четырех, но что примечательно — при одном потоке максимальное время было и около 1 секунды, при 4 и больше — оно стабильно 0.35s плюс-минус 0.02s из полусотни прогонов.
Итого, из 50 прогонов, (общее время всех прогонов, real time, система ничем не занята):
1 поток: 27s (бывало и 23, но редко)
2 потока: 18s (ощутимая разница, однако)
4 потока: 17s (чуть лучше)
8 потоков: 17s (без изменений)
16 потоков: 16s (видимо мы на пределе)
32 потока: 16s (таки да)
Я повторил эксперимент несколько раз, время отличается только для одного потока, остальные стабильно на показанном выше уровне. Хоть это и не кратное ускорение, но и условия не совсем честные (fork() вместо threads/aio), к тому же я читал файл а не устройство (но это вряд-ли существенно). Если использовать threads или aio, вероятно результаты станут лучше, с fork() я не совсем уверен что система реально отправляет запросы к устройству параллельно (а лезть в ebpf мне лень).
Опять-таки, если это будет запись вместо чтения, ситуация может существенно изменится в лучшую сторону — уверенности что оно читает из разных чипов нет, а писать скорее всего будет в разные (если будет время, поэксперементирую).
транзакция на пару мегабайт — это исключение всё-таки
Не скажите, зависит от приложения. Несколько сотен update/insert и уже накапливается, а в зависимости от размеров полей и больше может. Даже один апдейт по сотне тысяч строк легко наберёт мегабайты, если вспомнить что внутри Postgres update = delete/insert.
commit_delay на боевых базах вообще не включаю
Почему нет? Чем плохо совместить запись в журнал нескольких параллельных транзакций с одним fsync? Сто пудов два fsync на два блока по 1 MiB будут чуть-чуть медленней чем один на блок 2 MiB, а если блоки поменьше то выигрыш может быть существенным — на моей боевой прирост был около 30%, всего-то поднял delay до 100ms (там много параллельных insert/update). Шанс что она слетит весьма невысок (всего пару случает за последние 5 лет), если слетит — приложение её повторит (ибо будет знать об ошибке).
взяли бы, да и проверили, недолго же и недеструктивно
Не пустой изошник вышеупомянутой CenOS, в один поток — ~100 мкс, в восемь потоков — сюрприз, всего чуть-чуть выше — ~114 мкс. Или я где-то ошибся в расчётах? Проверьте у себя.
распараллеливание по чипам NAND контроллер сам делает, ничем вы ему тут не поможете
Вообще-то помогу, если отправлю сразу несколько команд на запись/чтение вместо одной, т.е. если он получит 8 команд на 8 блоков по 128K вместо одной на 1M, то внутри он может быстрее их обработать, если раскидает их по разным чипам, хотя, конечно, гарантии нет.
у постгреса 8 Кб, у остальных ЕМНИП цифры того же порядка
Ну он же не пишет каждый блок отдельно с fsync после каждого — а сразу пачкой, которая накапливается. Т.е. если транзакция на пару мегабайт, они и уйдут одним блоком (скорее всего, в любом случае не по одному с 8K каждый), а если commit_delay не особо низкий то и несколько транзакций из разных сессий упакует сразу, если окажутся рядом.
по вашим же ссылкам crystaldiskmark выдаёт 40 мегабайт в секунду в один поток
В один поток да, факт, но десяток потоков (которые пишут/читают в разные места) получат с высокой вероятностью по 100 мкс каждый.
У меня даже есть подозрение, что если взять условный блок в 1 MiB в приложении, разбить его на 8 по 128k каждый и отправить эти части асинхронно — то скорость получится быстрее чем если отправить его целиком, то есть можно обойти ограничение однопоточной производительности, если накопитель позволит.
Гугл особенно этим любит страдать: уехал в другой город, и почту уже не прочесть.
Вероятно, это не у всех (и/или не всегда) так. Я могу совершенно спокойно зайти на почту с адреса в США, при этом мой телефон находится в Германии — никаких вопросов и подозрений от гугла (кроме сообщения о том что "логин с нового устройства").
обычно база имеет общий журнал, куда пишется однопоточно и синхронно
Зависит от базы, но да, тут может быть тормоз. Правда, пишется он всё же обычно не маленькими блоками, и даже тут directio помогает. Но всё зависит от конкретных условий, да и при чтении выигрыш будет всё равно, а при наличии другой активности (вне базы) — и по записи. В случае если накопитель использует на хосте виртуалок — уж точно будет не очень плохо.
read latency существенно меньше 100 мкс я видел только на optane.
Для синхронной однопоточной или вообще? Потому что для асинхронной многопоточной он вполне выдает около 30 мкс, о чём говорят другие тесты — например тут или тут — в обоих около 30 мкс, и там сначала пишут потом читают.
Я повторил тест на большом файле который гарантированно без дыр (ISO c CentOS), получается около 100 мкс при iodepth=1 numjobs=1 bs=4k, но поскольку тут уже чуть-чуть замешана файловая система (даже при directio), не уверен что это совсем "чистый" результат:
Если вас интересует производительность накопителя, то вам нужен directio. Без него чтение (и запись без fsync) показывает производительность памяти минус оверхед на буферизацию прочие слои ввода-вывода (LVM, crypt etc). Запись с fsync почти соответствует directio только если fsync делает после каждой операции записи, хотя может быть добавлен оверхед на буфера и прочее.
Правдоподобный пример асинхронной многопоточной записи (не синхронной) — это собственно работа самой ОС и файловой системы — все приложения которые лезут на диск в рано или поздно потребуют обращения к накопителю, в зависимости от конкретного пути к нему, буферизации и т.п. всё скорее всего преобразуется в серию несвязанных запросов на операции которые свалятся накопителю в очередь, а он уже их попытается оптимизировать как может.
То есть, для конкретной базы — это однопоточная синхронная для одного процесса (соединения), но их обычно много — т.е. десяток клиентов могут обслуживаться накопителем одинаково хорошо, даже если лезут в разные части базы, хотя для каждого из них, конечно, могут быть ограничения в производительности.
Суть того о чём я говорил — несмотря на то что кажется что накопитель слаб в однопоточной синхронной записи, это необязательно будет сдерживающим фактором, разве что только в случае когда он используется ровно одним приложением.
Грубо говоря, если он даёт в одном потоке 100 MiB/s, то не факт что 10 клиентов получат каждый по 10 MiB — вполне может оказаться что каждый получит по 100 MiB/s или чуть меньше (но не в 10 раз).
Судя по своему опыту, паспортные показатели SSD (SATA, SAS и NVMe) накопители выдают либо на однопоточном синхронном доступе с большим размером блока (обычно 128-256K или выше), либо при многопоточном асинхронном с блоками небольшого размера (4-16K).
Вот пример с моего домашнего Pny CS3030 1T
, размер блока 4k, iodepth=8 numjobs=4, случайное чтение, directio:
Примерно столько же. Увы, на паспортный показатель выйти не удается с любыми параметрами (хотя система ни чем не загружена), разве что там MB а MiB. Причём что примечательно, если из последнего теста убрать directio и сбросить кэш (3 > drop_caches), то получим не очень приятные результаты:
Сразу видны накладные расходы на буферизацию и всё с ней связанное (сначала всё идёт в буфера и только потом в приложение, практически двойное копирование).
Как видите, прямой связи с производительностью накопителя нет, в том смысле что реальная производительность будет сильно зависеть от конкретных условий, размера памяти, политики буферизации и обработки очередей и ещё кучи факторов, но вот работа базы которая использует directio будет явно быстрее чем если будет использоваться обычный ввод-вывод (по крайней мере в моём случае).
Смысл как раз в том чтобы заменить fsync на directio, иначе вы тестируете не сам накопитель а все слои над ним, включая буферизацию.
Если хочется однопоточной производительности, ставьте уж тогда iodepth=1, зачем там 16?
Правда, не совсем понятно какой смысл тестировать однопоточную синхронную производительность если это не основной сценарий использования — устройство может плохо показать себя с QD1 но отлично — с QD4-32 (особенно NVMe), разница может быть на порядки.
Для тестов собственно накопителя лучше использовать --direct=1 в fio, fsync покажет результаты которые замылены системой, да и на чтение он не влияет (в вашем случае разница есть потому что это r/w тест, а не только чтение). И добавить --numjobs=4 (или сколько у вас там ядер) не помешает тоже.
Это основная его ценность — бесконтактные платежи, очень удобно потому что не нужно носить с собой пачку карт. Увы, с кастомными прошивками они не будут работать, там какая-то защита на уровне железа, в лучшем случае можно будет просто оплачивать покупки на Play Market.
Есть. Не 20 лет, но 17, к тому же это не просто дыра, это огромный кратер.
Можно и ещё нарыть, но найти такие дыры чисто изучением исходников очень непросто, особенно если не знать где искать.
Впрочем, надо отдать им должное — хоть и со скрипом, но ситуация с патчами улучшилась, а сами дыры… в продукте такой сложности, над которым работают тысячи разработчиков, неудивительно их наличие, а весьма широкая пользовательская база только способствует их нахождению — как злоумышленниками, так и самими пользователями.
Не забывайте --no-install-recommends и получите systemd без питона, равно как и php без кучи другого мусора, но вот без зависимости от perl обойтись очень трудно — посмотрите сколько пакетов его требуют, причём вовсе не как "рекомендуемый".
Настраивается, путём записи в /proc/$pid/oom_score_adj. Правда, если вы какой-то процесс настроите так чтобы он не убивался (это тоже возможно), но он сойдёт с ума и сожрёт всю память — может быть намного хуже.
И уж точно если вы делаете его неубиваемым — нужно обрабатывать ситуации когда он не может получить память, иначе это не имеет смысла (разве что он не содержит данных которые нужно сохранять).
Единственный способ предоставить процессу заведомо достаточное количество памяти — это жёстко её выделить изначально, а это не всегда целесообразно — к примеру, это может быть процесс который обычно требует 10 Мб для работы, но изредка ему нужно (на короткое время) 100/200/500 Мб — если в этот самый момент когда оно нужно памяти нет, всё же лучше это обработать (к примеру, приостановить работу пока не появится). Выделять ему сразу потенциальный максимум — это просто бесполезная трата ресурса, своп тоже не всегда имеется (или может быть медленным до непрактичности).
И как вы это сделаете без обработки ситуации "караул, память не дали"? К примеру, если у вас есть внутренние буфера, ещё не отправленные куда нужно с помощью write()/send()/etc, они банально пропадут, незакрытые транзакции БД могут откатится а могут и нет (зависит от БД и режима), и ещё много вещей которые зависнут в памяти приложения при жёстком завале, или даже просто внешнее состояние которое останется "грязным".
Если программа убивается извне, это тоже можно (и нужно) обрабатывать, кроме, разумеется, SIGKILL — но в последнем случае вы в принципе не сможете спроектировать приложение так чтобы данные не терялись пока эти данные хранятся в самом приложении, точнее, в выделенной ему памяти.
OOM киллер убивает не того кто просит память а того кто занимает больше всего памяти, при этом учитывая его важность для системы (OOM score).
Т.е. если ваш процесс не самый толстый в системе и к тому же имеет пониженный OOM score, то шанс его убийства довольно низок, хотя и остаётся открытым вопрос что случится если убитый процесс важен для работы вашего (или системы в целом).
В любом случае лучше попытаться (если возможно) аккуратно завершить работу в случае если память не дали (сбросить буфера, закрыть соединения, отменить транзакцию etc) чем просто умереть и потерять данные, на этот случай даже можно предусмотреть аварийный пул памяти (запрашиваемый в самом начале работы) которая может потребоваться на случай её нехватки в процессе нормальной работы.
Мысль приходит когда у вас 10+ терабайт данных — на RAID с SSD вы просто разоритесь, в то время как RAID на HDD вполне справляется с нагрузкой за более разумные деньги — бюджет не всегда безразмерен, особенно когда из своего кармана.
Чтобы буфер переполнился нужно загнать туда 30-60 гб одним куском — это уж очень нагруженная база получается (а вот тут можно вспомнить про commit_delay и размеры транзакций), хотя даже мой CS3030 пишет 500 MiB/s после заполнения кэша, что всё равно в два раза быстрее DC HDD.
Вероятно, для ряда задач всё же он есть, но не в общем.
Если мы перенесём эксперименты на HDD (не SSD) — разница проявится в полную силу. К сожалению, не везде и не всегда можно впихнуть SSD, к тому же не всегда они DC.
Независимо от скорости — два обращения к накопителю дороже одного, хотя бы потому что ядру нужно дождаться выполнения каждого по очереди (если пишет один процесс — а в postgres только один пишет в журнал, ибо всё должно быть последовательно). Насколько дороже — зависит от размера блока и их количества.
Видимо, я недостаточно ясно выразился — подразумевалось 8 команд параллельно, иначе смысл такого разбиения теряется, в вашем же варианте каждый поток читает их последовательно (причём гарантированно каждое чтение — как минимум одна команда накопителю, благодаря O_DIRECT). Причём, это было в контексте записи, хотя со чтением тоже помогает.
Примерно.
Каюсь, я был настолько "возмущен" наличем там for() что даже не стал анализировать pread(), оказалось — зря:
Вы в каждом блоке читаете
bs + offsetбайт, я думаю это не совсем то что нужно, если учесть чтоoffsetсильно зависит от номера потока и становится очень немаленьким, т.е. вы не читаете каждый кусок последовательно блоками одного размера — что и приводит к ужасной производительности, т.к. offset ненулевой для числа потоков > 1.Если убрать
offsetиз размера буфера, чтение в один поток медленнее (~ 0.64s) чем в более чем один (~ 0.54s).Но если вас интересуют "оригинальные" результаты (которые, на мой взгляд, лишены смысла из-за переменного размера блока зависящего от номера потока) — то получается всё те же ~ 0.64s для одного потока (ясный пень) но уже ощутимо медленнее для 2 (~ 0.81s) и уж тем более 8 (аж 2.40s).
Почитал (в начале, где с вероятностью 99% нет пустышек) — результаты не изменились.
Не могу представить за счёт чего. Если у меня есть 10 параллельных транзакций, то запись из по очереди с fsync будет по определению медленней чем всех сразу с одним fsync, на любом диске который изобретён, потому что записать много маленьких блоков всегда дороже чем один большой.
Выигрыша может не быть разве что на сравнительно маленьких транзакциях, до 1-2 мегабайт, тогда всё в кэш дисков валится, но у меня они бывают и в несколько десятков мегабайт.
Насчёт вашего super-fio есть пара замечаний:
pread(fd, buf, bs, offset);— и всё.К сожалению, время на чтение даже одного мегабайта уж очень мало в случае NVMe, так что сделал его 1 GiB и проверил — получилось 0.37s/0.24s (минимальное время) при одном потоке, и 0.35s/0.32s при четырех, но что примечательно — при одном потоке максимальное время было и около 1 секунды, при 4 и больше — оно стабильно 0.35s плюс-минус 0.02s из полусотни прогонов.
Итого, из 50 прогонов, (общее время всех прогонов, real time, система ничем не занята):
Я повторил эксперимент несколько раз, время отличается только для одного потока, остальные стабильно на показанном выше уровне. Хоть это и не кратное ускорение, но и условия не совсем честные (fork() вместо threads/aio), к тому же я читал файл а не устройство (но это вряд-ли существенно). Если использовать threads или aio, вероятно результаты станут лучше, с fork() я не совсем уверен что система реально отправляет запросы к устройству параллельно (а лезть в ebpf мне лень).
Опять-таки, если это будет запись вместо чтения, ситуация может существенно изменится в лучшую сторону — уверенности что оно читает из разных чипов нет, а писать скорее всего будет в разные (если будет время, поэксперементирую).
Правильный do_test():
И как я уже сказал раньше, блок у меня был в 1 GiB, при 4 MiB не хватало разрешения таймера (чтение идёт со скоростью около 2 GiB/s).
Не скажите, зависит от приложения. Несколько сотен update/insert и уже накапливается, а в зависимости от размеров полей и больше может. Даже один апдейт по сотне тысяч строк легко наберёт мегабайты, если вспомнить что внутри Postgres update = delete/insert.
Почему нет? Чем плохо совместить запись в журнал нескольких параллельных транзакций с одним fsync? Сто пудов два fsync на два блока по 1 MiB будут чуть-чуть медленней чем один на блок 2 MiB, а если блоки поменьше то выигрыш может быть существенным — на моей боевой прирост был около 30%, всего-то поднял delay до 100ms (там много параллельных insert/update). Шанс что она слетит весьма невысок (всего пару случает за последние 5 лет), если слетит — приложение её повторит (ибо будет знать об ошибке).
Проверяем:
Не пустой изошник вышеупомянутой CenOS, в один поток — ~100 мкс, в восемь потоков — сюрприз, всего чуть-чуть выше — ~114 мкс. Или я где-то ошибся в расчётах? Проверьте у себя.
Вообще-то помогу, если отправлю сразу несколько команд на запись/чтение вместо одной, т.е. если он получит 8 команд на 8 блоков по 128K вместо одной на 1M, то внутри он может быстрее их обработать, если раскидает их по разным чипам, хотя, конечно, гарантии нет.
Ну он же не пишет каждый блок отдельно с fsync после каждого — а сразу пачкой, которая накапливается. Т.е. если транзакция на пару мегабайт, они и уйдут одним блоком (скорее всего, в любом случае не по одному с 8K каждый), а если commit_delay не особо низкий то и несколько транзакций из разных сессий упакует сразу, если окажутся рядом.
В один поток да, факт, но десяток потоков (которые пишут/читают в разные места) получат с высокой вероятностью по 100 мкс каждый.
У меня даже есть подозрение, что если взять условный блок в 1 MiB в приложении, разбить его на 8 по 128k каждый и отправить эти части асинхронно — то скорость получится быстрее чем если отправить его целиком, то есть можно обойти ограничение однопоточной производительности, если накопитель позволит.
Не-не, логин успешный, приписка есть о том что "если это не вы, то...", и всё.
Вероятно, это не у всех (и/или не всегда) так. Я могу совершенно спокойно зайти на почту с адреса в США, при этом мой телефон находится в Германии — никаких вопросов и подозрений от гугла (кроме сообщения о том что "логин с нового устройства").
Зависит от базы, но да, тут может быть тормоз. Правда, пишется он всё же обычно не маленькими блоками, и даже тут directio помогает. Но всё зависит от конкретных условий, да и при чтении выигрыш будет всё равно, а при наличии другой активности (вне базы) — и по записи. В случае если накопитель использует на хосте виртуалок — уж точно будет не очень плохо.
Для синхронной однопоточной или вообще? Потому что для асинхронной многопоточной он вполне выдает около 30 мкс, о чём говорят другие тесты — например тут или тут — в обоих около 30 мкс, и там сначала пишут потом читают.
Я повторил тест на большом файле который гарантированно без дыр (ISO c CentOS), получается около 100 мкс при iodepth=1 numjobs=1 bs=4k, но поскольку тут уже чуть-чуть замешана файловая система (даже при directio), не уверен что это совсем "чистый" результат:
Не 30 мкс конечно, но всё же около 100.
Если вас интересует производительность накопителя, то вам нужен directio. Без него чтение (и запись без fsync) показывает производительность памяти минус оверхед на буферизацию прочие слои ввода-вывода (LVM, crypt etc). Запись с fsync почти соответствует directio только если fsync делает после каждой операции записи, хотя может быть добавлен оверхед на буфера и прочее.
Правдоподобный пример асинхронной многопоточной записи (не синхронной) — это собственно работа самой ОС и файловой системы — все приложения которые лезут на диск в рано или поздно потребуют обращения к накопителю, в зависимости от конкретного пути к нему, буферизации и т.п. всё скорее всего преобразуется в серию несвязанных запросов на операции которые свалятся накопителю в очередь, а он уже их попытается оптимизировать как может.
То есть, для конкретной базы — это однопоточная синхронная для одного процесса (соединения), но их обычно много — т.е. десяток клиентов могут обслуживаться накопителем одинаково хорошо, даже если лезут в разные части базы, хотя для каждого из них, конечно, могут быть ограничения в производительности.
Суть того о чём я говорил — несмотря на то что кажется что накопитель слаб в однопоточной синхронной записи, это необязательно будет сдерживающим фактором, разве что только в случае когда он используется ровно одним приложением.
Грубо говоря, если он даёт в одном потоке 100 MiB/s, то не факт что 10 клиентов получат каждый по 10 MiB — вполне может оказаться что каждый получит по 100 MiB/s или чуть меньше (но не в 10 раз).
Судя по своему опыту, паспортные показатели SSD (SATA, SAS и NVMe) накопители выдают либо на однопоточном синхронном доступе с большим размером блока (обычно 128-256K или выше), либо при многопоточном асинхронном с блоками небольшого размера (4-16K).
, размер блока 4k, iodepth=8 numjobs=4, случайное чтение, directio:
Теперь те же 4k но с iodepth/numbjobs 1:
Какие-то несчастные 100 MiB/s… А вот с блоком в 1M, iodepth/numjobs 1:
Т.е. мы получили те же почти 2 GiB/s что и 4k iodepth=8 numjobs=4. Теперь повторим 1M блок на iodepth=8 и numjobs=4:
Это уже почти близко к максимуму (по паспорту 3500 MB/s, я не уверен что имеется в виду MiB). Повторим с блоками по 4k, iodepth=16 numjobs=8:
Примерно столько же. Увы, на паспортный показатель выйти не удается с любыми параметрами (хотя система ни чем не загружена), разве что там MB а MiB. Причём что примечательно, если из последнего теста убрать directio и сбросить кэш (3 > drop_caches), то получим не очень приятные результаты:
Сразу видны накладные расходы на буферизацию и всё с ней связанное (сначала всё идёт в буфера и только потом в приложение, практически двойное копирование).
Как видите, прямой связи с производительностью накопителя нет, в том смысле что реальная производительность будет сильно зависеть от конкретных условий, размера памяти, политики буферизации и обработки очередей и ещё кучи факторов, но вот работа базы которая использует directio будет явно быстрее чем если будет использоваться обычный ввод-вывод (по крайней мере в моём случае).
Смысл как раз в том чтобы заменить fsync на directio, иначе вы тестируете не сам накопитель а все слои над ним, включая буферизацию.
Если хочется однопоточной производительности, ставьте уж тогда iodepth=1, зачем там 16?
Правда, не совсем понятно какой смысл тестировать однопоточную синхронную производительность если это не основной сценарий использования — устройство может плохо показать себя с QD1 но отлично — с QD4-32 (особенно NVMe), разница может быть на порядки.
Для тестов собственно накопителя лучше использовать
--direct=1в fio, fsync покажет результаты которые замылены системой, да и на чтение он не влияет (в вашем случае разница есть потому что это r/w тест, а не только чтение). И добавить--numjobs=4(или сколько у вас там ядер) не помешает тоже.И бесконтактные платежи в оффлайн-магазинах тоже работают?
Это основная его ценность — бесконтактные платежи, очень удобно потому что не нужно носить с собой пачку карт. Увы, с кастомными прошивками они не будут работать, там какая-то защита на уровне железа, в лучшем случае можно будет просто оплачивать покупки на Play Market.
Есть. Не 20 лет, но 17, к тому же это не просто дыра, это огромный кратер.
Можно и ещё нарыть, но найти такие дыры чисто изучением исходников очень непросто, особенно если не знать где искать.
Впрочем, надо отдать им должное — хоть и со скрипом, но ситуация с патчами улучшилась, а сами дыры… в продукте такой сложности, над которым работают тысячи разработчиков, неудивительно их наличие, а весьма широкая пользовательская база только способствует их нахождению — как злоумышленниками, так и самими пользователями.
Не забывайте --no-install-recommends и получите systemd без питона, равно как и php без кучи другого мусора, но вот без зависимости от perl обойтись очень трудно — посмотрите сколько пакетов его требуют, причём вовсе не как "рекомендуемый".