Как стать автором
Поиск
Написать публикацию
Обновить

Неочевидная оптимизация по скорости при решении конкретной задачи на Python

Время на прочтение2 мин
Количество просмотров9.6K

Начнём


Имеется SQL база данных. Задача описывается тремя фразами:
  • выгрузка данных
  • валидация данных
  • генерация отчёта

Задача детальнее

  1. Скрипт должен выполняться очень часто.
  2. Выгрузка данных заключается в вычитке из базы результат простейшего запроса SELECT * FROM table. В таблице/вьюшке строк обычно более 100000, колонок ~100.
  3. Валидация представляет собой проверку набора условий вида rowObject.Column1 == Value (<, >, !=) и более сложных проверок. Смысл в том что проверка требует обращения к колонке по имени.
  4. Генерация отчёта по результату проверок.

Обратим внимание на пункт 1

Остальное не так интересно.
(В качестве примера использую базу данных sqlite)

Использовать какую либо ORM для такой задачи по меньшей мере странно. Делаем в лоб (для упрощения в память выгружаем весь результат)
import sqlite3
conn = sqlite3.connect(filePath)
result = tuple(row for row in conn.cursor().execute("SELECT * FROM test"))

После выполнения result содержит кортеж кортежей. Нам же нужен объект с аттрибутами.
Усложняем:
ColsCount = 100
class RowWrapper(object):
    def __init__(self, values):
        self.Id = values[0]
        for x in xrange(ColsCount):
            setattr(self, "Col{0}".format(x), values[x + 1])
result = tuple(RowWrapper(row) for row in conn.cursor().execute(self.query))

Мы готовы перейти к пункту 2. Или нет? А давайте замеряем скорость обоих примеров(полный тестовый код здесь).
100000 строк, 101 колонка
У меня получилось в секундах:
Sample 1: 4.64823588605
Sample 2: 17.1091031498
На создание инстансов класса тратится > 10сек
С++ программисту внутри меня захотелось с этим что-нибудь сделать.

Решение нашлось такое

Используем namedtuple из модуля collections. Не буду описывать здесь подробно принцип его работы. Приведу лишь небольшой пример демонстрирующий нужную нам функциональность.
import collections
columns = ('name', 'age', 'story')
values = ('john', '99', '...blahblah...')
SuperMan = collections.namedtuple('SuperMan', columns)
firstSuperMan = SuperMan._make(values)
print(firstSuperMan.name)
print(firstSuperMan.age)
print(firstSuperMan.story)

А теперь пример в контексте задачи:
import collections
columns = tuple(itertools.chain(('Id',), tuple("Col{0}".format(x) for x in xrange(ColsCount))))
TupleClass = collections.namedtuple("TupleClass", Columns)
result = tuple(TupleClass._make(row) for row in conn.cursor().execute(self.query))

Замеряем скорость:
Sample 1: 4.30456730876
Sample 2: 15.3314512807
Sample 3: 4.67008026138

Совсем другое дело. Полный пример кода с созданием базы и замерами скорости смотрим здесь

Для примеров в статье использовалось


Теги:
Хабы:
Всего голосов 8: ↑6 и ↓2+4
Комментарии25

Публикации

Ближайшие события