Обновить
17
Павел Килин@borodyadka

Нажиматель кнопок

2
Подписчики
Отправить сообщение

У меня в домашнем медиа-сервере стоят 4 рефабнутых видеорегистраторных диска, объединённые в зеркальный zpool. Подключены через nvme sata райзер.

Вот бенчи погонял только что

Последовательная запись (16ГБ):

$ sync; fio --filename=testfile-seq-16g --size=16G --direct=1 --rw=write --bs=1M --ioengine=libaio --numjobs=4 --iodepth=32 --name=seq-write-test --group_reporting --ramp_time=4
seq-write-test: (g=0): rw=write, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=32
...
fio-3.39
Starting 4 processes
seq-write-test: Laying out IO file (1 file / 16384MiB)
Jobs: 2 (f=2): [_(1),W(2),_(1)][99.2%][w=84.0MiB/s][w=84 IOPS][eta 00m:04s]
seq-write-test: (groupid=0, jobs=4): err= 0: pid=3789: Tue Jul 14 08:29:22 2026
  write: IOPS=126, BW=127MiB/s (133MB/s)(63.7GiB/514756msec); 0 zone resets
    slat (msec): min=2, max=856, avg=31.43, stdev=25.50
    clat (usec): min=2, max=2376.8k, avg=973220.09, stdev=292270.62
     lat (msec): min=2, max=2403, avg=1004.64, stdev=298.69
    clat percentiles (msec):
     |  1.00th=[   90],  5.00th=[  355], 10.00th=[  558], 20.00th=[  793],
     | 30.00th=[  885], 40.00th=[  953], 50.00th=[ 1011], 60.00th=[ 1070],
     | 70.00th=[ 1133], 80.00th=[ 1200], 90.00th=[ 1284], 95.00th=[ 1368],
     | 99.00th=[ 1519], 99.50th=[ 1653], 99.90th=[ 2022], 99.95th=[ 2056],
     | 99.99th=[ 2265]
   bw (  KiB/s): min=24576, max=1095680, per=100.00%, avg=130460.16, stdev=16545.53, samples=4084
   iops        : min=   24, max= 1070, avg=127.00, stdev=16.15, samples=4084
  lat (usec)   : 4=0.01%
  lat (msec)   : 4=0.01%, 20=0.01%, 50=0.02%, 100=1.01%, 250=1.49%
  lat (msec)   : 500=6.03%, 750=9.05%, 1000=29.99%, 2000=52.48%, >=2000=0.12%
  cpu          : usr=0.05%, sys=4.04%, ctx=470862, majf=0, minf=149
  IO depths    : 1=0.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=100.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.1%, 64=0.0%, >=64=0.0%
     issued rwts: total=0,65110,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=32

Run status group 0 (all jobs):
  WRITE: bw=127MiB/s (133MB/s), 127MiB/s-127MiB/s (133MB/s-133MB/s), io=63.7GiB (68.4GB), run=514756-514756msec

Последовательное чтение (16ГБ):

$ sync; fio --filename=testfile-seq-16g --direct=1 --rw=read --bs=1M --ioengine=libaio --numjobs=4 --iodepth=32 --name=seq-read-test --group_reporting --readonly --ramp_time=4
seq-read-test: (g=0): rw=read, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=32
...
fio-3.39
Starting 4 processes
Jobs: 4 (f=4): [R(4)][98.0%][r=395MiB/s][r=395 IOPS][eta 00m:04s]
seq-read-test: (groupid=0, jobs=4): err= 0: pid=3806: Tue Jul 14 08:33:32 2026
  read: IOPS=332, BW=333MiB/s (349MB/s)(63.0GiB/193527msec)
    slat (usec): min=980, max=140089, avg=12024.84, stdev=7783.81
    clat (nsec): min=1643, max=537729k, avg=372582032.49, stdev=38765449.55
     lat (usec): min=1229, max=547243, avg=384614.85, stdev=39514.38
    clat percentiles (msec):
     |  1.00th=[  209],  5.00th=[  326], 10.00th=[  338], 20.00th=[  351],
     | 30.00th=[  359], 40.00th=[  368], 50.00th=[  376], 60.00th=[  380],
     | 70.00th=[  388], 80.00th=[  401], 90.00th=[  414], 95.00th=[  426],
     | 99.00th=[  451], 99.50th=[  464], 99.90th=[  493], 99.95th=[  502],
     | 99.99th=[  518]
   bw (  KiB/s): min=262144, max=712704, per=99.86%, avg=340763.63, stdev=9163.37, samples=1546
   iops        : min=  256, max=  696, avg=332.75, stdev= 8.95, samples=1546
  lat (usec)   : 2=0.01%, 4=0.01%
  lat (msec)   : 2=0.01%, 4=0.01%, 10=0.01%, 20=0.01%, 50=0.03%
  lat (msec)   : 100=0.03%, 250=1.52%, 500=98.52%, 750=0.06%
  cpu          : usr=0.02%, sys=0.44%, ctx=66816, majf=0, minf=149
  IO depths    : 1=0.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=100.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.1%, 64=0.0%, >=64=0.0%
     issued rwts: total=64362,0,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=32

Run status group 0 (all jobs):
   READ: bw=333MiB/s (349MB/s), 333MiB/s-333MiB/s (349MB/s-349MB/s), io=63.0GiB (67.6GB), run=193527-193527msec

Случайная запись (4ГБ):

$ sync; fio --filename=testfile-rand-4g --size=4G --direct=1 --rw=randwrite --bs=4k --ioengine=libaio --numjobs=4 --iodepth=32 --name=rand-write-test --group_reporting --ramp_time=4 --time_based --runtime=300
rand-write-test: (g=0): rw=randwrite, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=32
...
fio-3.39
Starting 4 processes
rand-write-test: Laying out IO file (1 file / 4096MiB)
Jobs: 4 (f=4): [w(4)][100.0%][w=10.6MiB/s][w=2711 IOPS][eta 00m:00s]
rand-write-test: (groupid=0, jobs=4): err= 0: pid=3832: Tue Jul 14 08:41:19 2026
  write: IOPS=4784, BW=18.7MiB/s (19.6MB/s)(5608MiB/300008msec); 0 zone resets
    slat (usec): min=2, max=379403, avg=832.71, stdev=5748.24
    clat (nsec): min=1213, max=552035k, avg=25915667.12, stdev=45154826.86
     lat (usec): min=181, max=570060, avg=26748.44, stdev=46046.12
    clat percentiles (usec):
     |  1.00th=[   578],  5.00th=[   652], 10.00th=[   725], 20.00th=[  3884],
     | 30.00th=[  6456], 40.00th=[  8717], 50.00th=[ 10552], 60.00th=[ 13435],
     | 70.00th=[ 18482], 80.00th=[ 28443], 90.00th=[ 69731], 95.00th=[123208],
     | 99.00th=[223347], 99.50th=[270533], 99.90th=[371196], 99.95th=[400557],
     | 99.99th=[438305]
   bw (  KiB/s): min= 2235, max=56976, per=100.00%, avg=19375.13, stdev=2413.96, samples=2308
   iops        : min=  557, max=14244, avg=4842.74, stdev=603.56, samples=2308
  lat (usec)   : 2=0.01%, 4=0.01%, 250=0.01%, 500=0.04%, 750=11.32%
  lat (usec)   : 1000=3.05%
  lat (msec)   : 2=1.08%, 4=4.85%, 10=26.61%, 20=25.44%, 50=14.73%
  lat (msec)   : 100=6.10%, 250=6.10%, 500=0.68%, 750=0.01%
  cpu          : usr=0.23%, sys=3.31%, ctx=1023281, majf=0, minf=156
  IO depths    : 1=0.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=100.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.1%, 64=0.0%, >=64=0.0%
     issued rwts: total=0,1435504,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=32

Run status group 0 (all jobs):
  WRITE: bw=18.7MiB/s (19.6MB/s), 18.7MiB/s-18.7MiB/s (19.6MB/s-19.6MB/s), io=5608MiB (5880MB), run=300008-300008msec

Случайное чтение (4ГБ):

$ sync; fio --filename=testfile-rand-4g --direct=1 --rw=randread --bs=4k --ioengine=libaio --numjobs=4 --iodepth=32 --name=rand-read-test --group_reporting --readonly --ramp_time=4 --ru
ntime=300
rand-read-test: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=32
...
fio-3.39
Starting 4 processes
Jobs: 4 (f=3): [r(3),f(1)][4.1%][r=2424KiB/s][r=606 IOPS][eta 01h:58m:33s]
rand-read-test: (groupid=0, jobs=4): err= 0: pid=3860: Tue Jul 14 08:47:47 2026
  read: IOPS=569, BW=2279KiB/s (2334kB/s)(668MiB/300010msec)
    slat (usec): min=2, max=57136, avg=7022.26, stdev=4286.66
    clat (nsec): min=1523, max=338880k, avg=217672103.30, stdev=23987146.84
     lat (msec): min=7, max=348, avg=224.69, stdev=24.37
    clat percentiles (msec):
     |  1.00th=[  165],  5.00th=[  180], 10.00th=[  188], 20.00th=[  199],
     | 30.00th=[  205], 40.00th=[  211], 50.00th=[  218], 60.00th=[  224],
     | 70.00th=[  230], 80.00th=[  239], 90.00th=[  249], 95.00th=[  257],
     | 99.00th=[  275], 99.50th=[  284], 99.90th=[  300], 99.95th=[  309],
     | 99.99th=[  326]
   bw (  KiB/s): min= 1704, max= 2800, per=99.94%, avg=2278.04, stdev=41.96, samples=2398
   iops        : min=  426, max=  700, avg=569.47, stdev=10.49, samples=2398
  lat (usec)   : 2=0.01%, 4=0.01%
  lat (msec)   : 10=0.01%, 20=0.01%, 50=0.01%, 100=0.02%, 250=91.21%
  lat (msec)   : 500=8.83%
  cpu          : usr=0.03%, sys=0.33%, ctx=159930, majf=0, minf=149
  IO depths    : 1=0.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=100.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.1%, 64=0.0%, >=64=0.0%
     issued rwts: total=170838,0,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=32

Run status group 0 (all jobs):
   READ: bw=2279KiB/s (2334kB/s), 2279KiB/s-2279KiB/s (2334kB/s-2334kB/s), io=668MiB (700MB), run=300010-300010msec

Случайное чтение показало себя хуже всего. Я бы даже сказал чудовищно плохо. Но, если верить статье, то это как раз специфика такого рода дисков.

Команды взял отсюда, результаты мои.

Бенчи не совсем честные (или совсем нечестные), т.к. бенчится зеркальный пул, но дисков вне пула у меня сейчас нет.

Это я к тому, что такие диски нормально себя показывают при обычном использовании: киношки, торренты, всякое медиа. Понятное дело, для современных игр и работы с видео они не подойдут.

И ещё, мои диски довольно старые, у них очень большой пробег и они дико стучат бошками при нагрузке. Без нагруки иногда постукивают. Раньше я гадал из-за чего это может быть, но статья, вроде бы, ответила на этот вопрос.

Я воспользовался поводом, заблокировал все новые поступления

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

Я уже думаю воспользоваться поводом и вообще перестать читать хабр. Большая часть статей здесь это нейрослоп, маркетинговый буллшит и реклама говёных курсов или тг-каналов. Желания читать это нет, желание писать что-то сюда тоже давно пропало.

Уже ответили про ленту.

А можно какой-нибудь срок по фиксу этого, а то за неделю 600+ статей прилетело, 99% которых должны были быть отфильтрованы.

Извиняюсь за оффтоп, но может здесь хотя бы обратят внимание. На прошлой неделе сломали личную RSS ленту и теперь в неё сваливается вообще всё, даже от заблокированных юзеров. Поддержка даже не отвечает.

P.S. плюсанул, идея в посте нравится.

Не все платные. llama3, например, вполне можно локально запустить у себя через ollama (там ещё куча других моделей есть)

Так почему бы не вынести драйвера в отдельный пакет?


import (
  "path.to/your/lib"
  "path.to/your/lib/drivers/postgres"
  // or
  "path.to/your/lib/drivers/json"
  // ...
)

Насколько помню, будут скачиваться только те зависимости, которые явно использованы.

Раньше активно его использовал, как раз когда пользовался express'ом. У тайпорма хорошая интеграция с nestjs, поэтому такая связка мне больше нравится. Ну и аннотации выглядят гораздо чище, чем возня с sequelize.define.

NestJS + TypeORM. Полгода использую, в основном положительные впечатления. Про Express не вспоминаю даже.

Большое спасибо за ответ. Но дело в том, что я не захожу на главную, а перехожу из RSS. К тому же, я часто читаю с мобильных устройств, а там не в каждый браузер можно поставить адблок. Наверное, придётся как-то фильровать самому по тэгам, которые можно получить из фида.

А отписка от надоедливых хабов/компаний так и не появится? Я клянчил её почти два года назад, но мне даже не ответили.

Когда вызывается return или мы выходим из генератора, то всё равно возвращается Promise, содержащий IteratorResult. Вот пример с кодом:


async function* values () {
  yield 1
  yield 2
}

function handle () {
  const iter = values()
  console.log(iter.next()) // Promise { value: 1, done: false }
  console.log(iter.next()) // Promise { value: 2, done: false }
  // следующего элемента нет
  console.log(iter.next()) // Promise { value: undefined, done: true }
}
handle()

Т.е. последний промис разрешается сразу и возвращает done = true. Вы можете запустить этот код и проверить самостоятельно.


Зачем тут чушь пишите?

В каком месте я написал чушь?

А если сильно извратиться — можно ли чисто теоретически снаружи повлиять на наш генератор

Да, вы можете в next() передать какое-нибудь значение:


function* generator () {
  let value = yield 'Hi!'
  console.log('Hello %s!', value)
}

const iterator = generator()
console.log(iterator.next().value) // выведет "Hi!"
iterator.next('World') // выведет "Hello World!"

Таким образом можно передать генератору что угодно.

А снаружи-то как вовремя узнать что больше элементов не будет?

Снаружи "вовремя" будет тогда, когда зарезолвится промис, т.к. пока это не произойдёт ваш цикл не сможет пойти дальше. А когда промис резолвится, то он возвращает состояние итератора, где указано завершился итератор или нет.


Чему будет равно свойство done итератора во время выполнения оператора await в генераторе?

Оно будет равно false. Станет равным true лишь при выходе из функции-генератора явно (по return) или неявно (кончилось тело функции).

Сейчас и для обычных итераторов их нельзя применить. Для этого сначала нужно преобразовать в массив (с помощью Array.from или spread оператора [ ...iterable ]), а потом над ним уже совершать операции.
В качестве эксперимента я пишу библиотеку, которая добавляет эти методы прямо к итератору (изменяет его прототип, как делает SugarJS), но она далека от завершения, к тому же есть множество более качественных альтернатив: Wu, Lazy.JS и т.д.

Давайте избавимся от for-await-of и посмотрим как это можно обработать вручную. Допустим, у нас есть удалённая очередь queue с несколькими элементами.
Пример для IteratorResult<Promise>:


const queue = ... // queue это итератор
while (true) {
  const { value, done } = queue.next()
  // done всегда будет false, т.к. из генератора синхронно(!) мы не можем узнать закончилась ли очередь
  const result = await value // здесь мы дожидаемся разрешения value
  if (result === null) { break } // вот здесь нужно проверить, что нам вернулось пустое значение. Но дело в том, что null может быть вполне валидным значением, а не индикатором пустой очереди
  // обрабатываем  result; следующая итерация
}

Как видите, этот подход имеет недостаток — у нас нет четкого понимания, что очередь пуста. Мы не можем трактовать null как конец, если только не приняли некое соглашение, что null — это всегда конец очереди.


В случае с Promise<IteratorResult> всё несколько иначе:


const queue = ...
while (true) {
  const { value, done } = await queue.next() // здесь у нас есть Promise, который возвращает текущее состояние итератора
  if (done) { break } // и есть четкое понимание когда стоит прекратить цикл
  // обрабатываем value; следующая итерация
}

Т.е. при подходе Promise<IteratorResult> у нас есть возможность без всяких соглашений четко дать понять, что очередь пуста, можно выходить из цикла. queue, например, может при каждом вызове next() помимо получения элемента спрашивать у очереди сколько элементов осталось и при значении 0 вернуть done = true, чтобы прервать цикл и не создать последующих запросов.

Судя по всему, поддержку недавно добавили в V8. Не использую Chrome, так что не могу проверить.

Для этого придуман Git LFS.

php/python/ruby etc. сами по себе интерпретируемые языки

неправда. Они компилируются в байткод, который потом исполняется виртуальной машиной.


умудряются напихать каких-то бешеных абстракций

которые нужны, чтобы код от разных вендоров работал "искаропки" и имел одинаковые интерфейсы.


ORM — «птичий» язык доступа к БД, зачем когда есть SQL

затем, чтобы вы могли выбрать данные из одной СУБД (например MySQL), "сджойнить" их с данными из другой (например MongoDB, мне это приходилось делать), закешировать на некоторое время, и всё это без кучи бойлерплейт кода каждый раз. Для любителей SQL есть Query Builder'ы, которые генерят запросы для разных диалектов SQL.


Шаблонизаторы — еще один птичий язык

а что вы предлагаете взамен? Только PHP, насколько я знаю, позволяет инлайнить код прямо в HTML, остальным языкам (React не рассматриваем) нужны шаблонизаторы. Хорошо, что есть широкоиспользуемые языки для шаблонов (Mustache, Jade/Pug и т.д.), которые позволяют не учить 100500 разных синтаксисов, а везде делать одинаково (имел несчастье работать с angular1 на фронте и php на бэкенде, один шаблонизатор немного сгладил боль).


переаллокации памяти и перекачивание данных между буферами

оверхед неизбежен, но это плата за скорость разработки.


как работает веб сервер — активизируется заново для каждого обращения

это не так. Какой-нибудь apache ещё может порождать по процессу на запрос, но, к счастью, к нему прибегают всё реже. nginx, например, имеет пул процессов, которые обрабатывают запросы пользователей. Здесь можно мне возразить, что процесс всё же порождается, но только это не веб-сервер, а какой-нибудь PHP-интерпретатор. Да, это так, но только если вы не используете fastCGI. В остальных языках чаще используется какой-нибудь встроенный в веб-фреймворк (Node.JS, Ruby Unicorn и т.д.) HTTP сервер (хотя мне тоже такое решение не особо нравится).


на Си — вполне разумное решение

нет. Это очень опасное занятие. Даже матёрые программисты допускают глупые ошибки, которые могут очень дорого обойтись. Все эти "интерпретируемые языки" зачастую неплохо отлажены и не имеют таких багов, по крайней мере, их сложнее эксплуатировать. Если хотите убер-скорость, но чтобы было безопасно, взгляните на Rust, с его zero-cost abstractions, и какой-нибудь веб-фреймворк для него или на Go и его решения.


P.S. Сам я веб-разработчик и сейчас, как раз, изучаю Rust, но всё равно не стал бы на нём писать веб.

Информация

В рейтинге
5 272-й
Откуда
Россия
Зарегистрирован
Активность