Как работать с numba в питоне с использованием ClickHouse?

Пытаюсь работать с numba в питоне и не получается использовать курсор базы данных. Использую request = client.execute_iter(base_req), причем создание курсора я пытался выносить в отдельную функцию, для которой не применял декоратор @njit, не помогло. Искал в документации numba clickhouse - ничего нет. На этот код я получаю сообщение:

This error may have been caused by the following argument(s):
- argument 0: cannot determine Numba type of <class 'clickhouse_driver.client.Client'>

По сути же, я в итераторе буду идти по этому курсору и получать обычные кортежи из двух элементов, с которыми numba работает. Как работать с numba, используя ClickHouse? Описание задачи: происходит запрос к БД, из которой выгружаются кортежи из двух элементов, первый элемент - строка, второй число. Генерируется словарь, в качестве ключа которого выступают строки, а значения - списки из чисел, то есть происходит некая группировка по строкам. На текущий момент скорость заполнения словаря составляет примерно 500 000 строк в секунду.

d = defaultdict(list)
request = client.execute_iter(base_req)
{d[_i[1].lower()].append(_i[0]) for _i in tqdm.tqdm(request) if _i[0] not in d[_i[1].lower()]}

Запрос, к сожалению, показать не могу. Примерный результат вывода запроса: ("string", 5).


Ответы (1 шт):

Автор решения: CrazyElf

Ну вот теперь хоть есть какой-то предмет (код) для разговора:

{d[_i[1].lower()].append(_i[0]) for _i in tqdm.tqdm(request) if _i[0] not in d[_i[1].lower()]}
  1. Если вас интересует скорость - не используйте tqdm во внутренних циклах обработки! Если у вас есть какой-то внешний цикл - там можете использовать. А если вы оборачиваете самый внутренний цикл в tqdm - будьте готовы к значительному замедлению работы кода (вполне возможно, что всё замедление даёт вам именно tqdm). Возможно, с тех пор, как я всё это замерял прошло время и библиотеку немного улучшили, но работа с GUI всегда медленная и это также подтверждает мой личный опыт, что tqdm замедляет скорость работы кода в несколько раз.
  2. У вас может по два раза осуществляться доступ к d[_i[1].lower()], лучше занесите эту ссылку на список в отдельную переменную и делайте проверку и добавление через эту переменную. И функция lower тоже может работать не очень быстро, так что тут убьёте сразу двух зайцев.
  3. Чтобы сделать изменение кода из п.2, вам видимо нужно отказаться от использования включения множества или что там у вас получилось через конструкцию { comprehension }, тем более, что результат его вы всё-равно не используете, просто зря расходуя ресурсы на его генерацию - у вас получается там создаётся множество из элементов None (потому что list.append возвращает None), взятых в количестве элементов, возвращённых запросом. Всё это схлопывается в итоге в пустое множество. Зачем эта лишняя работа? Списковые (и прочие) включения - штука удобная и их можно и нужно использовать для выполнения небольших работ, но не в том, случае, когда речь идёт о миллионах записей и дорога каждая микросекунда, в этом случае надо тщательно смотреть, какой подход будет оптимальнее.

Так что предлагаю написать обычный прямой код, померить, с какой скоростью он будет работать и потом уже, если надо, думать дальше насчёт Numba, lru_cache или каких-то других улучшений:

for _i in request:
    l = d[_i[1].lower()]
    v = _i[0]
    if v not in l:
        l.append(v)

Кстати, вопрос - важен ли вам порядок элементов в списке, лежащем в словаре? Если нет, то сходу могу предложить использовать defaultdict(set) и l.add(v), проверка на наличие элемента в коллекции будет работать значительно быстрее.

d = defaultdict(set)
for _i in request:
    l = d[_i[1].lower()]
    v = _i[0]
    if v not in l:
        l.add(v)
→ Ссылка