Spec-Zone.ru › SQLite

Измерение и снижение использования ЦП в SQLite

Содержание
1. Обзор
2. Измерение производительности
2.1. Параметры компиляции
2.2. Рабочая нагрузка
2.2.1. Обновление от 2026-01-06
2.3. Измерение производительности
2.4. Микрооптимизации
3. Процесс измерения производительности
4. Ограничения
5. Почему график заканчивается в 2025 году?

1. Обзор

На графике ниже показано количество тактов ЦП, затраченных SQLite на стандартную рабочую нагрузку, для версий SQLite с 3.6.1 (2008-08-06) по 3.48.0 (2025-01-14), то есть за период около 16,4 года. За это время количество затраченных тактов ЦП снизилось примерно с 3,376 миллиона до 0,965 миллиона. Таким образом, к 2025 году SQLite требовалось лишь около 29% от количества тактов ЦП, которое было необходимо в 2008 году.

В этой статье рассказывается о том, как разработчики SQLite измеряют использование ЦП, что именно означают эти измерения и какие методы применяют разработчики SQLite, продолжая стремиться к дальнейшему снижению использования ЦП библиотекой SQLite.


Измерения выполнены с помощью cachegrind в Ubuntu 16.04 на x64 с gcc 5.4.0 и -Os.

2. Измерение производительности

Кратко, производительность SQLite с точки зрения ЦП измеряется следующим образом:

  1. Скомпилировать SQLite в конфигурации, поставляемой по умолчанию, без каких-либо специальных параметров телеметрии или отладки.
  2. Связать SQLite с тестовой программой, выполняющей около 30 000 операторов SQL, представляющих типичную рабочую нагрузку.
  3. Подсчитать количество использованных тактов ЦП с помощью cachegrind.

2.1. Параметры компиляции

Для измерения производительности SQLite компилируется примерно так же, как и для использования в производственных системах. Конфигурация во время компиляции является «приблизительной» в том смысле, что в каждом конкретном случае использования SQLite в производственной среде она отличается. Параметры компиляции, используемые одной системой, не обязательно совпадают с параметрами других систем. Важно то, что мы избегаем параметров, существенно влияющих на генерируемый машинный код. Например, параметр -DSQLITE_DEBUG опущен, поскольку он добавляет тысячи операторов assert() в критически важные для производительности участки библиотеки SQLite. Параметр -pg (для GCC) также опущен, поскольку он заставляет компилятор генерировать дополнительный код для вероятностного измерения производительности, мешающий фактическим измерениям.

Для измерения производительности используется параметр -Os (оптимизация размера), а не -O2, поскольку параметр -O2 вызывает настолько значительное перемещение кода, что становится трудно сопоставлять конкретные инструкции ЦП со строками исходного кода на C.

2.2. Рабочая нагрузка

«Типичная» рабочая нагрузка генерируется программой speedtest1.c из канонического дерева исходного кода SQLite. Эта программа предназначена для проверки библиотеки SQLite способом, типичным для реальных приложений. Разумеется, все приложения различаются, поэтому ни одна тестовая программа не может в точности воспроизвести поведение всех приложений.

Программа speedtest1.c время от времени обновляется по мере того, как меняются представления разработчиков SQLite о том, что считать «типичным» использованием.

Для запуска программы speedtest1.c используется сценарий командной оболочки speed-check.sh, который также находится в каноническом дереве исходного кода. Чтобы повторить измерения производительности, соберите следующие файлы в одном каталоге:

  • сценарий «speed-check.sh»,
  • тестовую программу «speedtest1.c» и
  • файлы исходного кода амальгамации SQLite «sqlite3.c» и «sqlite3.h»

Затем выполните «sh speed-check.sh trunk».

2.2.1. Обновление от 2026-01-06

Эта статья была впервые написана 2016-12-08. Сценарий speed-check.sh больше не используется. Был создан новый сценарий speedtest.tcl, который применяется с начала 2025 года: speedtest.tcl. Подробности см. в документации speedtest.md. В этой статье по-прежнему упоминается старый сценарий измерения производительности в качестве исторической справки.

2.3. Измерение производительности

Для измерения производительности используется Cachegrind, поскольку он дает результаты, воспроизводимые с точностью до 7 и более значащих цифр. Для сравнения, фактическое время выполнения (по часам) редко бывает воспроизводимым с точностью более одной значащей цифры.

2.4. Микрооптимизации

Высокая воспроизводимость результатов cachegrind позволяет разработчикам SQLite реализовывать и измерять «микрооптимизации». Микрооптимизация — это изменение кода, обеспечивающее очень небольшое повышение производительности. Типичные микрооптимизации сокращают количество тактов ЦП на 0,1%, 0,05% или даже меньше. Такие улучшения невозможно измерить по времени выполнения в реальных условиях. Но сотни или тысячи микрооптимизаций накапливаются и в итоге дают измеримый прирост производительности в реальных условиях.

3. Процесс измерения производительности

Редактируя исходный код SQLite, разработчики запускают сценарий командной оболочки speed-check.sh, чтобы отслеживать влияние изменений на производительность. Этот сценарий компилирует программу speedtest1.c, запускает ее под управлением cachegrind, обрабатывает результаты cachegrind с помощью сценария TCL cg_anno.tcl, а затем сохраняет результаты в виде набора текстовых файлов. Типичный вывод сценария speed-check.sh выглядит так:

==8683== 
==8683== I   refs:      1,060,925,768
==8683== I1  misses:       23,731,246
==8683== LLi misses:            5,176
==8683== I1  miss rate:          2.24%
==8683== LLi miss rate:          0.00%
==8683== 
==8683== D   refs:        557,686,925  (361,828,925 rd   + 195,858,000 wr)
==8683== D1  misses:        5,067,063  (  3,544,278 rd   +   1,522,785 wr)
==8683== LLd misses:           57,958  (     16,067 rd   +      41,891 wr)
==8683== D1  miss rate:           0.9% (        1.0%     +         0.8%  )
==8683== LLd miss rate:           0.0% (        0.0%     +         0.0%  )
==8683== 
==8683== LL refs:          28,798,309  ( 27,275,524 rd   +   1,522,785 wr)
==8683== LL misses:            63,134  (     21,243 rd   +      41,891 wr)
==8683== LL miss rate:            0.0% (        0.0%     +         0.0%  )
   text	   data	    bss	    dec	    hex	filename
 523044	   8240	   1976	 533260	  8230c	sqlite3.o
 220507 1007870 7769352 sqlite3.c

Важные части вывода (те, которым разработчики уделяют наибольшее внимание) выделены красным. По сути, разработчикам нужно знать размер скомпилированной библиотеки SQLite и количество тактов ЦП, необходимых для выполнения теста производительности.

Вывод сценария cg_anno.tcl показывает количество тактов ЦП, затраченных на каждую строку кода. Отчет содержит около 80 000 строк. Ниже приведен небольшой фрагмент из середины отчета, показывающий, как он выглядит:

         .  SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
         .    MemPage *pPage;
         .    assert( cursorOwnsBtShared(pCur) );
         .    assert( pRes!=0 );
         .    assert( *pRes==0 || *pRes==1 );
         .    assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
   369,648    pCur->info.nSize = 0;
   369,648    pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
   369,648    *pRes = 0;
   739,296    if( pCur->eState!=CURSOR_VALID ) return btreeNext(pCur, pRes);
 1,473,580    pPage = pCur->apPage[pCur->iPage];
 1,841,975    if( (++pCur->aiIdx[pCur->iPage])>=pPage->nCell ){
     4,340      pCur->aiIdx[pCur->iPage]--;
     5,593      return btreeNext(pCur, pRes);
         .    }
   728,110    if( pPage->leaf ){
         .      return SQLITE_OK;
         .    }else{
     3,117      return moveToLeftmost(pCur);
         .    }
   721,876  }

Слева, разумеется, указано количество тактов ЦП для соответствующей строки кода.

Сценарий cg_anno.tcl удаляет лишние подробности из стандартного вывода аннотаций cachegrind, чтобы можно было сравнивать отчеты «до» и «после» с помощью бокового сравнения diff и изучать конкретное влияние попытки микрооптимизации на производительность.

4. Ограничения

Использование стандартизированной рабочей нагрузки speedtest1.c и cachegrind позволило добиться значительного повышения производительности. Однако важно учитывать ограничения этого подхода:

  • Измерения производительности выполняются с помощью одного компилятора (gcc 5.4.0), с одним параметром оптимизации (-Os) и на одной платформе (Ubuntu 16.04 LTS на x64). Производительность при использовании других компиляторов и процессоров может отличаться.

  • Измеряемая рабочая нагрузка speedtest1.c предназначена для представления широкого спектра типичных случаев использования SQLite. Но каждое приложение отличается от других. Рабочая нагрузка speedtest1.c может быть неудачным приближением к действиям, выполняемым некоторыми приложениями. Разработчики SQLite постоянно совершенствуют программу speedtest1.c, чтобы она лучше отражала реальное использование SQLite. Мы приветствуем отзывы сообщества.

  • Значения количества тактов, предоставляемые cachegrind, хорошо приближают фактическую производительность, но не являются на 100% точными.

  • Здесь измеряется только количество тактов ЦП. Оно хорошо приближает энергопотребление, но не обязательно хорошо коррелирует со временем выполнения в реальных условиях. Время, затраченное на операции ввода-вывода, не отражается в количестве тактов ЦП, а во многих сценариях использования SQLite именно на ввод-вывод уходит основная часть времени.

5. Почему график заканчивается в 2025 году?

Количество тактов ЦП во многом зависит от используемого компилятора, а также в некоторой степени от операционной системы. Все приведенные выше измерения были выполнены с помощью GCC 5.4.0 в Ubuntu 16.04. Этот компьютер был выключен в начале 2025 года, поэтому использовать его для дополнительных измерений больше нельзя.

Мы могли бы повторить все 61 измерение, показанное на графике, с помощью более современного компилятора. (Это обновление вносится в начале 2026 года в Linux Mint 2.22 с GCC 13.3.0.) Но это потребовало бы больших усилий без заметной пользы. Большая часть прироста производительности пришлась на 2013–2017 годы, когда описанные выше методы микрооптимизации впервые начали применять в SQLite. На графике хорошо видно, что примерно в 2018 году кривая производительности выровнялась. С тех пор производительность медленно повышалась, но без столь значительных скачков, как в предыдущие годы. Мы ожидаем, что эта тенденция сохранится.

Разработчики продолжают регулярно измерять производительность — безусловно, при каждом выпуске, а также после каждого значительного изменения. Мы продолжаем оптимизировать SQLite, стремясь сократить число используемых тактов ЦП. Однако существенного повышения производительности не ожидается. Обратите внимание, что SQLite уже на 35% быстрее файловой системы. Насколько еще можно его ускорить?

Последнее обновление этой страницы: 2026-01-12 12:33:24Z

SQLite is in the Public Domain.
https://sqlite.org/cpu.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API