본문 바로가기
DEVLOG/개발일기

파이썬(Python) 코드를 효율적으로 작성하는 법 Part 2

2019. 9. 12.
반응형

 

파이썬(Python) 코드를 효율적으로 작성하는 법 Part 1

1. 내장함수(built-in functions)를 사용하라. 파이썬으로도 효율적인 코드를 작성할 수 있습니다. 하지만 C언어로 작성된 내장함수를 이기기는 힘듭니다. 내장함수는 정말 빠릅니다. 파이썬 내장함수는 링크에서..

deepwelloper.tistory.com

1. 직관에 의존하지말고, 툴을 이용해 코드를 프로파일하라

속도 문제는 미묘할 수 있으므로 직관에 의존하면 안됩니다. "cprofile" 모듈을 사용하면 간단하게 파이썬 코드를 프로파일링할 수 있습니다.

python -m cProfile myprogram.py

아래와 같이 테스트 코드를 작성했습니다. 위 그림은 프로파일링 결과입니다. 여기서 bottleneck은 "very_slow()" 함수 호출입니다. "fast()" 와 "slow()"가 각각 200번씩 호출됐다는 것도 알 수 있습니다. 이게 의미하는 것은 만약 "fast()"와 "slow()"를 개선할 수 있다면, 훨씬 뛰어난 퍼포먼스 향상을 보일 수 있다는 것입니다. cprofile 모듈은 런타임중에도 import할 수 있습니다. long-running process를 확인하는데 유용합니다.

import time
def fast():
	time.sleep(0.001)
def slow():
	time.sleep(0.01)
def very_slow():
	for i in xrange(100):
		fast()
        slow()
    time.sleep(0.1)
    
def main():
	very_slow()
    very_slow()

if __name__ == '__main__':
	main()

 

2. 시간복잡도를 검토하라

프로파일링 후에 속도에 대한 기본적인 분석을 해보십시오.상수시간 O(1)은 가장 이상적이고, O(logN)은 안정적입니다. O(N!)은 따질 필요도 없습니다.

O(1) -> O(logN) -> O(NlogN) -> O(N^2) -> O(N^3) -> O(N^k) -> O(k^N) -> O(N!)

 

3. third-party 패키지를 사용하라

많은 고성능 라이브러리가 있습니다. 다음은 몇가지 유용한 패키지들입니다.

  • Numpy : MatLab과 비슷한 오픈소스
  • SciPy : 빠른 수치 처리 라이브러리
  • GPULib : 코드의 속도향상을 위해 GPU를 사용합니다
  • PyPy : 파이썬 코드를 최적화하기 위한 just-in-time 컴파일러
  • Cython : Python 코드를 C로 변환
  • ShedSkin : Python 코드를 C++로 변환

4. 동시성을 위해 multiprocessing module을 사용하라

GIL(Global Interpreter Lock)은 스레드를 직렬화하기 때문에, 파이썬의 멀티스레딩은 멀티프로세서에서 코드의 속도를 높일 수 없습니다. 따라서 파이썬은 스레드 대신 추가 프로세스를 생성하여 GIL에 의해 부과된 제한을 해제할 수 있는 multiprocessing module을 제공합니다. 게다가 이 팁을 외부의 C 코드와 결합해 프로그램을 더 빠르게 만들 수 있습니다.

 

스레드는 자동적으로 같은 메모리 주소 공간과 파일 디스크립터를 공유하기 때문에, 프로세스는 보통 스레드보다 비용이 많이 듭니다. 즉, 프로세스를 만드는데 시간이 더 걸리고 스레드를 만드느 것보다 더 많은 메모리가 필요하다는 것을 의미합니다. 많은 프로세스를 생성할 경우 고려해야할 사항입니다.

from ctypes import *
cdll.LoadLibrary("libc.so.6")
libc = CDLL("libc.so.6")
for i in range(10):
	print(libc.random() %10,

 

5. Let's go native.

So you’ve decided to use native code for performance. With the standard ctypes module, you can directly load compiled binary (.dll or .so files) into Python, without worrying about writing C/C++ code or build dependencies. For example, we wrote a program (on the right) that loads libc to generate random numbers.
However, the overhead of ctypes binding is not light. You should consider ctypes as a quick glue to your OS libraries or device drivers for accessing hardware. There are several libraries such as SWIG, Cython, and Boost Python that introduce much less calling overhead than ctypes does. Here we pick Boost Python library to inter-operate with Python, because Boost Python supports object-oriented features such as class and inheritance. As we can see from the example (on the right), we keep the usual C++ code (line 1~7), and then import it later (line 21~24). The major work here is to write a wrapper (line 10~18).

/*First write a struct*/
struct Hello {
	std::string say_hi()
    {
    	return "Hello world!";
    }
}

/*Wrap the Hello struct for Python*/
#include <boost/python.hpp>
using namespace boost::python;

BOOST_PYTHON_MODULE(mymodule)
{
	class_<Hello>("Hello")
    	.def("say_hi", &Hello::say_hi)
    ;
}
'''
Ready to use in Python
'''
import mymodule
hello = mymodule.Hello()
hello.say_hi() # 'Hello World!'

 

반응형

댓글