목록 통합 및 기능 기능이 "루프용"보다 빠릅니까?
Python의 Python의 기능이라고 할 수 .map()
,filter()
★★★★★★★★★★★★★★★★★」reduce()
포루프보다 빠를까요?기술적으로 포루프는 python 가상 머신 속도로 실행되는 반면 포루프는 C 속도로 실행되는 이유는 무엇입니까?
개발 중인 게임에서 루프를 사용하여 복잡하고 거대한 지도를 그릴 필요가 있다고 가정합니다.예를 들어 목록 이해가 실제로 더 빠르면 (코드의 시각적 복잡성에도 불구하고) 지연을 피하기 위해 훨씬 더 좋은 옵션이 될 것이기 때문에 이 질문은 분명히 관련이 있을 것이다.
다음은 경험에 근거한 대략적인 지침과 교육적인 추측이다.은 해야 합니다.timeit
또는 구체적인 사용 사례를 프로파일링하여 확실한 수치를 얻을 수 있습니다.이 수치들은 다음과 같은 수치와 일치하지 않을 수 있습니다.
는 보통 하는 항목보다 더 .for
loop은과 그 루프('루프')를 검색할 없기 에, 「」, 「루프」, 「루프」, 「루프」를 참조해 주세요.append
모든 반복에서 메서드를 지정합니다.의 루프 「」, 「」, 「」는 아직 하고 있습니다.
>>> dis.dis(<the code object for `[x for x in range(10)]`>)
1 0 BUILD_LIST 0
3 LOAD_FAST 0 (.0)
>> 6 FOR_ITER 12 (to 21)
9 STORE_FAST 1 (x)
12 LOAD_FAST 1 (x)
15 LIST_APPEND 2
18 JUMP_ABSOLUTE 6
>> 21 RETURN_VALUE
목록을 작성하지 않는 루프 대신 목록 이해 기능을 사용하면 의미 없는 값 목록을 축적한 후 목록을 폐기하는 것이 종종 목록 작성 및 확장의 오버헤드로 인해 느려집니다.목록 수집은 본질적으로 오래된 루프보다 빠른 마법은 아닙니다.
기능 목록 처리 기능에 대해서는:이러한 기능은 C로 작성되어 Python으로 작성된 동등한 기능보다 성능이 뛰어나지만, 반드시 가장 빠른 옵션은 아닙니다.함수도 C로 표기되어 있으면 속도가 다소 빨라질 것으로 예상됩니다.그러나 대부분의 경우,lambda
(또는 다른 Python 함수), Python 스택 프레임 등을 반복적으로 설정하는 데 드는 오버헤드가 절감되는 부분을 모두 소비합니다.호출 없이 목록 )map
★★★★★★★★★★★★★★★★★」filter
이(가) 인 경우가 많습니다이(가) 약간 빠른 경우가 많습니다.
개발 중인 게임에서 루프를 사용하여 복잡하고 거대한 지도를 그릴 필요가 있다고 가정합니다.예를 들어 목록 이해가 실제로 더 빠르면 (코드의 시각적 복잡성에도 불구하고) 지연을 피하기 위해 훨씬 더 좋은 옵션이 될 것이기 때문에 이 질문은 분명히 관련이 있을 것이다.
최적화되지 않은 좋은 Python으로 작성했을 때 이와 같은 코드가 이미 충분히 빠르지 않다면 Python 수준의 마이크로 최적화를 아무리 해도 충분히 빠르지 않을 가능성이 높기 때문에 C로 드롭하는 것을 고려해야 합니다.광범위한 마이크로 최적화는 종종 Python 코드를 상당히 빠르게 만들 수 있지만, 여기에는 낮은(절대적인) 제한이 있습니다.게다가 그 한계에 도달하기 전에, 보다 코스트 효율이 향상됩니다(같은 노력으로 고속화 15%, 고속화 300%).
python.org 에서 정보를 체크하면, 다음의 요약이 표시됩니다.
Version Time (seconds)
Basic loop 3.47
Eliminate dots 2.45
Local variable & no dots 1.79
Using map function 0.54
하지만 성능 차이의 원인을 이해하려면 위의 기사를 자세히 읽어보셔야 합니다.
또한 타임잇을 사용하여 코드 시간을 재는 것이 좋습니다.예를 들어, 하루의 끝자락에서 벗어나야 하는 상황이 발생할 수 있습니다.for
루프를 설정합니다.를 해서 보다 더 수 .map
.map()
,filter()
★★★★★★★★★★★★★★★★★」reduce()
일반적인 기능 프로그래밍에 대해 알고 싶으시겠죠?의 모든 간의 함수 )starmap
기능itertools
모듈) For-timeout(For-timeout(For-timeout 1.25).사용한 샘플 코드는 다음과 같습니다.
import itertools, time, math, random
class Point:
def __init__(self,x,y):
self.x, self.y = x, y
point_set = (Point(0, 0), Point(0, 1), Point(0, 2), Point(0, 3))
n_points = 100
pick_val = lambda : 10 * random.random() - 5
large_set = [Point(pick_val(), pick_val()) for _ in range(n_points)]
# the distance function
f_dist = lambda x0, x1, y0, y1: math.sqrt((x0 - x1) ** 2 + (y0 - y1) ** 2)
# go through each point, get its distance from all remaining points
f_pos = lambda p1, p2: (p1.x, p2.x, p1.y, p2.y)
extract_dists = lambda x: itertools.starmap(f_dist,
itertools.starmap(f_pos,
itertools.combinations(x, 2)))
print('Distances:', list(extract_dists(point_set)))
t0_f = time.time()
list(extract_dists(large_set))
dt_f = time.time() - t0_f
기능 버전이 절차 버전보다 빠릅니까?
def extract_dists_procedural(pts):
n_pts = len(pts)
l = []
for k_p1 in range(n_pts - 1):
for k_p2 in range(k_p1, n_pts):
l.append((pts[k_p1].x - pts[k_p2].x) ** 2 +
(pts[k_p1].y - pts[k_p2].y) ** 2)
return l
t0_p = time.time()
list(extract_dists_procedural(large_set))
# using list() on the assumption that
# it eats up as much time as in the functional version
dt_p = time.time() - t0_p
f_vs_p = dt_p / dt_f
if f_vs_p >= 1.0:
print('Time benefit of functional progamming:', f_vs_p,
'times as fast for', n_points, 'points')
else:
print('Time penalty of functional programming:', 1 / f_vs_p,
'times as slow for', n_points, 'points')
@Alisa의 코드를 수정하여 사용하였습니다.cProfile
목록 이해 속도가 빠른 이유를 보여줍니다.
from functools import reduce
import datetime
def reduce_(numbers):
return reduce(lambda sum, next: sum + next * next, numbers, 0)
def for_loop(numbers):
a = []
for i in numbers:
a.append(i*2)
a = sum(a)
return a
def map_(numbers):
sqrt = lambda x: x*x
return sum(map(sqrt, numbers))
def list_comp(numbers):
return(sum([i*i for i in numbers]))
funcs = [
reduce_,
for_loop,
map_,
list_comp
]
if __name__ == "__main__":
# [1, 2, 5, 3, 1, 2, 5, 3]
import cProfile
for f in funcs:
print('=' * 25)
print("Profiling:", f.__name__)
print('=' * 25)
pr = cProfile.Profile()
for i in range(10**6):
pr.runcall(f, [1, 2, 5, 3, 1, 2, 5, 3])
pr.create_stats()
pr.print_stats()
결과는 다음과 같습니다.
=========================
Profiling: reduce_
=========================
11000000 function calls in 1.501 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1000000 0.162 0.000 1.473 0.000 profiling.py:4(reduce_)
8000000 0.461 0.000 0.461 0.000 profiling.py:5(<lambda>)
1000000 0.850 0.000 1.311 0.000 {built-in method _functools.reduce}
1000000 0.028 0.000 0.028 0.000 {method 'disable' of '_lsprof.Profiler' objects}
=========================
Profiling: for_loop
=========================
11000000 function calls in 1.372 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1000000 0.879 0.000 1.344 0.000 profiling.py:7(for_loop)
1000000 0.145 0.000 0.145 0.000 {built-in method builtins.sum}
8000000 0.320 0.000 0.320 0.000 {method 'append' of 'list' objects}
1000000 0.027 0.000 0.027 0.000 {method 'disable' of '_lsprof.Profiler' objects}
=========================
Profiling: map_
=========================
11000000 function calls in 1.470 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1000000 0.264 0.000 1.442 0.000 profiling.py:14(map_)
8000000 0.387 0.000 0.387 0.000 profiling.py:15(<lambda>)
1000000 0.791 0.000 1.178 0.000 {built-in method builtins.sum}
1000000 0.028 0.000 0.028 0.000 {method 'disable' of '_lsprof.Profiler' objects}
=========================
Profiling: list_comp
=========================
4000000 function calls in 0.737 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1000000 0.318 0.000 0.709 0.000 profiling.py:18(list_comp)
1000000 0.261 0.000 0.261 0.000 profiling.py:19(<listcomp>)
1000000 0.131 0.000 0.131 0.000 {built-in method builtins.sum}
1000000 0.027 0.000 0.027 0.000 {method 'disable' of '_lsprof.Profiler' objects}
IMHO:
reduce
★★★★★★★★★★★★★★★★★」map
일반적으로는 꽤 느립니다.만 아니라, ★★★★★★★★★★★★★★★★★★★★★★.sum
map
sum
를for_loop
.append는 append를 사용합니다.- 은 목록 시간을 할애할 만 아니라 작성에도 이 걸립니다.
sum
더 빠른 입니다.「 」와는 으로, 「 」map
저는 속도를 테스트하는 간단한 대본을 썼는데 이렇게 알게 되었습니다.사실 내 경우엔 루프가 가장 빨랐어정말 놀랐어요, 아래를 보세요(제곱합을 계산하고 있었습니다).
from functools import reduce
import datetime
def time_it(func, numbers, *args):
start_t = datetime.datetime.now()
for i in range(numbers):
func(args[0])
print (datetime.datetime.now()-start_t)
def square_sum1(numbers):
return reduce(lambda sum, next: sum+next**2, numbers, 0)
def square_sum2(numbers):
a = 0
for i in numbers:
i = i**2
a += i
return a
def square_sum3(numbers):
sqrt = lambda x: x**2
return sum(map(sqrt, numbers))
def square_sum4(numbers):
return(sum([int(i)**2 for i in numbers]))
time_it(square_sum1, 100000, [1, 2, 5, 3, 1, 2, 5, 3])
time_it(square_sum2, 100000, [1, 2, 5, 3, 1, 2, 5, 3])
time_it(square_sum3, 100000, [1, 2, 5, 3, 1, 2, 5, 3])
time_it(square_sum4, 100000, [1, 2, 5, 3, 1, 2, 5, 3])
0:00:00.302000 #Reduce
0:00:00.144000 #For loop
0:00:00.318000 #Map
0:00:00.390000 #List comprehension
@alpii의 코드 중 일부를 수정해 보니 List 이해가 loop보다 조금 더 빠르다는 것을 알 수 있었습니다.원인일 수 있습니다.int()
목록 이해와 루프 사이에 공평하지 않습니다.
from functools import reduce
import datetime
def time_it(func, numbers, *args):
start_t = datetime.datetime.now()
for i in range(numbers):
func(args[0])
print (datetime.datetime.now()-start_t)
def square_sum1(numbers):
return reduce(lambda sum, next: sum+next*next, numbers, 0)
def square_sum2(numbers):
a = []
for i in numbers:
a.append(i*2)
a = sum(a)
return a
def square_sum3(numbers):
sqrt = lambda x: x*x
return sum(map(sqrt, numbers))
def square_sum4(numbers):
return(sum([i*i for i in numbers]))
time_it(square_sum1, 100000, [1, 2, 5, 3, 1, 2, 5, 3])
time_it(square_sum2, 100000, [1, 2, 5, 3, 1, 2, 5, 3])
time_it(square_sum3, 100000, [1, 2, 5, 3, 1, 2, 5, 3])
time_it(square_sum4, 100000, [1, 2, 5, 3, 1, 2, 5, 3])
0:00:00.101122 #Reduce
0:00:00.089216 #For loop
0:00:00.101532 #Map
0:00:00.068916 #List comprehension
Alphii 답변에 반전을 더하면, 실제로 for 루프는 차선이며, 보다 약 6배 느립니다.map
from functools import reduce
import datetime
def time_it(func, numbers, *args):
start_t = datetime.datetime.now()
for i in range(numbers):
func(args[0])
print (datetime.datetime.now()-start_t)
def square_sum1(numbers):
return reduce(lambda sum, next: sum+next**2, numbers, 0)
def square_sum2(numbers):
a = 0
for i in numbers:
a += i**2
return a
def square_sum3(numbers):
a = 0
map(lambda x: a+x**2, numbers)
return a
def square_sum4(numbers):
a = 0
return [a+i**2 for i in numbers]
time_it(square_sum1, 100000, [1, 2, 5, 3, 1, 2, 5, 3])
time_it(square_sum2, 100000, [1, 2, 5, 3, 1, 2, 5, 3])
time_it(square_sum3, 100000, [1, 2, 5, 3, 1, 2, 5, 3])
time_it(square_sum4, 100000, [1, 2, 5, 3, 1, 2, 5, 3])
을 없애는 것이다.sum
이 있는 「콜이나, 「불필요한」이라고 하는 콜도 있습니다.int()
마지막 케이스에서는for 루프와 맵을 같은 용어로 표현하면 사실이죠.람다는 기능적인 개념이고 이론적으로 부작용이 있어서는 안 되지만, 음, 그것들은 추가되는 것과 같은 부작용이 있을 수 있다는 것을 기억하세요.a
.6.1,) @ 3.@ Python 3.6.1, Ubuntu 14.04, Ubuntu (TM) i7-4770 CPU @ 40GHz 입니다
0:00:00.257703 #Reduce
0:00:00.184898 #For loop
0:00:00.031718 #Map
0:00:00.212699 #List comprehension
언급URL : https://stackoverflow.com/questions/22108488/are-list-comprehensions-and-functional-functions-faster-than-for-loops
'IT' 카테고리의 다른 글
장고 - South를 사용하여 모델 필드의 이름을 변경하는 방법 (0) | 2022.11.27 |
---|---|
함수 호출 전 @ 문자 (0) | 2022.11.27 |
라라벨 블레이드 점검 비어 있는 포어치 (0) | 2022.11.27 |
개인 방법은 정말 안전한가요? (0) | 2022.11.27 |
루프에서 나머지 작업을 실행하는 Java 스레드가 다른 모든 스레드를 차단합니다. (0) | 2022.11.18 |