Ответ 1
Введение
Мое предположение заключалось в том, что у нас есть проблема с измерением производительности здесь, поэтому я написал небольшую программу Go для создания записей и сохранения их в базе данных SQLite, а также для реализации Python и Go небольшой задачи по этим записям.
Вы можете найти соответствующий репозиторий в https://github.com/mwmahlberg/sqlite3perf
Модель данных
Созданные записи состоят из
-
ID
: идентификатор строки, сгенерированный SQLite -
rand
: hex encoded 8 байт псевдослучайное значение -
hash
: A hex encoded SHA256 хэш незарегистрированногоrand
Схема таблицы относительно проста:
sqlite> .schema
CREATE TABLE bench (ID int PRIMARY KEY ASC, rand TEXT, hash TEXT);
Сначала я сгенерировал записи на 1.5M и затем очистил базу данных sqlite с помощью
$ ./sqlite3perf generate -r 1500000 -v
Затем я назвал реализацию Go в отношении этих записей за 1,5М. Как Go, так и реализация Python в основном выполняют одну и ту же простую задачу:
- Прочитайте все записи из базы данных.
- Для каждой строки декодируйте случайное значение из hex, затем создайте шестнадцатеричный SHA256 из результата.
- Сравните сгенерированную шестнадцатеричную строку SHA256 с той, которая хранится в базе данных
- Если они совпадают, продолжайте, иначе перерыв.
Предположения
Мое предположение явно заключалось в том, что Python сделал некоторый тип ленивой загрузки и/или, возможно, даже выполнение SQL-запроса.
Результаты
Go реализация
$ ./sqlite3perf bench
2017/12/31 15:21:48 bench called
2017/12/31 15:21:48 Time after query: 4.824009ms
2017/12/31 15:21:48 Beginning loop
2017/12/31 15:21:48 Acessing the first result set
ID 0,
rand: 6a8a4ad02e5e872a,
hash: 571f1053a7c2aaa56e5c076e69389deb4db46cc08f5518c66a4bc593e62b9aa4
took 548.32µs
2017/12/31 15:21:50 641,664 rows processed
2017/12/31 15:21:52 1,325,186 rows processed
2017/12/31 15:21:53 1,500,000 rows processed
2017/12/31 15:21:53 Finished loop after 4.519083493s
2017/12/31 15:21:53 Average 3.015µs per record, 4.523936078s overall
Обратите внимание на значения для "time after query" (время, которое потребовала команда запроса для возврата), и время, затрачиваемое на получение первого набора результатов после начала итерации по набору результатов.
Реализация Python
$ python bench.py
12/31/2017 15:25:41 Starting up
12/31/2017 15:25:41 Time after query: 1874µs
12/31/2017 15:25:41 Beginning loop
12/31/2017 15:25:44 Accessing first result set
ID: 0
rand: 6a8a4ad02e5e872a
hash: 571f1053a7c2aaa56e5c076e69389deb4db46cc08f5518c66a4bc593e62b9aa4
took 2.719312 s
12/31/2017 15:25:50 Finished loop after 9.147431s
12/31/2017 15:25:50 Average: 6.098µs per record, 0:00:09.149522 overall
Снова обратите внимание на значение "время после запроса" и время, необходимое для доступа к первому набору результатов.
Резюме
Потребовалось некоторое время, чтобы вернуться после запроса SELECT, в то время как Python, казалось, быстро сверкал в сравнении. Однако с момента, когда потребовалось фактически получить доступ к первому набору результатов, мы видим, что реализация Go более чем в 500 раз быстрее, чтобы фактически получить доступ к первому набору результатов (5.372329ms против 2719.312ms) и примерно вдвое быстрее для задачи как реализация Python.
Примечания
- Чтобы доказать предположение, что Python фактически выполняет ленивую загрузку в результирующем наборе, нужно было получить доступ к каждой строке и столбцу, чтобы убедиться, что Python вынужден фактически считывать значение из базы данных.
- Я выбрал хеширование, потому что предположительно реализация SHA256 сильно оптимизирована на обоих языках.
Заключение
Python, похоже, делает ленивую загрузку наборов результатов и, возможно, даже не выполняет запрос, если на самом деле не получен соответствующий набор результатов. В этом симулированном сценарии матовый драйвер SQLite для Go превосходит Python примерно на 100% и порядков в зависимости от того, что вы хотите сделать.
Изменить. Поэтому, чтобы иметь быструю обработку, выполните свою задачу в Go. Хотя для отправки фактического запроса требуется больше времени, доступ к отдельным строкам набора результатов намного быстрее. Я предлагаю начать с небольшого подмножества ваших данных, скажем, 50 тыс. Записей. Затем, чтобы улучшить код, используйте profiling, чтобы определить ваши узкие места. В зависимости от того, что вы хотите сделать во время обработки, pipelines, например, может помочь, но как улучшить скорость обработки задачи под рукой трудно сказать без фактического кода или подробного описания.