Функция Numba @jit (nopython = True) не дает улучшения скорости для тяжелой функции Numpy. - PullRequest
0 голосов
/ 14 февраля 2019

В настоящее время я запускаю test_matrix_speed(), чтобы увидеть, насколько быстро работает моя функция search_and_book_availability.Используя профилировщик PyCharm, я вижу, что каждый вызов функции search_and_book_availability имеет скорость в среднем 0,001 мс.Наличие декоратора Numba @jit(nopython=True) не влияет на производительность этой функции.Это потому, что нет никаких улучшений, и Numpy работает здесь максимально быстро?(Меня не волнует скорость функции generate_searches)

Вот код, который я запускаю

import random

import numpy as np
from numba import jit


def generate_searches(number, sim_start, sim_end):
    searches = []
    for i in range(number):
        start_slot = random.randint(sim_start, sim_end - 1)
        end_slot = random.randint(start_slot + 1, sim_end)
        searches.append((start_slot, end_slot))
    return searches


@jit(nopython=True)
def search_and_book_availability(matrix, search_start, search_end):
    search_slice = matrix[:, search_start:search_end]
    output = np.where(np.sum(search_slice, axis=1) == 0)[0]
    number_of_bookable_vecs = output.size
    if number_of_bookable_vecs > 0:
        if number_of_bookable_vecs == 1:
            id_to_book = output[0]
        else:
            id_to_book = np.random.choice(output)
        matrix[id_to_book, search_start:search_end] = 1
        return True
    else:
        return False


def test_matrix_speed():
    shape = (10, 1440)
    matrix = np.zeros(shape)
    sim_start = 0
    sim_end = 1440
    searches = generate_searches(1000000, sim_start, sim_end)
    for i in searches:
        search_start = i[0]
        search_end = i[1]
        availability = search_and_book_availability(matrix, search_start, search_end)

1 Ответ

0 голосов
/ 15 февраля 2019

Использование вашей функции и следующего кода для профилирования скорости

import time

shape = (10, 1440)
matrix = np.zeros(shape)
sim_start = 0
sim_end = 1440
searches = generate_searches(1000000, sim_start, sim_end)

def reset():
    matrix[:] = 0

def test_matrix_speed():
    for i in searches:
        search_start = i[0]
        search_end = i[1]
        availability = search_and_book_availability(matrix, search_start, search_end)

def timeit(func):
    # warmup
    reset()
    func()

    reset()
    start = time.time()
    func()
    end = time.time()

    return end - start

print(timeit(test_matrix_speed))

Я нахожу порядка 11,5 с для jit ed версии и 7,5 с без jit.Я не эксперт по numba, но для этого она оптимизирует числовой код, написанный не векторизованным способом, в частности, с явными циклами for.В вашем коде их нет, вы используете только векторизованные операции.Поэтому я ожидал, что jit не превзойдет базовое решение, хотя должен признать, что удивился, увидев его намного хуже.Если вы хотите оптимизировать свое решение, вы можете сократить время выполнения (по крайней мере, на моем ПК) следующим кодом:

def search_and_book_availability_opt(matrix, search_start, search_end):
    search_slice = matrix[:, search_start:search_end]

    # we don't need to sum in order to check if all elements are 0.
    # ndarray.any() can use short-circuiting and is therefore faster.
    # Also, we don't need the selected values from np.where, only the
    # indexes, so np.nonzero is faster
    bookable, = np.nonzero(~search_slice.any(axis=1))

    # short circuit
    if bookable.size == 0:
        return False

    # we can perform random choice even if size is 1
    id_to_book = np.random.choice(bookable)
    matrix[id_to_book, search_start:search_end] = 1
    return True

и инициализировать matrix как np.zeros(shape, dtype=np.bool) вместопо умолчанию float64.Я могу получить время выполнения около 3,8 с, улучшение на ~ 50% по сравнению с вашим неопубликованным решением и улучшение на ~ 70% по сравнению с джитированной версией.Надеюсь, это поможет.

...