Python의 순환 - 비교, 분석 및 성능

파이톤은 현재 가장 유행하는 프로그래밍 언어 중의 하나다.그것은 해석적인 고급 언어로 문법이 우아하고 읽기 쉽다.그러나 파이톤은 보통 자바, C#보다 훨씬 느리다. 특히 C, C++,Fortran이다.때때로 성능 문제와 병목은 응용 프로그램의 가용성에 심각한 영향을 줄 수 있다.
다행히도 대부분의 경우 파이썬 프로그램의 성능을 향상시킬 수 있는 해결 방안이 있다.개발자는 코드의 속도를 높이기 위해 몇 가지 선택을 취할 수 있다.예를 들어 일반적으로 최적화된 Python 내장 또는 제3자 절차를 사용하고 C나 Cython으로 작성합니다.또한 국부 변수를 사용하는 것이 전역 변수를 사용하는 것보다 빠르기 때문에 순환하기 전에 전역 변수를 국부 변수로 복제하는 것은 좋은 방법이다.잠깐만요.
마지막으로, 항상 C, C++, 또는 Cython으로 자신의 Python 함수를 작성해서 프로그램에서 호출하고, Python 병목 규칙을 바꿀 수 있습니다.그러나 이것은 통상적으로 극단적인 해결 방안으로 실천에서 매우 적게 필요하다.
파이썬 순환을 사용할 때, 특히 대량으로 교체되는 상황에서 성능 문제가 발생할 수 있다.코드를 개선하고 더 빨리 실행할 수 있는 유용한 기교가 많은데, 이것은 본문의 범위를 넘어섰다.
이 문서에서는 두 시퀀스를 요소별로 화합할 때의 성능을 비교합니다.
  • while 순환
  • 사용
  • for순환
  • 사용
  • 목록 이해에서 for 순환
  • 사용
  • 타사 라이브러리 사용numpy
  • 그러나 소프트웨어를 개발할 때 성능이 유일한 문제는 아니다.또 도널드 크누트(Donald Knuth)의'컴퓨터 프로그래밍 아트'(Art of Computer Programming)에 따르면 "조기 최적화는 프로그래밍에서 모든 악(또는 최소한 대부분)의 근원"이라고 말했다.어쨌든, "가독성이 중요하다."는 짐 피터스가 파이톤선에서 말한 바와 같다.

    문제 진술


    우리는 원소에 따라 두 서열에 대해 화합을 구하려고 시도할 것이다.다시 말하면, 우리는 두 개의 크기가 같은 서열 (목록이나 그룹) 을 가져오고, 입력에 해당하는 요소를 추가해서 얻은 요소를 사용하여 세 번째 서열을 만들 것이다.

    준비 작업


    Python 내장 패키지 random을 가져오고 0 ~ 99 (포함) 의 위조 랜덤을 10만 개 포함하는 목록 r:
    import random
    r = [random.randrange(100) for _ in range(100_000)]
    
    타사 패키지numpy도 사용할 예정이므로 가져오도록 하겠습니다.
    import numpy as np
    
    우리는 출발할 준비가 되었다!

    단순 순환


    우선 간단한 파이썬 순환을 살펴보자.
    순수한 Python 사용
    우리는 두 개의 목록부터 시작할 것이다. 목록마다 1000개의 요소를 포함한다.정수 변수 n은 각 목록의 길이를 나타냅니다.목록 x 및 y는 r에서 n개의 요소를 무작위로 선택하여 얻을 수 있습니다.
    n = 1_000
    x, y = random.sample(r, n), random.sample(r, n)
    
    n개의 원소를 포함하는 새로운 목록 z를 얻는 데 얼마나 걸릴지 봅시다. 모든 원소는 x와 y에 해당하는 원소의 총체입니다.
    먼저 while 루프의 성능을 테스트합니다.
    %%timeit
    i, z = 0, []
    while i < n:
        z.append(x[i] + y[i])
        i += 1
    
    출력:
    루프당 160ºs±1.44ºs(7회 실행 평균±표준 편차, 회당 10000º)timeit의 출력은 여러 가지 요소에 따라 달라질 수 있으니 주의하십시오.
    Python의 for 순환은 이러한 상황에 대해 더욱 좋은 최적화를 진행했는데 그것이 바로 교체 집합, 교체기, 생성기 등이다.어떻게 작동하는지 살펴보겠습니다.
    %%timeit
    z = []
    for i in range(n):
        z.append(x[i] + y[i])
    
    출력:
    회로당 122ºs±188ns(7회 운행의 평균치 ±표준 편차, 회당 10000회로)
    이런 상황에서 for 순환은while보다 빠르지만 우아하다.
    목록 이해는 일반 for 순환과 매우 비슷합니다.그것들은 간단한 상황(예를 들어 본례)에 적용된다.더 빡빡한 것을 제외하고는 약간의 지출을 없앴기 때문에, 그것들은 통상적으로 약간 빠르다.
    %%timeit
    z = [x[i] + y[i] for i in range(n)
    
    출력:
    회로당 87.2 ºs±490ns(7회 운행의 평균치 ±표준 편차, 회당 10000회로)
    순환이 필요할 때 모든 상황에서 목록 이해를 적용할 수 없다는 것을 기억하십시오.일부 더 복잡한 상황은 일반적인 for 심지어while 순환을 필요로 한다.
    Python과 NumPy를 결합하여 사용numpy는 제3자 Python 라이브러리로 보통 수치 계산에 사용된다.그것은 수조를 조작하기에 특히 적합하다.그것은 많은 유용한 절차를 제공하여 수조를 처리하지만, 순환이 필요 없이 간결하고 우아한 코드를 작성할 수 있다.
    실제로 순환과 기타 성능의 관건적인 조작은 모두 낮은 수준numpy에서 이루어진다.이것은 numpy 루틴이 순수한 Python 코드보다 훨씬 빠르다.또 하나의 장점은 numpy 변수와 유형을 처리하는 방식이다.
    먼저 Python 정수 x와 y 목록을 사용하여 해당하는 numpy 64비트 정수 배열을 만듭니다.
    x_, y_ = np.array(x, dtype=np.int64), np.array(y, dtype=np.int64)
    
    x u 및 y u 요소 두 개numpy를 x u+y u만큼 간단하게 배열합니다.하지만 성능을 점검해 보겠습니다.
    %%timeit
    z = x_ + y_
    
    출력:
    회로당 1.03ºs±5.09na초(7회 운행의 평균치 ±표준 편차, 회로당 1000000개)
    이것은 우리가 목록을 사용해서 이해하는 속도보다 85배 가까이 빠르다.코드가 아주 간단하고 우아해요.numpy 대규모 어레이를 사용하는 경우 어레이가 더 적합할 수 있습니다.데이터가 클수록 성능의 이점은 대개 더 큽니다.
    그렇게 지도 모른다, 아마, 아마...64비트 정수 대신 32비트 정수를 사용하기로 합의한 경우 메모리와 시간을 절약할 수 있습니다.
    x_, y_ = np.array(x, dtype=np.int32), np.array(y, dtype=np.int32)
    
    이 두 어레이를 이전처럼 추가할 수 있습니다.
    %%timeit
    z = x_ + y_
    
    출력:
    회로당 814나초 ±5.8나초(7회 운행의 평균치 ±표준 편차, 회로당 1000000나초)
    n보다 크면 10 000 및 100 000에 대한 결과는 다음과 같습니다.이 예에서처럼 numpy를 사용할 때 성능이 더욱 향상되는 동일한 관계를 나타낸다.

    네스트된 주기


    이제 끼워 넣은 파이톤 순환을 비교해 봅시다.
    순수한 Python 사용
    우리는 x와 y라는 두 개의 목록을 다시 사용할 것이다.목록마다 1000개의 위조 랜덤 정수 요소를 포함하는 내부 목록이 100개씩 포함됩니다.따라서 x와 y는 실제로 100행과 1000열을 가진 행렬을 대표한다.
    m, n = 100, 1_000
    x = [random.sample(r, n) for _ in range(m)]
    y = [random.sample(r, n) for _ in range(m)]
    
    두 개의 중첩 while 사이클을 사용하여 성능을 추가하는 방법을 살펴보겠습니다.
    %%timeit
    i, z = 0, []
    while i < m:
        j, z_ = 0, []
        while j < n:
            z_.append(x[i][j] + y[i][j])
            j += 1
        z.append(z_)
        i += 1
    
    출력:
    회로당 19.7ms±271µs(7회 운행의 평균치 ±표준 편차, 회로당 100개
    마찬가지로, 우리는 플러그인 for 순환을 통해 몇 가지 성능 개선을 얻을 수 있다.
    %%timeit
    z = []
    for i in range(m):
        z_ = []
        for j in range(n):
             z_.append(x[i][j] + y[i][j])
        z.append(z_)
    
    출력:
    회로당 16.4ms±303‰s(7회 운행의 평균치 ±표준 편차, 회로당 100개)
    어떤 경우, 플러그인 for 순환은 목록 이해와 함께 사용할 수 있어 또 다른 장점을 실현할 수 있다.
    %%timeit
    z = [[x[i][j] + y[i][j] for j in range(n)] for i in range(m)]
    
    출력:
    회로당 12.1ms±99.4ºs(7회 운행의 평균치 ±표준 편차, 회로당 100개)
    우리는 플러그인 순환의 경우 목록 이해가 일반 for 순환보다 빠르고 후자가while보다 빠르다는 것을 알 수 있다.
    이 예에서는 목록마다 100.000(100×1.000)의 정수 요소가 있다.이 예는 100.000개의 요소와 하나의 순환을 포함하는 예보다 조금 느리다.이것은 모든 세 가지 방법 (목록 이해, 일반 for,while 순환) 의 결론이다.
    Python과 NumPy를 결합하여 사용numpy는 다차원 그룹을 처리하기에 매우 적합하다.x와 y를 사용하여 적절한 numpy 64비트 정수 배열을 만듭니다.
    x_, y_ = np.array(x, dtype=np.int64), np.array(y, dtype=np.int64)
    
    성능을 점검해 보겠습니다.
    %%timeit
    z = x_ + y_
    
    출력:
    회로당 69.9‰s±909나노초(7회 운행의 평균치 ±표준 편차, 회당 10000회로)
    이는 리스트 이해보다 173배 빠르다.그러나 32비트 정수를 사용하면 속도가 더 빨라질 수 있습니다.
    x_, y_ = np.array(x, dtype=np.int32), np.array(y, dtype=np.int32)
    
    성능 검사는 앞에서 설명한 바와 같습니다.
    %%timeit
    z = x_ + y_
    
    출력:
    회로당 34.3ºs±44.6ns(7회 운행의 평균치 ±표준 편차, 회로당 10000개)
    이것은 64비트 정수보다 두 배 빠르다.

    결과 요약


    다음 표는 결과를 요약한 것입니다.
    원소수, m×n
    1×1.000
    1×10.000
    1×100.000
    100×1.000
    Pythonwhile160μs
    1.66ms
    17.0ms
    19.7ms
    Pythonfor122μs
    1.24ms
    13.4ms
    16.4ms
    목록 이해 기능이 있는 파이톤for87.2μs
    894μs
    8.92ms
    12.1msnumpy 64비트 정수
    1.03μs
    6.36μs
    71.9μs
    69.9μsnumpy 32비트 정수
    814나초
    2.87μs
    34.1μs
    34.3μs

    결론


    본고는 원소에 두 개의 목록이나 그룹을 추가할 때 파이톤이 순환하는 성능을 비교하였다.그 결과 리스트 이해는 일반 for 순환보다 빠르고 후자는while 순환보다 빠르다.이 세 가지 상황에서 단순 순환의 속도는 모두 끼워 넣는 순환보다 약간 빠르다.numpy가 제공하는 규칙과 연산자는 코드량을 크게 줄이고 실행 속도를 높일 수 있다.그것은 일차원과 다차원 그룹을 처리할 때 특히 유용하다.
    여기에서 얻은 결론이나 결과 간의 관계가 모든 상황에서 적용되거나 유효하거나 유용한 것은 아니라는 것을 명심하세요!그것들은 설명하기 위해서다.저효율을 처리하는 정확한 방법은 병목을 발견하고 자신의 테스트를 실행하는 것이다.


    좋은 웹페이지 즐겨찾기