Programming

목록 이해와 기능적 기능이 "for loop"보다 빠릅니까?

procodes 2020. 7. 6. 21:24
반응형

목록 이해와 기능적 기능이 "for loop"보다 빠릅니까?


파이썬의 성능면에서 목록 이해 또는 map (), filter () 및 reduce ()와 같은 함수가 for 루프보다 빠릅니까? 기술적으로 "파이 속도 가상 머신 속도로 for 루프가 실행되는 동안"C 속도로 실행되는 이유는 무엇입니까?

개발중인 게임에서 for 루프를 사용하여 복잡하고 거대한 맵을 그려야한다고 가정 해 봅시다. 예를 들어, 목록 이해가 실제로 더 빠르면 지연을 피하기 위해 훨씬 더 나은 옵션이 될 것입니다 (코드의 시각적 복잡성에도 불구하고).


다음은 경험을 바탕으로 한 대략적인 지침과 교육 된 추측입니다. 당신은해야 timeit또는 하드 번호를 얻을 수 있도록 구체적인 사용 사례를 프로파일 링하고, 그 숫자는 때때로 아래에 동의 할 수있다.

목록 이해는 일반적으로 정확히 동등한 for루프 (실제로 목록을 작성하는 루프) 보다 약간 빠르며 , 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)이 종종 약간 더 빠릅니다.

개발중인 게임에서 for 루프를 사용하여 복잡하고 거대한 맵을 그려야한다고 가정 해 봅시다. 예를 들어, 목록 이해가 실제로 더 빠르면 지연을 피하기 위해 훨씬 더 나은 옵션이 될 것입니다 (코드의 시각적 복잡성에도 불구하고).

"최적화되지 않은"좋은 파이썬으로 작성했을 때 이와 같은 코드가 아직 충분히 빠르지 않다면, 파이썬 수준의 마이크로 최적화가 충분히 빠르지 않아 C로 떨어질 생각을 시작해야 할 것입니다. 마이크로 최적화는 종종 파이썬 코드의 속도를 크게 향상시킬 수 있으며, 이에 대한 제한은 절대적입니다. 또한, 한도에 도달하기 전에도 총알을 물고 C를 쓰는 것이 더 비용 효율적입니다 (동일한 노력으로 15 % 속도 향상 vs. 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

하지만 당신은 정말 해야 성능 차이의 원인을 이해하는 세부 위의 문서를 참조하십시오.

또한 timeit 을 사용하여 코드의 시간을 정해야한다고 강력히 제안합니다 . 하루가 끝나면 for조건이 충족 될 때 루프 에서 벗어날 수있는 상황이있을 수 있습니다 . 호출하여 결과를 찾는 것보다 빠를 수 있습니다 map.


map (), filter () 및 reduce ()에 대해 구체적으로 묻지 만 일반적으로 함수형 프로그래밍에 대해 알고 싶다고 가정합니다. 점 집합 내의 모든 점 사이의 거리 계산 문제에 대해 직접 테스트 한 결과 기능 프로그래밍 (내장 itertools 모듈의 스타 맵 기능 사용)은 루프보다 약간 느리게 나타났습니다 (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')

나는 속도를 테스트하는 간단한 스크립트를 작성했으며 이것이 내가 찾은 것입니다. 실제로 for 루프는 제 경우 가장 빠릅니다. 그것은 정말로 나를 놀라게했습니다. 벨로우즈를 확인하십시오 (제곱합 계산).

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


Alphii answer에 트위스트를 추가하면 실제로 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])

Main changes have been to eliminate the slow sum calls, as well as the probably unnecessary int() in the last case. Putting the for loop and map in the same terms makes it quite fact, actually. Remember that lambdas are functional concepts and theoretically shouldn't have side effects, but, well, they can have side effects like adding to a. Results in this case with Python 3.6.1, Ubuntu 14.04, Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz

0:00:00.257703
0:00:00.184898
0:00:00.031718
0:00:00.212699

I have managed to modify some of @alpiii's code and discovered that List comprehension is a little faster than for loop. It might be caused by int(), it is not fair between list comprehension and for loop.

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

0:00:00.089216

0:00:00.101532

0:00:00.068916

참고URL : https://stackoverflow.com/questions/22108488/are-list-comprehensions-and-functional-functions-faster-than-for-loops

반응형