Pull to refresh

Comments 60

Например, потому что в операционной системе эффективно работает кэш файловой системы,

mysql, например, чтобы избежать двойного кэширования использует O_DIRECT

почему-то в компиляторе MSVC в режиме debug очень "тормозят" итераторы

Непроверенная догадка: может компилятор добавляет в сгенерированный код дополнительные проверки, чтобы ловить всякие внештатные ситуации и облегчать отладку?

Поэтому "тормозят" в кавычках. Да, отладка итераторов вещь полезная, но в моем проекте ненужная.

Зачем гадать, если можно просто взять и посмотреть в исходники?

Тут нет гаданий. По исходникам и нашел же, что bounds checking создают overhead. Так как я их проверяю сам, поэтому они мне не нужны.

А если маппить на память, будет быстрее или нет?

И вы нигде лицензию не указали, возможно это стоит исправить.

Да, спасибо про лицензию. Да, однозначно mmap и/или флаг O_DIRECT в open могут помочь, но это все же вызовы OS, а не C++. Поэтому на текущем этапе избегал (это часть требований).

Ну как бы да, а с другой стороны вы уже используете _fseeki64 так что код все равно прибит к одной ОС :)

На самом деле мне не очевидно даст ли mmap какой-либо выигрыш кроме по умолчанию не блокирующей выгрузки на диск, которую в БД все равно приходится избегать.

@tzlom прав, что код всё таки "прибит" к OS. Проблема в том, что std::fseek ограничена 4Gb, а _fseeki64() фича MSVC, fseeko64 - GNU C. Насколько мне известно, без выхода за пределы стандартного C++ 64-битное позиционирование сейчас не получить. Печалька. В будущем под win/linux напишу.

Вот тут чуваки в соавторстве с Andy Pablo говорят что MMAP для баз данных - плохая идея:
Are You Sure You Want to Use MMAP in Your Database Management System? https://www.cidrdb.org/cidr2022/papers/p13-crotty.pdf

Поэтому, только если IO_DIRECT

Отличная статья и ответ на мой вопрос, спасибо

скорость I/O накопителя (HDD/SSD) в сотни раз медленнее, чем работа с оперативной памятью (RAM)

А вот и нет. Скорость линейного чтения из DDR4 порядка 20-25GB/s. При этом вполне себе доступны NVMe SSD со скоростью чтения 6-7GB/s.

"DDR4 порядка 20-25GB/s"
С одного канала, одной планки. Обычно каналов, планок, в количестве от 2 до 8 и больше, соответственно и скорость в 2-8 раза больше. И если сравнивать с каким медленным SSD/SATA, то разница вполне может на сотню потянуть ;)

одной планки

Ну так и SSD можно несколько штук поставить.

И завести в raid, иначе скорость не вырастет.

А 7 ГБ/c с SSD в типичных нагрузках вы и сейчас не увидите. Увидите 70-100Мб/c в однопоток. Ну а ваши базы данных в однопоток и пишут и писать в 50 потоков, чтобы нагрузить SSD параллельной записью, просто не способны.

SSD одним потоком последовательно вполне пишут свои 7ГБ\с. Вот со случайным доступом уже всё несколько грустнее.

4k 1Q 1T: 106 Мб чтение и 365 Мб запись. И это на файле в 1ГБ, то есть не пробивая SLC-кеш на запись, а как пробьете, скорость на запись упадет в разы.

Если же говорить про 1M Q8 T1, то это фактически пишут пачками по 8 Мб и в SSD действует внутренний распараллеливающий механизм, то есть это для ОС запись последовательная, а по факту она параллельная и на файле в 1ГБ это вообще тест ОЗУ SSD. А надо писать так, чтобы если уж такие объемы записи, чтобы пробить SLC кеш диска и тогда у вас скорость тоже драматическим образом упадет.

Поэтому дайте нормальный бенчмарк на файле с 200 Гб или такого размера, чтобы пробить SLC-кеш конкретного SSD (и уж тем более его ОЗУ). Лучше всего fio.

и на файле в 1ГБ это вообще тест ОЗУ SSD

AFAIK нет, RAM SSD используется в первую очередь для хранения таблицы транслятора, что же до кэширования записи, оно происходит только в рамках формирования целых страниц (NAND просто не умеет писать за раз меньше, чем страницу, которая сегодня значительно больше сектора, которым оперирует ОС).


Поэтому дайте нормальный бенчмарк на файле с 200 Гб или такого размера, чтобы пробить SLC-кеш конкретного SSD (и уж тем более его ОЗУ).

держите. не 7, конечно, но всё равно немало
root@debian:~# fio -ioengine=libaio -sync=0 -direct=1 -name=test -bs=64k -iodepth=1 -rw=write -runtime=300 -filename=/dev/nvme3n1 -numjobs=1
test: (g=0): rw=write, bs=(R) 64.0KiB-64.0KiB, (W) 64.0KiB-64.0KiB, (T) 64.0KiB-64.0KiB, ioengine=libaio, iodepth=1
fio-3.25
Starting 1 process
Jobs: 1 (f=1): [W(1)][100.0%][w=2216MiB/s][w=35.5k IOPS][eta 00m:00s]
test: (groupid=0, jobs=1): err= 0: pid=665472: Wed Jan  4 02:45:51 2023
  write: IOPS=35.4k, BW=2213MiB/s (2321MB/s)(648GiB/300001msec); 0 zone resets
    slat (nsec): min=2359, max=53196, avg=2517.88, stdev=116.96
    clat (nsec): min=430, max=2151.0k, avg=25371.04, stdev=5988.31
     lat (usec): min=24, max=2153, avg=27.92, stdev= 5.99
    clat percentiles (usec):
     |  1.00th=[   25],  5.00th=[   25], 10.00th=[   25], 20.00th=[   25],
     | 30.00th=[   25], 40.00th=[   25], 50.00th=[   25], 60.00th=[   26],
     | 70.00th=[   26], 80.00th=[   27], 90.00th=[   27], 95.00th=[   28],
     | 99.00th=[   29], 99.50th=[   29], 99.90th=[   36], 99.95th=[   36],
     | 99.99th=[  108]
   bw (  MiB/s): min= 2179, max= 2227, per=100.00%, avg=2214.44, stdev= 9.85, samples=599
   iops        : min=34866, max=35644, avg=35430.98, stdev=157.65, samples=599
  lat (nsec)   : 500=0.01%
  lat (usec)   : 20=0.01%, 50=99.97%, 100=0.02%, 250=0.01%, 500=0.01%
  lat (usec)   : 750=0.01%, 1000=0.01%
  lat (msec)   : 2=0.01%, 4=0.01%
  cpu          : usr=6.14%, sys=9.53%, ctx=10624294, majf=0, minf=14
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.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.0%, 64=0.0%, >=64=0.0%
     issued rwts: total=0,10624239,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=1

Run status group 0 (all jobs):
  WRITE: bw=2213MiB/s (2321MB/s), 2213MiB/s-2213MiB/s (2321MB/s-2321MB/s), io=648GiB (696GB), run=300001-300001msec

Disk stats (read/write):
  nvme3n1: ios=46/10617848, merge=0/0, ticks=2/273002, in_queue=273004, util=100.00%

samsung pm9a3


на блоках 4К ожидаемо будет меньше (≈350), на блоках 1М — больше (≈3500).
и дело тут не только в распараллеливании записи со стороны накопителя, а и в накладных расходах на сисколы и т. п.

По тем данным, что я встречал, стоимость переключения контекста оценивается в 1 мксек. При 35к IOPS, если каждая операция IO приводит к переключению контекста, получаем 35k * 1 мксек = 0.035 сек - оверхед на переключение контекста в секунду на операции IO. Таким образом можно предположить, что сисколы имеют вклад в IOPS на уровне процентов и в разы влиять на IOPS не должны.

в разы влиять на IOPS не должны.

да, наверное, вы правы, проверил сейчас на той же машине fio блоками 4к на tmpfs — ≈700k iops (напомню, против ≈90к iops на nvme)

Нет, это не тест ОЗУ накопителя. Потому что есть гарантии по сохранности записанного в случае отключения электропитания. Если накопитель подтвердил завершение конкретной операции, это значит, что данные попали в энергонезависимую память.

Да, если писать много, то скорость падает, но у хороших накопителей это все еще порядка 1.5 Гб/сек

Не дочитал до этого. Прекрасно, т.е. fsync() там просто спихивает это всё в ОЗУ накопителя и в зависимости от его поведения уже дальше оно всё или пропадёт или нет.

Молодцы.

Там не написано, что fsync игнорируется. Написано, что не гарантируется очередность записи.

Ну в данном случае соглашусь, что запись первые 13% - это не ОЗУ, а видимо SLC-кеш, так как вряд ли на SSD ОЗУ 128 ГБ под кеш записи выделено.

Но:
1) нет никаких гарантий, что AIDA64 делает fsync в процессе бенча и если делает, то после скольки блоков она его делает?
2) SSD с конденсаторами при fsync сразу врут, что записали на энергонезависимую память, так как могут гарантировать запись за счет конденсаторов и тут рядом есть бенч на эту тему: https://habr.com/ru/post/708768/comments/#comment_25073958

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

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

Но этот бенч будет оценкой снизу, реальная нагрузка будет более оптимальна для распараллеливания.

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

только это не будет временем записи 4кб в nand.


мы пишем 4кб, а накопителю нужно сбросить целую страницу (16кб или больше), обновить таблицу транслятора и сбросить её изменение тоже (это опять минимум страница).


WAF получается очень большой, как и нагрузка на сборщик мусора.


По факту если взять скорость записи в ячейку, то видимо она сильно не выросла за эти годы, а прирост линейной скорости записи происходит за счет возможности параллельной записи

если про latency ещё можно сказать, что накопитель врёт, то именно про скорость записи не понимаю ваших претензий. у ssd очень небольшой буфер на запись, и он сбрасывает данные оттуда в nand именно с той скоростью, что мы видим в тесте.
ну а что высокая скорость достигается в том числе и за счёт многоканальности контроллера ssd — не вижу в этом ничего плохого.


кстати, о задержках работы с nand: если запись кэшируется, то на чтении задержки честные. и в топовых накопителях они тоже постепенно улучшаются.
если во времена sata ssd типичным было время доступа 100+ мкс, то сейчас у приличных datacenter nvme время доступа 50-70 мкс, а некоторые десктопные накопители выдают в тестах и чуть меньше 50 мкс

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

Опытным путем на своем SSD замерял, что лучшая скорость записи/чтения блоками 512Кб, а для HDD постраничный 8К (уже не 4Кб). Хотя все это зависит больше от самого железа.

что-то вы странное говорите. для hdd скорость линейного чтения вообще не зависит от размера блока и определяется плотностью записи и скоростью вращения диска.

Ну это надо именно бенчить, есть же еще ФС над HDD. Так по памяти где-то читал, что PostgreSQL пишет по 8к, а InnoDB по 16к.

Ну почему же. Данные могут писать в несколько потоков. А вот wal-журнал, да. В один поток.

Но все равно раз запись двойная, быстрее чем в один поток в wal не будет.
Да еще и частые fsync-и.

Да еще и частые fsync-и

не совсем в тему, но БД, всё-таки, это обычно datacenter ssd, на которых штраф за fsync околонулевой.

Сейчас нет данных под рукой, но по памяти штраф не нулевой даже у Optane. А есть бенч где нулевой?

sync=1, ≈11.2 мкс
root@debian:~# fio -ioengine=libaio -sync=1 -direct=1 -name=test -bs=4k -iodepth=1 -rw=randwrite -runtime=300 -filename=/dev/nvme3n1 -numjobs=1
test: (g=0): rw=randwrite, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=1
fio-3.25
Starting 1 process
Jobs: 1 (f=1): [w(1)][100.0%][w=336MiB/s][w=86.1k IOPS][eta 00m:00s]
test: (groupid=0, jobs=1): err= 0: pid=665989: Wed Jan  4 03:02:25 2023
  write: IOPS=85.7k, BW=335MiB/s (351MB/s)(98.0GiB/300001msec); 0 zone resets
    slat (nsec): min=1101, max=49936, avg=1205.22, stdev=144.39
    clat (nsec): min=230, max=114371, avg=9983.13, stdev=600.79
     lat (nsec): min=10065, max=115572, avg=11216.26, stdev=639.44
    clat percentiles (nsec):
     |  1.00th=[ 9664],  5.00th=[ 9664], 10.00th=[ 9664], 20.00th=[ 9792],
     | 30.00th=[ 9792], 40.00th=[ 9792], 50.00th=[ 9792], 60.00th=[ 9920],
     | 70.00th=[10048], 80.00th=[10176], 90.00th=[10304], 95.00th=[10432],
     | 99.00th=[11328], 99.50th=[12224], 99.90th=[18304], 99.95th=[18816],
     | 99.99th=[20096]
   bw (  KiB/s): min=305064, max=346504, per=100.00%, avg=342901.90, stdev=4658.80, samples=599
   iops        : min=76266, max=86626, avg=85725.42, stdev=1164.71, samples=599
  lat (nsec)   : 250=0.01%, 500=0.01%, 750=0.01%, 1000=0.01%
  lat (usec)   : 4=0.01%, 10=68.29%, 20=31.69%, 50=0.01%, 100=0.01%
  lat (usec)   : 250=0.01%
  cpu          : usr=10.61%, sys=16.50%, ctx=25697354, majf=0, minf=11
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.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.0%, 64=0.0%, >=64=0.0%
     issued rwts: total=0,25697868,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=1

Run status group 0 (all jobs):
  WRITE: bw=335MiB/s (351MB/s), 335MiB/s-335MiB/s (351MB/s-351MB/s), io=98.0GiB (105GB), run=300001-300001msec

Disk stats (read/write):
  nvme3n1: ios=46/25685523, merge=0/0, ticks=3/245296, in_queue=245299, util=100.00%

sync=0, ≈11.1 мкс
root@debian:~# fio -ioengine=libaio -sync=0 -direct=1 -name=test -bs=4k -iodepth=1 -rw=randwrite -runtime=300 -filename=/dev/nvme3n1 -numjobs=1test: (g=0): rw=randwrite, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=1
fio-3.25
Starting 1 process
Jobs: 1 (f=1): [w(1)][100.0%][w=338MiB/s][w=86.6k IOPS][eta 00m:00s]
test: (groupid=0, jobs=1): err= 0: pid=666062: Wed Jan  4 03:07:26 2023
  write: IOPS=86.3k, BW=337MiB/s (353MB/s)(98.8GiB/300001msec); 0 zone resets
    slat (nsec): min=1090, max=107487, avg=1182.30, stdev=143.24
    clat (nsec): min=230, max=66686, avg=9922.23, stdev=594.35
     lat (nsec): min=9885, max=118733, avg=11131.44, stdev=634.03
    clat percentiles (nsec):
     |  1.00th=[ 9664],  5.00th=[ 9664], 10.00th=[ 9664], 20.00th=[ 9664],
     | 30.00th=[ 9664], 40.00th=[ 9792], 50.00th=[ 9792], 60.00th=[ 9792],
     | 70.00th=[ 9920], 80.00th=[10048], 90.00th=[10176], 95.00th=[10432],
     | 99.00th=[11200], 99.50th=[12096], 99.90th=[18304], 99.95th=[18560],
     | 99.99th=[20096]
   bw (  KiB/s): min=267336, max=348400, per=100.00%, avg=345507.41, stdev=5421.34, samples=599
   iops        : min=66834, max=87100, avg=86376.73, stdev=1355.35, samples=599
  lat (nsec)   : 250=0.01%, 500=0.01%
  lat (usec)   : 2=0.01%, 4=0.01%, 10=75.96%, 20=24.02%, 50=0.01%
  lat (usec)   : 100=0.01%
  cpu          : usr=10.37%, sys=16.53%, ctx=25888992, majf=0, minf=12
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.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.0%, 64=0.0%, >=64=0.0%
     issued rwts: total=0,25889393,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=1

Run status group 0 (all jobs):
  WRITE: bw=337MiB/s (353MB/s), 337MiB/s-337MiB/s (353MB/s-353MB/s), io=98.8GiB (106GB), run=300001-300001msec

Disk stats (read/write):
  nvme3n1: ios=46/25875310, merge=0/0, ticks=3/245584, in_queue=245586, util=100.00%

всё тот же samsung pm9a3. на тех intel nvme, что тестировал, та же картина

А как вы думаете, как часто устанавливают дисковый raid при одной планке памяти. И ниже уже написали про latency, которая намного отличается.

только префетч не всегда можно устроить, а latency отличается на три порядка (50-100 нс против 50+ мкс)

С новым годом!

Всё хорошо, но то что код стайл будто какой-то неконсистентый по исходнику, мешает восприятию.

Согласен. Переучиваюсь на проекте с C и Java, на современный C++. Проскакивает местами

Шикарная работа. Очень шикарная.

А насчёт интерпретатора - не готов ответить на данный момент. Железо... Ограничения... Бюджеты...

  1. Отличная работа.
    А учитывая, что вы это делаете ради самообразования \ любви к искусству (программирования) - снимаю шляпу.

    ПС
    А транзакции планируются?
    Мне всегда казалось, что DBMS это что-то про гарантии консистентности данных (ну то есть по-простому начиная с транзакций).

Спасибо! ACID - хотелось бы реализовать, но пока не разобрался как это делается в NoSQL.

Обычно никак 😀

NoSQL чаще всего распределённые и там почти невозможно реализовать межшардовые транзакции. Есть, конечно, всякие NewSQL вроде Google Spanner, но там всё завязано на очень точную синхронизацию времени вроде бы.

А если вместо хэшмапы использовать битовые поля?
Для поиска бит можно использовать либо интринсики либо быстрые алгоритмы, например такой:
Индекс бита это номер страницы.
Индекс * размер страницы => смещение в mmap файле.

// возвращает размер _bits в 32 битных беззнаковых словах.
int size( bool units = true ); 

// получить индекс бита в 1
   int getMSB() {
      int r = -1;
#ifndef MACHINE_INSTRUCTIONS
      const char lt[ 256 ] =
      {
#define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n
         - 1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
         LT( 4 ), LT( 5 ), LT( 5 ), LT( 6 ), LT( 6 ), LT( 6 ), LT( 6 ),
         LT( 7 ), LT( 7 ), LT( 7 ), LT( 7 ), LT( 7 ), LT( 7 ), LT( 7 ), LT( 7 )
      };
      register quint32 t, tt, v;
      for( int i = size( true ) - 1; i >= 0; i-- ) {
         v = _bits[ i ];
         if( v ) {
            if( tt = v >> 16 ) {
               r = ( t = tt >> 8 ) ? 24 + lt[ t ] : 16 + lt[ tt ];
            }
            else {
               r = ( t = v >> 8 ) ? 8 + lt[ t ] : lt[ v ];
            }
            return ( i * 32 + r );
         }
      }
#else
      register unsigned long index;
      register quint32 v;
      for( int i = size( true ) - 1; i >= 0; i-- ) {
         v = _bits[ i ];
         if( v ) {
            if( _BitScanReverse( &index, v ) ) {
               return i * ( sizeof( quint32 ) * 8U ) + index;
            }
         }
      }
#endif
      return r;
   }

   // количество установленных бит
   int count() {
      register quint32 v = 0, c = 0;
      register int r = 0, i = 0, sizeInWords = size( true );
#ifndef MACHINE_INSTRUCTIONS
      for( ; i < sizeInWords; i++ ) {
         v = _bits[ i ];
         v = v - ( ( v >> 1 ) & 0x55555555 );
         v = ( v & 0x33333333 ) + ( ( v >> 2 ) & 0x33333333 );
         c = ( ( v + ( v >> 4 ) & 0xF0F0F0F ) * 0x1010101 ) >> 24;
         r += c;
      }
#else
      Q_UNUSED( v );
      Q_UNUSED( c );
      for( ; i < sizeInWords; i++ ) {
         r += __popcnt( _bits[ i ] );
      }
#endif
      return r;
   }

   // set bit
   // index >= 0 && < sizeInBits -- set bit by index
   // otherwise -- set all bits
   void set( int index = -1 ) {
      if( index >= 0 ) {
         if( index < size() )
            _bits[ index / ( sizeof( quint32 ) * 8U ) ] |= ( 1U << ( index % ( sizeof( quint32 ) * 8U ) ) );
      }
      else {
         _bits.fill( ~( 0U ) );
      }
   }
   // clear bit
   // index >= 0 && < sizeInBits -- clear bit by index
   // otherwise -- clear all bits
   void clear( int index = -1 ) {
      if( index >= 0 ) {
         if( index < size() ) {
            quint32 bit = ( 1U << ( index % ( sizeof( quint32 ) * 8U ) ) );
            int chunk = index / ( sizeof( quint32 ) * 8U );
            _bits[ chunk ] |= bit;
            _bits[ chunk ] ^= bit;
         }
      }
      else {
         _bits.fill( 0 );
      }
   }

   //index >= 0 && < sizeInBits -- is bit set by index?
   // otherwise -- is all bits set?
   bool isSet( int index = -1 ) {
      if( index >= 0 ) {
         if( index < size() )
            return _bits[ index / ( sizeof( quint32 ) * 8U ) ] & ( 1U << ( index % ( sizeof( quint32 ) * 8U ) ) );
      }
      else {
         for( int i = 0, sizeInWords = size( true ); i < sizeInWords; i++ ) {
            if( _bits[ i ] != ~( 0U ) ) {
               return false;
            }
         }
         return true;
      }
      return false;
   }

   //index >= 0 && < sizeInBits -- is bit clear by index?
   // otherwise -- is all bits clear?
   bool isClear( int index = -1 ) {
      if( index >= 0 ) {
         if( index < size() )
            return !( _bits[ index / ( sizeof( quint32 ) * 8U ) ] & ( 1U << ( index % ( sizeof( quint32 ) * 8U ) ) ) );
      }
      else {
         for( int i = 0, sizeInWords = size( true ); i < sizeInWords; i++ ) {
            if( _bits[ i ] ) {
               return false;
            }
         }
         return true;
      }
      return false;
   }

Возможно, в операции read если position кратен PAGE_SIZE и length равна PAGE_SIZE, то вы будете искать/читать 2 страницы вместо 1.

Возможно, вы очень оптимистичны, считая, что разница steady_timer time_point выдаёт наносекунды. Желательно использовать duration_cast (https://en.cppreference.com/w/cpp/chrono/duration/duration_cast);

Для хобби, возможно, это некритично:

[наверное что-то просмотрел и скопировать класс нельзя, но] лучше явно запретить копирование, а вот перенос можно бы и сделать.

гарантии на исключения вообще никакие. У вас bad_alloc может вылетать и ... ничего.

Спасибо за советы! Про read страниц проверю, а вот про время duration_cast не знал. По исключениям подумаю как обрабатывать.

Спасибо огромное за review! Действительно, страницы читались дважды при position кратном PAGE_SIZE и length равна PAGE_SIZE. Поправил. И касательно копирования - запретил. Обработку bad_alloc добавил.

в 2000 году был мне заказ сделать обработку данных на примере одной программы написанной очень крутым (для меня) программистом, который, по слухам, уехал в США и работал уже в Майкрософт.

Я знал только Delphi и LocalBDE и поэтому делал реализацию на них. У него проект был на C+WinAPI+какой-то бинарный формат БД.

Больше всего была разница в объемах файлов БД, у меня месяц работы выражался в 2-3 мегабайт (не гигабайт) данных, для транспортировки на одной дискете использовал ZIP, выгрузка сразу шла в архив при экспорте. А у гуру в прошлом проекте данные за год умещались в 200 килобайт и плохо паковались, подозреваю что он сам всё сжимал в какой-то LZ вариант. Тогда я понял, что мне не стать хорошим программистом XD (и не стал). А смотря на поделки современных разработчиков уверен что сделал бы отличную карьеру если бы пошел этим путём, ибо сегодня говнокодеры в почёте.

что-то я сомневаюсь, что самописная БД в 2000 году — признак хорошего программиста

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

Первая версия SQLite появилась как раз в 2000 году. До неё со встраивыми СУБД было как-то не очень. Кто-то писал в пачку *.dbf, кто-то (ICQ, например) использовал формат MS Access.

LocalBDE я как раз использовал с DBF. SQLite знаю только название, но никогда не пользовался. Был доступен Cache еще, но в тестовом режиме, платная была. После DOS Clipper/FoxPro мне dbf были проще в работе.

Олдскулы свело, Clipper 🥲

Sign up to leave a comment.

Articles

Change theme settings