LINUX.ORG.RU

Можно ли ещё лучше оптимизировать данную функцию?

 ,


0

1

Запилил тут сильно оптимизированную функцию поиска строки и штампа времени в базе данных sqlite. В данный момент эта функция расходует 75% процессорного времени. Вопрос, эта функция является оптимизированной в плане того, что сначала идёт выборка по time_t, а потом только по char?

int has_been_used(sqlite3 *db, const char *l_path, time_t epoch)
{
	const char *sql = "SELECT 1 FROM files WHERE path = ? AND mtime = ?;";
	sqlite3_stmt *stmt;
	int rc, exists = 0;

	rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
	if (rc != SQLITE_OK)
	{
		fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db));
		return 0;
	}

	sqlite3_bind_text(stmt, 1, l_path, -1, SQLITE_STATIC);
	sqlite3_bind_int64(stmt, 2, (sqlite3_int64)epoch);

	rc = sqlite3_step(stmt);

	sqlite3_finalize(stmt);
	return rc;
}

что в ней оптимизированного ? смотри бд, а не код на с. Если ли индекс по path/mtime? Ну и если есть, то попробуй вставить реальные данные сразу в запрос без бинда, скорость по идее должна вырасти (может на порядок), а дальше уже решай стоит ли оно того.

vtVitus ★★★★★
()
Ответ на: комментарий от steemandlinux
  1. Вместо ограничения уникальности лучше сделать path первичным ключём (что тоже подразумевает ограничение уникальности) — если только у тебя в этой таблице уже нет первичного ключа. Если не привязываться к конкретным реализациям, можно считать PRIMARY KEY как комбинацию NOT NULL и UNIQUE.
  2. У тебя есть ограничение уникальности по столбцу path, оно реализовано через уникальный индекс (то же самое выполняется также для ограничения первичного ключа). Индекс, который ты создаёшь руками дальше по коду, будет избыточным.
  3. Для поиска по нескольким полям создай составной индекс по этим полям.
  4. Для оптимизации запросов научись пользоваться EXPLAIN.
theNamelessOne ★★★★★
()
Последнее исправление: theNamelessOne (всего исправлений: 1)
Ответ на: комментарий от theNamelessOne

Индекс для большего количества столбцов работает как индекс для меньшего количества столбцов, если убирать поля с конца

(field1, field2, field3) -> (field1, field2) -> (field1)

достатчоно первого идекса

anonymous
()

mtime это возрастающий лог событий?

строки - это произвольная произвольность али некие слова над алфавитом и следовательно легко сжимаемый текст с некоторомы не вполне известным распределением

от характера данных и их обьёма зависит оптимальное решение

крч уширь данные задачи

qulinxao3 ★☆
()

Это фигня какая-то для каждого запроса вызывать prepare/free. Сделай это вне цикла по файлам. Ну и потом возможно имеет смысл делать пакетные запросы. Надо тестировать

cobold ★★★★★
()
Последнее исправление: cobold (всего исправлений: 1)
Ответ на: комментарий от vtVitus

Нет, это рекурсивное копирование файлов с r/o устройства, которое запускается при получении сигнала от патченного DWC2 OTG драйвера:

	begin_transaction(db);
	scan_files(l_path, fs, d_path, db, &update_time);
	end_transaction(db);

Если делать без BEGIN TRANSACTION, то очевидно производительность будет в районе дна.

steemandlinux ★★★★★
() автор топика
Ответ на: комментарий от steemandlinux

Ты в курсах что такое транзакция и для чего они нужны? Я, например, занимаюсь бд больше 20 лет и для меня, мягко скажем, не очевидно, почему без транзакций (уровень изоляции транзакций хоть настроен или умолчательный для select полностью бессмысленный ?) будет сильно медленно.

Почитай хотя бы вот это https://stackoverflow.com/questions/60166976/why-is-postgres-transaction-block-so-much-slower-and-how-to-solve

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

vtVitus ★★★★★
()
Последнее исправление: vtVitus (всего исправлений: 2)
Ответ на: комментарий от vtVitus

почему без транзакций (уровень изоляции транзакций хоть настроен или умолчательный для select полностью бессмысленный ?) будет сильно медленно

Отсюда:

Unless already in a transaction, each SQL statement has a new transaction started for it. This is very expensive, since it requires reopening, writing to, and closing the journal file for each statement. This can be avoided by wrapping sequences of SQL statements with BEGIN TRANSACTION; and END TRANSACTION; statements. This speedup is also obtained for statements which don’t alter the database.

theNamelessOne ★★★★★
()
Ответ на: комментарий от vtVitus

Я кстати попробовал

"SELECT 1 FROM files INDEXED BY idx WHERE path = ? AND mtime = ?;";

и

"CREATE INDEX IF NOT EXISTS idx ON files (mtime, path);"

Никакого толку нет. Видимо sqlite по-умолчанию сначала integer смотрит.

steemandlinux ★★★★★
() автор топика
Ответ на: комментарий от theNamelessOne

Это очень и очень странная рекомендация 10 летней давности. Возможно, в sql lite так и есть и она до сих пор как бд в 80 годы на каждую не транзакционную операцию создаёт не нужную транзакцию, но спорить не буду sql lite слабо знаю.

vtVitus ★★★★★
()
Ответ на: комментарий от steemandlinux

У тебя уникальный индекс по path. Оптимизатор просто другие индексы рассматривать даже не будет.

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

vtVitus ★★★★★
()
Ответ на: комментарий от steemandlinux

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

Еще погугли, вдруг в SQLite есть аналог постгресовского COPY-протокола, оно хорошо подходит для загрузки больших объемов данных.

theNamelessOne ★★★★★
()
Ответ на: комментарий от steemandlinux

А зачем тебе при импорте данных делать запрос на то, существует ли запись в таблице?

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

theNamelessOne ★★★★★
()
Ответ на: комментарий от theNamelessOne

А зачем тебе при импорте данных делать запрос на то, существует ли запись в таблице?

Накопитель R/O, который периодически считывается и файлы, которые были скопированы, заносятся в базу.

steemandlinux ★★★★★
() автор топика
Ответ на: комментарий от steemandlinux

Так запрос на чтение зачем нужен?

Просто игнорируй дубликаты при вставке:

insert into files(path, mtime) values (...)
on conflict(path) do nothing;

Или обновляй mtime у дубликатов:

insert into files(path, mtime) values (...)
on conflict(path) do update set mtime = excluded.mtime;
theNamelessOne ★★★★★
()
Последнее исправление: theNamelessOne (всего исправлений: 1)
Ответ на: комментарий от theNamelessOne

Короче, это r/o устройств вставлено в прибор, который пишет на него файл, а ARM борда читает его в режиме R/O, чтобы не сломать файловую систему. И каждый раз туда прилетает новый файл. Их может быть столько же, сколько максимальное количество кластеров у FAT32

steemandlinux ★★★★★
() автор топика
Ответ на: комментарий от steemandlinux

Ты записываешь при чтении с устройста информацию о том, какие файлы были скопированы, в таблицу. Зачем тебе при записи доставать из таблицы инфу, есть ли конкретный файл в таблице или нет?

Я уже понял, что устройство у тебя R/O, не понимаю, как это связано с твоими запросами.

theNamelessOne ★★★★★
()
Последнее исправление: theNamelessOne (всего исправлений: 1)

Как минимум это, хорошо бы вынести из функции в контекст приложения: rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);

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

faq2
()
Ответ на: комментарий от theNamelessOne

Ну вот зачем ты человеку архитектуру чинишь, ща посмотрели бы на профили sqllite, поискали бы там узкие места…

А так и ему весь код переписывать, и нам -фан.

faq2
()