2016-10-31 4 views
0

У меня есть небольшая функция (см. Ниже), которая возвращает список имен, которые отображаются из списка целых чисел (например, [1,2,3,4]), который может иметь длину до тысяча.Оптимизация для кода Python

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

graph_hash - большой хэш, который отображает ключи на наборы длиной 1000 или меньше. Я повторяю набор и сопоставляю значения с именами и возвращаю список. u.get_name_from_id() запрашивает базу данных sqlite.

Любые мысли для оптимизации любой части этой функции?

def get_neighbors(pid): 
    names = [] 
    for p in graph_hash[pid]: 
     names.append(u.get_name_from_id(p)) 
    return names 
+1

Вы можете сделать это генератором, хотя это просто подтолкнет узкое место к вызывающей функции кеша –

+1

так же, как вы можете 'get_neighbors' и' u.get_name_from_id' – salparadise

+1

Можете ли вы построить '' 'WHERE x IN [все элементы в наборе] '' 'clause и просто сделать запрос на один дБ на' '' pid''' ??? Если вы можете и '' 'get_name_from_id''' возвращает список, используйте' '' names.extend (....) '' ' – wwii

ответ

1

Я бы рекомендовал использовать deque вместо list если вы делаете тысячи в конце. Таким образом, names должен быть names = deque().

+0

Это неверно - списки в Python оптимизированы для эффективной обработки' append() ' операция. Новые списки создаются _not_, элементы просто добавляются в конце. –

+0

Согласно https://wiki.python.org/moin/TimeComplexity: '" Внутренне список представлен как массив: наибольшие затраты растут за пределами текущего размера размещения (потому что все должно двигаться) или от вставки или удалить где-то рядом с началом (потому что все после этого должно двигаться). Если вам нужно добавить/удалить с обоих концов, рассмотрите вместо этого использование файла collection.deque. '' – Nurjan

+0

Ключевая часть здесь «на обоих концах».Это не тот случай, когда мы добавляем список, мы добавляем элементы в самый эффективный конец. (И вообще, «deque» также должен расти, если добавлять элементы за пределы его начальной емкости) –

2

Вы ударять последовательно базы данных здесь:

for p in graph_hash[pid]: 
    names.append(u.get_name_from_id(p)) 

Я бы рекомендовал делать это одновременно с использованием потоков. Что-то, как это должно вам начать:

def load_stuff(queue, p): 
    q.put(u.get_name_from_id(p)) 

def get_neighbors(pid): 
    names = Queue.Queue() 
    # we'll keep track of the threads with this list 
    threads = [] 
    for p in graph_hash[pid]: 
     thread = threading.Thread(target=load_stuff, args=(names,p)) 
     threads.append(thread) 
     # start the thread 
     thread.start() 
    # wait for them to finish before you return your Queue 
    for thread in threads: 
     thread.join() 
    return names 

Вы можете превратить Queue обратно в список с [item for item in names.queue], если это необходимо.

Идея состоит в том, что вызовы базы данных блокируются до тех пор, пока они не будут выполнены, но вы можете сделать несколько операторов SELECT в базе данных без блокировки. Таким образом, вы должны использовать потоки или какой-либо другой метод параллелизма, чтобы избежать ненужного ожидания.

2

Кэширование и многопоточность пока вас доставят, вы должны создать новый метод, который использует executemany под капотом для извлечения нескольких имен из базы данных в массовом порядке.

Нечто вроде names = u.get_names_from_ids(graph_hash[pid]).

1

список понимание есть начало (аналогично-х @ cricket_007 генератор предложение), но вы ограничены вызовы функций:

def get_neighbors(pid): 
    return [u.get_name_from_id(p) for p in graph_hash[pid]] 

Как @salparadise предложил рассмотреть memoization ускорить get_name_from_id().

Смежные вопросы