>  기사  >  백엔드 개발  >  효율적인 Python 코드

효율적인 Python 코드

巴扎黑
巴扎黑원래의
2017-04-05 15:45:551484검색

제 생각에는 Python 커뮤니티는 Python 2.x 조직, 3.x 조직 및 PyPy 조직의 세 가지 학교로 나누어져 있습니다. 이 분류는 기본적으로 라이브러리 호환성과 속도로 귀결됩니다. 이 기사에서는 몇 가지 일반적인 코드 최적화 기술과 C로 컴파일한 후의 상당한 성능 향상에 중점을 둘 것입니다. 물론 세 가지 주요 Python 장르의 실행 시간도 제공할 것입니다. 내 목표는 하나가 다른 것보다 낫다는 것을 증명하는 것이 아니라, 다양한 상황에서 이러한 특정 예를 사용하여 비교하는 방법에 대한 아이디어를 제공하는 것입니다.

생성기 사용

일반적으로 간과되는 메모리 최적화는 생성기를 사용하는 것입니다. 생성기를 사용하면 한 번에 모든 레코드를 반환하는 대신 한 번에 하나의 레코드만 반환하는 함수를 만들 수 있습니다. python2.x를 사용하는 경우 range 대신 xrange를 사용하거나 필터 대신 ifilter를 사용하는 것입니다. 좋은 예는 큰 목록을 만들고 함께 연결하는 것입니다.

아아아아

속도가 조금 더 빨라질 뿐만 아니라 전체 목록을 메모리에 저장하는 것도 방지됩니다.

Ctype 소개

주요 성능 코드의 경우 Python 자체는 주로 ctypes를 통해 C 메서드를 호출할 수 있는 API를 제공합니다. C 코드를 작성하지 않고도 ctypes를 사용할 수 있습니다. 기본적으로 Python은 미리 컴파일된 표준 C 라이브러리를 제공합니다. 생성기 예제로 돌아가서 ctypes를 사용하여 이를 구현하는 데 시간이 얼마나 걸리는지 살펴보겠습니다.

아아아아

그냥 C의 랜덤함수로 교체했더니 실행시간이 절반 이상 줄었어요! 이제 우리가 더 잘할 수 있다고 말하면 믿으시겠어요?

Cython 소개

Cython은 성능 향상을 위해 C 함수를 호출하고 변수를 선언할 수 있는 Python의 상위 집합입니다. Cython을 사용하려면 먼저 설치해야 합니다.

import timeit
import random

def generate(num):
while num:
yield random.randrange(10)
num -= 1

def create_list(num):
numbers = []
while num:
numbers.append(random.randrange(10))
num -= 1
return numbers
print(timeit.timeit("sum(generate(999))", setup="from __main__ import generate", number=1000))
>>> 0.88098192215 #Python 2.7
>>> 1.416813850402832 #Python 3.2
print(timeit.timeit("sum(create_list(999))", setup="from __main__ import create_list", number=1000))
>>> 0.924163103104 #Python 2.7
>>> 1.5026731491088867 #Python 3.2

Cython은 본질적으로 더 이상 개발되지 않는 또 다른 Pyrex 유사 라이브러리의 포크입니다. Python과 유사한 코드를 Python 파일에서 호출할 수 있는 C 라이브러리로 컴파일합니다. Python 파일에 .py 접미사 대신 .pyx 접미사를 사용하세요. Cython을 사용하여 생성기 코드를 실행하는 방법을 살펴보겠습니다.

아아아아

Cython이 함수를 컴파일하도록 하려면 setup.py를 만들어야 합니다.

아아아아

다음을 사용하여 컴파일:

import timeit
from ctypes import cdll

def generate_c(num):
#Load standard C library
libc = cdll.LoadLibrary("libc.so.6") #Linux
#libc = cdll.msvcrt #Windows
while num:
yield libc.rand() % 10
num -= 1

print(timeit.timeit("sum(generate_c(999))", setup="from __main__ import generate_c", number=1000))
>>> 0.434374809265 #Python 2.7
>>> 0.7084300518035889 #Python 3.2

cython_generator.c 파일과 Generator.so 파일이라는 두 개의 파일을 볼 수 있습니다. 우리는 프로그램을 테스트하기 위해 다음 방법을 사용합니다:

sudo pip install cython

나쁘지 않습니다. 개선할 수 있는 부분이 있는지 살펴보겠습니다. 먼저 "num"을 정수로 선언한 다음 표준 C 라이브러리를 가져와서 임의 함수를 담당할 수 있습니다.

아아아아

컴파일하고 다시 실행하면 이 놀라운 숫자를 볼 수 있습니다.

아아아아

몇 가지 변화만으로도 괜찮은 결과를 얻었습니다. 그러나 때로는 이 변경이 지루할 수 있으므로 일반 Python을 사용하여 이를 수행하는 방법을 살펴보겠습니다.

PyPy 소개 PyPy는 Python 2.7.3용 JIT(Just-In-Time) 컴파일러입니다. 일반인의 관점에서 이는 코드 실행 속도를 높이는 것을 의미합니다. Quora는 프로덕션에서 PyPy를 사용합니다. PyPy의 다운로드 페이지에 몇 가지 설치 지침이 있지만 Ubuntu를 사용하는 경우 apt-get을 통해 설치할 수 있습니다. 작동 방식은 기본적으로 제공되므로 미친 bash나 스크립트 실행이 필요하지 않으며 다운로드하여 실행하기만 하면 됩니다. 원본 생성기 코드가 PyPy에서 어떻게 작동하는지 살펴보겠습니다.

아아아아

우와! 코드 한 줄도 수정하지 않고 순수 파이썬 구현보다 실행 속도가 8배 빠릅니다.

추가 테스트 추가 조사가 필요한 이유는 무엇입니까? PyPy가 챔피언이에요! 전적으로 사실이 아닙니다. 대부분의 프로그램은 PyPy에서 실행될 수 있지만 일부 라이브러리는 완전히 지원되지 않습니다. 게다가 컴파일러를 변경하는 것보다 프로젝트에 대한 C 확장을 작성하는 것이 더 쉽습니다. 조금 더 깊이 파고들어 ctypes를 사용하여 C에서 라이브러리를 작성하는 방법을 살펴보겠습니다. 병합 정렬 속도와 피보나치 수열 계산 속도를 테스트해 보겠습니다. 다음은 우리가 사용할 C 코드(functions.c)입니다:

#cython_generator.pyx
import random

def generate(num):
while num:
yield random.randrange(10)
num -= 1

Linux 플랫폼에서는 다음 방법을 사용하여 공유 라이브러리로 컴파일할 수 있습니다:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

setup(
cmdclass = {'build_ext': build_ext},
ext_modules = [Extension("generator", ["cython_generator.pyx"])]
)

ctypes를 사용하면 이전에 표준 C 라이브러리에서 했던 것처럼 "libfunctions.so" 공유 라이브러리를 로드하여 이 라이브러리를 사용할 수 있습니다. 여기서는 Python 구현과 C 구현을 비교해 보겠습니다. 이제 피보나치 수열 계산을 시작합니다.

python setup.py build_ext --inplace

예상한 대로 C는 Python 및 PyPy보다 빠릅니다. 병합 정렬도 같은 방식으로 비교할 수 있습니다.

우리는 아직 Cypes 라이브러리를 살펴보지 않았기 때문에 이 예제는 Python의 강력한 측면을 반영하지 않습니다. Cypes 라이브러리에는 int, char 배열, float, bytes 등과 같은 몇 가지 표준 유형 제한 사항만 있습니다. 기본적으로 정수 배열은 없지만 c_int(ctype은 int 유형)를 곱하면 이러한 배열을 간접적으로 얻을 수 있습니다. 이는 코드의 7번째 줄에 표시되는 내용이기도 합니다. 우리는 숫자 배열인 c_int 배열을 만들고 이를 c_int 배열로 묶었습니다.

  主要的是c语言不能这样做,而且你也不想。我们用指针来修改函数体。为了通过我们的c_numbers的数列,我们必须通过引用传递merge_sort功能。运行merge_sort后,我们利用c_numbers数组进行排序,我已经把下面的代码加到我的functions.py文件中了。

#Python Merge Sort
from random import shuffle, sample

#Generate 9999 random numbers between 0 and 100000
numbers = sample(range(100000), 9999)
shuffle(numbers)
c_numbers = (c_int * len(numbers))(*numbers)

from heapq import merge
def merge_sort(m):
if len(m) <= 1:
return m
middle = len(m) // 2
left = m[:middle]
right = m[middle:]
left = merge_sort(left)
right = merge_sort(right)
return list(merge(left, right))

start = time.time()
numbers = merge_sort(numbers)
finish = time.time()
print("Python: " + str(finish - start))

#C Merge Sort
start = time.time()
libfunctions.merge_sort(byref(c_numbers), len(numbers))
finish = time.time()
print("C: " + str(finish - start))
Python: 0.190635919571 #Python 2.7
Python: 0.11785483360290527 #Python 3.2
Python: 0.266992092133 #PyPy 1.9
Python: 0.265724897385 #PyPy 2.0b1
C: 0.00201296806335 #Python 2.7 + ctypes
C: 0.0019741058349609375 #Python 3.2 + ctypes
C: 0.0029308795929 #PyPy 1.9 + ctypes
C: 0.00287103652954 #PyPy 2.0b1 + ctypes

  这儿通过表格和图标来比较不同的结果。

효율적인 Python 코드

  Merge Sort Fibonacci
Python 2.7 0.191 1.187
Python 2.7 + ctypes 0.002 0.044
Python 3.2 0.118 1.272
Python 3.2 + ctypes 0.002 0.046
PyPy 1.9 0.267 0.564
PyPy 1.9 + ctypes 0.003 0.048
PyPy 2.0b1 0.266 0.567
PyPy 2.0b1 + ctypes 0.003 0.046

위 내용은 효율적인 Python 코드의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.