2012-02-28 2 views
1

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

Но в Python, если у меня есть:

list_of_sets = generate_a_list_containg_a_bunch_of_sets() 
intersection_of_all = reduce(list_of_sets[0].intersection, list_of_sets) 

Это резко (в моих опытах около 40%) медленнее, чем:

list_of_sets = generate_a_list_containg_a_bunch_of_sets() 
intersection_of_all = reduce(set.intersection, list_of_sets) 

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

Может ли кто-нибудь осветить, где мое понимание испорчено?

+0

Вы видите это differenc e для многих небольших наборов или для нескольких крупных? Я ожидал, что связанные проблемы будут иметь значение в первом случае, но не в последнем (когда фактическая работа пересечения доминирует над накладными расходами). Я вижу два противоречивых ответа (один из них дважды) и не могу сказать, что правильно. – ugoren

+0

Это было как для небольшого (список из примерно 10 наборов), так и для среды (список из 100 наборов, которые были созданы случайным образом). Причина была объяснена Свеном в его ответе ниже. –

ответ

12

Это полностью не связано с привязкой к методу и т. Д. Первая версия вычисляет пересечение трех наборов на каждой итерации, а вторая версия пересекает только два набора. Это легко увидеть, если вместо этого использовать явные петли.

Вариант 1:

intersection = list_of_sets[0] 
for s in list_of_sets[1:]: 
    intersection = list_of_sets[0].intersection(intersection, s) 

Вариант 2:

intersection = list_of_sets[0] 
for s in list_of_sets[1:]: 
    intersection = set.intersection(intersection, s) 

(вы согласитесь, в настоящее время Гвидо имеет смысл?)

Обратите внимание, что это, вероятно, будет еще быстрее:

intersection = list_of_sets[0] 
for s in list_of_sets[1:]: 
    intersection.intersection_update(s) 
+0

AHHHHHHH, ок, я вижу. Благодаря! –

+0

И yup, зацикливание с intersection_udpate было более быстрым (около 3%), чем сокращение с помощью set.intersection. –

+0

@AdamParkin: Поскольку я ожидал гораздо большую разницу для нетривиальных случаев, поэтому [я сам сделал некоторые тайминги] (https://gist.github.com/1934353). И действительно, я нашел версию цикла более чем в два раза быстрее, чем версия 'reduce(). Не нужно создавать новый набор на каждой итерации * должен * иметь значение! –

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