레이블이 python인 게시물을 표시합니다. 모든 게시물 표시
레이블이 python인 게시물을 표시합니다. 모든 게시물 표시

20130831

python - cython

나는 Python이 좋다. 하지만 Python은 느리다
뭐가 느리냐 하면 지명적인 것이지만 사칙연산이 느리다. 하지만 동적으로 형이 정의되는 다른 스크립트 언어(Perl, Ruby, Javascript)도 C, Java처럼 컴파일을 하는 정적형 정의 언어에 비해 압도적으로 느리다.(최근에는 Javascript처럼 그물게 진보한 언어도 있으므로 필히 그렇다라고는 말할수 없다.)
스크립트 언어가 느린 원인중 하나는 변수의 형이 지정되어 있지 않으므로 형 체크를 매회 수행할 필요가 있기때문이다.이런 특징으로 인해 자동적으로 형을 변화해 오버로드를 방지해 주는 장점도 있지만 어찌하던간 정적형 지정언어보다가는 속도를 낼수 없다.
그렇다면 Python의 코드에 형지정을 더하여 컴파일을 해버리면 되지않는가라는 것이 Cython이다. 정확하게 말하면 Python라이크 문법으로 적을 코드를 C/C++로 변환하여 컴파일 한다. 거짓말을 보태어 단순한 계산의 경우 Python으로 코드를 실행하는것 보다 100배 이상도 속도를 낼수 있다고 한다. 자 그럼 실험으로 확인해 보자

Cython의 기본적인 사용방법은 공식 다큐멘트를 읽어 보도록 하자
일본어판
주의:이하의 벤치마크는 MacOS 10.7.3 MacBook Core2 Duo 2.26GHz에서 Python과 C/C++의 컴파일러로 Mac표준의 Python2.7.1, llvm-gcc를 사용하고 있다.
C/C++는 time커맨드, Python/Cython는 ipython의 timeit을 이용하여 실험시간을 측정한다.
그리고 이하의 기록은 환경과 구현에 따라 결과가 다를수 있다.

단순한 가산

먼저 단순한 가산을 비교해 보자. 1~100000000의 수를 더한다.
C
$ gcc -O2 simple_add.c
$ time ./a.out

real     0m0.004s
user     0m0.001s
sys     0m0.003s
Python
In [1]: import test_py
In [2]: timeit -n1 test_py.simple_add(100000000)
1 loops, best of 3: 8.39 s per loop
Python 느리다! C는 아마도 너무 빨라서 측정할수 없다.
그러면 Cython을 사용해보자. pyximport를 사용하면 Cython가 import시에 컴파일 해준다. 먼저 Python의 코드를 변경하지말고 그냥 컴파일 해보자.
Python/pyximport+.py
In [1]: import pyximport; pyximport.install(pyimport = True)
In [2]: import test_py
In [3]: timeit -n1 test_py.simple_add(100000000)
1 loops, best of 3: 4.27 s per loop
아무것도 하지않고 2배정도 빨라졌다! 이것만으로도 만족스러운 결과다.
다음은 역시 코드에 손을 대지 않고 확장자를 Cythonの.pyx로 변경해 보자.
Cython/pyximport+.pyx
In [1]: import pyximport; pyximport.install()
In [2]: import test_cy
In [3]: timeit -n1 test_cy.simple_add(100000000)
1 loops, best of 3: 4.36 s per loop
이전의 결과랑 거의 다르지 않다.
pyx의 형으로 지정하여 실행해보자. cdef의 형을 지정하는 것으로 C의 변수를 사용하므로 빨라 질것이다.
Cython/cdef
In [1]: import pyximport; pyximport.install()
In [2]: import test_cy
In [3]: timeit -n1 test_cy.simple_addc(100000000)
1 loops, best of 3: 1.19 us per loop
・・・( ゚д゚)
Python과 차원이 다르다라고 할까. 놀랄만한 결과다. C의 시간이 time커맨드에서는 측정불능이였는데 이에 비등할정도의 속도?
결과:

time(s)Python과의 상대효율(배)
C측정불능측정불능
Python8.391.0
Python/pyximport+.py4.271.96
Cython/pyximport+.pyx4.361.92
Cython/cdef0.000001197050420

if문과 동적배열

다음은 if문을 사용해 10000000까지의 홀수를 동적으로 배열에 넣는 처리를 해보자
C++는 익숙하지 않지만 vector를 사용해 구현해보았다. Python은 list.append를 사용했다.
C++/vector
$ g++ -O2 odd.cpp
$ time ./a.out

real     0m0.195s
user     0m0.083s
sys     0m0.106s
Python/list
In [1]: import test_py
In [2]: timeit -n1 test_py.odd(10000000)
1 loops, best of 3: 2.24 s per loop
list.append는 있을수 없어! Python을 사용하다면 리스트 내장표기법이 상식! 이라는 말을 하시는 분이 계실것 같아 리스트 내장표기로도 실험해 보자
Python/list내장 표기
In [1]: import test_py
In [2]: timeit -n1 test_py.odd_listcomp(10000000)
1 loops, best of 3: 1.64 s per loop
역시 내장표기법이 확실히 빠르다. 하지만 C++에 비해 느리다
Cythonで은 문제없이 리스트 내장 표기를 사용할수 있으므로 Python의 코드에 형 지정만 더하여 컴파일 한다.
지금부터는 setup.py를 준비해 컴파일을 할것이다.
Cython/cdef+list내장표기
In [1]: import test_cy
In [2]: timeit -n1 test_cy.odd_listcomp(10000000)
1 loops, best of 3: 288 ms per loop
C++보다는 느리지만 원래의 Python보다 현저히 빨라졌다. Cython에서도 리스트 내장표기를 사용할수 있는것이 맘에 든다.
다음은 Cython에서 C++의 vector를 불러내 봤다. 실은 Cython에서는 C++의 표준 라이브러리를 간단하게 사용할수 있도록 되어 있어 deque, list, map, pair, queue, set, stack, vector가 지원되고 있다고 한다. vector<int>가 vector[int]와 같이 조금 문법의 차이가 있지만 Python에서 C++를 적는 느낌이 들었다.
Cython/vector
In [1]: import test_cy
In [2]: timeit -n1 test_cy.odd_vec(10000000)
1 loops, best of 3: 97.4 ms per loop
아니 이럴수가? C++보다 빠르다. C++는 프로그램전체 실행시간을 time커맨드로 측정하고 있는 반면에 Cython은 ipython로 import한후 함수만 불러내므로 유리한 조건이지만 이정도로까지 속도가 좋을줄이야 상상도 하지 못했다.
결과:

time(s)Python과의 상대효율(배)
C++/vector0.19511.5
Python/list.append2.241.0
Python/list내장표기1.641.37
Cython/cdef+list내장표기0.2887.78
Cython/vector0.097423.0

배열에 대한 계산

세번쨰는 NumPy랑도 비교해 보자. 1~10000000의 수를 저장한 배열 전체에서 log를 뽑아 한개씩 읽어 합산을 한다.
NumPy이외는 log와 sum의 계산을 한번 해봐도 좋지만 NumPy에 유리하도록 log와 sum의 일정을 나누어 놓는다. 그리고 만일을 위해 계산결과를 표시한다.
먼저 C++과 Python부터터
C/vector+math.log
$ g++ -O2 logsum.cpp
$ time ./a.out
1.51181e+08

real     0m0.742s
user     0m0.515s
sys     0m0.197s
Python/math.log
In [1]: import test_py
In [2]: li = [i for i in xrange(1, 10000000)]
In [3]: timeit -n1 test_py.logsum(li)
151180949.369
151180949.369
151180949.369
1 loops, best of 3: 3.48 s per loop
다음은 Cython으로 C++와 같이 vector를 사용한다. 또 Python의 math를 사용하지 않고 C의 math.h를 사용하는 쪽이 빠를것 같아 사용한다.
Cython/vector+cdef math.log
In [1]: import test_cy
In [2]: li = [i for i in xrange(1, 10000000)]
In [3]: timeit -n1 test_cy.logsum(li)
151180949.369
151180949.369
151180949.369
1 loops, best of 3: 850 ms per loop
결과가 좋다. C++에 근접한 속도가 나왔다.
다음은 Python으로 행열계산이 장점인 NumPy를 사용해 보자
Python/Numpy
In [1]: import test_py
In [2]: li = [i for i in xrange(1, 10000000)]
In [3]: timeit -n1 test_py.logsum_numpy(li)
151180949.369
151180949.369
151180949.369
1 loops, best of 3: 2.09 s per loop
단순히 Python으로 적는것 보다 빠르지만 Cython/vector의 결과를 봐버리며 
NumPy는 SIMD명령에 대응하고 있으로 더 빠를것이라고 생각했는데 매우 실망했다.
NumPy는 익숙해지면 행열에 대한 계산을 알기쉽게 컴팩트하게 적을수 있지만 속도를 낼려면 Cython으로 적어야 되지 않을까 한다.
결과:

time(s)Python과의 상대효율(배)
C++/vector+math.log0.7424.69
Python/math.log3.481.0
Cython/vector+cdef math.log0.8504.09
Python/NumPy2.091.67

*경품

Cython은 NumPy에도 지원하고 있는듯 하므로 어디까지 고속화 할수 있는건가 실험해보자
10000*10000의 행열에 0부터의 순으로 넣어 모든값에 +1한후 행단위로 sum으로 나누어 행열 전체의 합산을 구한다.
행단위의 합산으로 나누면 행의 합계는 1.0이 될것이다. sum=1*1000이 될것이다.
Python/Numpy
In [1]: import test_py
In [2]: timeit -n1 test_py.dim2sum_numpy(1000)
1000.0
1000.0
1000.0
1 loops, best of 3: 869 ms per loop
Cython/Numpy+cdef int
In [1]: import test_cy
In [2]: timeit -n1 test_cy.dim2sum_numpy(1000)
1000.0
1000.0
1000.0
1 loops, best of 3: 621 ms per loop
Cython은 Python/NumPy의 코드의 루프를 사용하는 변수를 int형으로 지정만 하였다. 극적인 스피드업은 없지만 거의 코드의 수정없이 좋은 결과가 나왔다.
다음은 cimport로 NumPy를 사용하겠다. 이렇게 하면 NumPy의 배열에 고속으로 접근할수있다고 한다.
Cython/cimport Numpy+cdef int
In [1]: import test_cy
In [2]: timeit -n1 test_cy.dim2sum_pynumpy(1000)
1000.0
1000.0
1000.0
1 loops, best of 3: 630 ms per loop
엥? 바뀌지 않았다. cimport로 효과가 있는것은 인수에 NumPy의 배열을 받을때 뿐인가?
음.. 공부부족!
결과:

time(s)Python과의 상대효율(배)
Python/NumPy0.8691.0
Cython/NumPy+cdef int0.6211.4
Cython/cimport NumPy+cdef int6301.38

추기(3/10):
@lucidfrontier45씨의 시간이 바뀌지 않는것은 numpy.ndarray를 cdef로 지정하지 않았기때문이라고 갈켜주셨다.
게다가 원래 ndarray의 배열의 참조는 foo[i][j]보다가도 foo[i,j]쪽이 빠르다는것도 알았으므로 이 두 부분을 수정해 실행해보았다.
결과(수정판):

time(s)Python과의 상대효율(배)
Python/NumPy0.3501.0
Cython/NumPy+cdef int0.2221.58
Cython/cimport NumPy+cdef int0.04318.12
Cython를 사용하지 않아도 2차윈의 ndarray의 참조 방법을 바꾸는 것만으로 빨라 졌다. 이것은 이후 신경쓰지 않으면... 
Cython쪽은 ndarray를 cdef로 하면 매우 빨라지는것을 확인할수 있었다. 이번의 코드만으로 ndarray.sum()를 사용하고 있는 부분이 많지만 배열의 순차 참조부분이 많을수록 차이는 더 커질것이라고 생각한다.
업로드한 소스도 수정했다.

다른분의 벤지마크:

Python을 고속화하는 여러방법들의 벤치마크가 적혀있다.
Python2.7 < PyPy1.5 < Cython < Cython+numpy < ShedSkin
의 순서로 빨랐다고 한다. ShedSkin은 Cython과 동 사양에 Python을 C++로 변환하는 것같은데 아직은 실험단계?

Python < NumPy < Weave < Cython
Weave라는 것은 SciPy의 모듈로써 Python의 코드에 인라인으로 C의 코드를 적을수 있는 것같다.

결론

이번의 결과와 필자의 스킬, 가능성등을 생각하면
Python << NumPy < Cython < Cython+NumPy
*예외로서 호환성을 완전히 배제하고 거의 C의 Cython
이라는 느낌이랄까
NumPy와 Cython을 공부해 가는 동안 순위가 바뀔가능성이 클것 같다.

사용한 소스코드는 github에 업로드했다.


출처 : http://blog.naver.com/parkjy76?Redirect=Log&logNo=30156508008


20130827

python daemonize

대충 수정해서...

run 부분을 override 해서 사용하면 될듯...




[code]

20130823

python os.glob

 glob 모듈은 윈도우의 dir 명령어나 리눅스의 ls 명령어와 유사한 기능을 제공합니다.

glob.glob(path)
glob() 함수는 경로에 대응되는 모든 파일 및 디렉터리의 리스트를 반환합니다.
'*'와 '?'도 사용가능하며 "[""]"를 사용한 문자열 비교도 가능합니다.
>>> glob.glob('*')
['DLLs', 'Doc', 'include', 'Lib', 'libs', 'LICENSE.txt', 'NEWS.txt', 'python.exe', 'pythonw.exe', 'README.txt', 'tcl', 'Tools', 'w9xpopen.exe']
>>> glob.glob('./[0-9].*')
['./1.gif', './2.txt']
>>> glob.glob('*.gif')
['1.gif', 'card.gif']
>>> glob.glob('?.gif')
['1.gif']
>>> glob.glob('*.exe')
['python.exe', 'pythonw.exe', 'w9xpopen.exe']
>>> from os.path import *
>>> glob.glob(abspath('.' + '\\*.exe'))
['C:\\Python30\\python.exe', 'C:\\Python30\\pythonw.exe', 'C:\\Python30\\w9xpopen.exe']

glob.iglob(path)
glob과 동일한 동작을 수행하지만, 리스트로 결과를 반환하는 것이 아니라 이터레이터를 반환.
한번에 모든 결과를 리스트에 담지 않으므로, 결과가 매우 많은 경우 유용하게 쓰일 수 있다.
>>> glob.iglob('*')
<generator object iglob at 0x012230D0>
>>> for i in glob.iglob('*'):
    print (i)

    
DLLs
Doc
include
Lib
libs
LICENSE.txt
NEWS.txt
python.exe
pythonw.exe
README.txt
tcl
Tools
w9xpopen.exe


[ Example ] - Tree 예제
w tree는 하위 디렉터리 구조를 보여 주는 툴입니다.
os.path와 glob의 일부 기능을 이용해서 tree와 비슷한 기능을 수행하는 예제 작성.

[ tree.py 소스코드 ]

[ 실행결과 ]




출처 : http://devanix.tistory.com/299

python regular expression

[반복 메타 문자]
[메타 문자]
[의미]
*
0회 이상 반복
+
1회 이상 반복
?
0회 or 1회
{m}
m회 반복
{m, n}
m회부터 n회까지 반복

[ 매칭 메타 문자 ]
[메타 문자]
[의미]
.
줄바꿈 문자를 제외한 모든 문자와 매치됨
^
문자열의 시작과 매치됨
$
문자열의 마지막과 매치됨
[ ]
문자 집합 중 한 문자를 의미
|
또는(or)를 의미
{ }
정규식을 그룹으로 묶음

[ 이스케이프 기호 ]
[종류]
[설명]
\\
역슬래쉬 문자 자체
\d
모든 숫자와 매치됨 [0-9]
\D
숫자가 아닌 문자와 매치됨 [^0-9]
\s
화이트 스페이스 문자와 매치됨 [ \t\n\r\f\v]
\S
화이트 스페이스가 아닌 것과 매치됨 [^ \t\n\r\f\v]
\w
숫자 또는 문자와 매치됨 [a-zA-Z0-9_]
\W
숫자 또는 문자가 아닌 것과 매치됨 [^a-zA-Z0-9_]
\b
단어의 경계를 나타냄. 단어는 영문자 혹은 숫자의 연속 문자열
\B
단어의 경계가 아님을 나타냄
\A
문자열의 처음에만 일치
\Z
문자열의 끝에만 일치

[ 최소 매칭을 위한 정규식 ]
[기회]
[의미]
*?
*와 같으나 문자열을 취소로 매치함
+?
+와 같으나 문자열을 취소로 매치함
??
?와 같으나 문자열을 최소로 매치함
{m,n}?
{m,n}과 같으나 문자열을 최소로 매치함

[ 정규 표현식에서 사용 가능한 플래그 ]
[플래그]
[내용]
I, IGNORECATE
대, 소문자를 구별하지 않는다
L, LOCATE
\w, \W, \b, \B를 현재의 로케일에 영향을 받게 한다
M, MULTILINE
^가 문자열의 맨 처음, 각 라인의 맨 처음과 매치 된다
$는 문자열의 맨 끝, 각 라인의 맨 끝과 매치
S, DOTALL
.을 줄바꾸기 문자도 포함하여 매치하게 한다
U, UNICODE
\w, \W, \b, \B가 유니코드 문자 특성에 의존하게 한다
X, VERBOSE
정규식 안의 공백은 무시된다

[ re모듈의 주요 메소드 ]
[메소드]
[설명]
compile(pattern[, flags])
pattern을 컴파일하여 정규식 객체를 반환
match(pattern, string[,flags])
string의 시작부분부터 pattern이 존재하는지 검사하여
MatchObject 인스턴스를 반환
search(pattern, string[,flags])
string의 전체에 대해서 pattern이 존재하는지 검사하여
MatchObject 인스턴스를 반환
split(pattern, string[, maxplit=0])
pattern을 구분자로 string을 분리하여 리스트로 반환
findall(pattern, string[, flags])
string에서 pattern을 만족하는 문자열을 리스트로 반환
finditer(pattern, string[, flags])
string에서 pattern을 만족하는 문자열을 반복자로 반환
sub(pattern, repl, string[, count=0])
string에서 pattern과 일치하는 부분에 대하여
repl로 교체하여 결과 문자열을 반환
subn(pattern, repl, string[, count=0])
sub와 동일하나, 결과로(결과문자열, 매칭횟수)를
튜플로 반환
escape(string)
영문자 숫자가 아닌 문자들을 백슬래쉬 처리해서 리턴.
(임의의 문자열을 정규식 패턴으로 사용할 경우 유용)

[ Match 객체 ]
Match객체는 match(), search()의 수행 결과로 생성되며, 검색된 결과를 효율적으로 처리할 수 있는 기능 제공.
◎ Match객체가 지원하는 메소드와 속성
[메소드]
[속성]
group([group1, ...])
입력받은 인덱스에 해당하는 매칭된 문자열 결과의 부분 집합을 반환합니다.
인덱스가 '0'이거나 입력되지 않은 경우 전체 매칭 문자열을 반환합니다.
groups()
매칭된 결과를 튜플 형태로 반환
groupdict()
이름이 붙여진 매칭 결과를 사전 형태로 반환
start([group])
매칭된 결과 문자열의 시작 인덱스를 반환. (인자로 부분 집합의 번호나
명시된 이름이 전달된 경우, 그에 해당하는 시작 인덱스를 반환)
end([group])
매칭된 결과 문자열의 종료 인덱스를 반환. (인자로 부분 집합의 번호나
명시된 이름이 전달된 경우, 그에 해당하는 종료 인덱스를 반환)
pos
원본 문자열에서 검색을 시작하는 위치입니다.
endpos
원본 문자열에서 검색을 종료하는 위치입니다.
lastindex
매칭된 결과 집합에서 마지막 인덱스 번호를 반환. (일치된 결과가 없는 경우
에는 None을 반환)
lastgroup
매칭된 결과 집합에서 마지막으로 일치한 이름을 반환. (정규식의 매칭 조건에
이름이 지정되지 않았거나 일치된 결과가 없는 경우 None 반환)
string
매칭의 대상이 되는 원본 문자열입니다.


[ 예제 ] - re 모듈 함수
▶ re.match()와 re.search()의 차이
re.match()의 경우 대상 문자열의 시작부터 검색을 하지만,
re.search()함수는 대상 문자열 전체에 대해서 검색을 수행한다.
예) 아래와 같이 검색의 대상이 되는 문자열에 공백이 있는 경우나 또는
검색 키워드와 일치하는 문자열이 대상 문자열의 중간 이후에 존재하는 경우
re.match()함수는 검색을 못함.

▶ re.split() - 대상 문자열을 입력된 패턴을 구분자로 하여 분리

▶ re.findall() - 검색 문자열에서 패턴과 매칭되는 모든 경우를 찾아 리스트로 반환

▶ re.sub() - 패턴과 일치하는 문자열 변경
 또한 변경할 문자열에 대해서 매칭된 문자열을 사용할 수도 있다.


[ 예제 ] - 정규 표현식 객체
▶ 동일한 패턴을 연속적으로 검색하는 경우, 정규식을 컴파일하여 정규표현식 객체를 생성.

▶ re.IGNORECASE 플래그로 대소문자 구분하지 않고 매칭 작업 수행

▶ re.MULTILINE 플래그를 설정하여 빈 라인을 제외하고 라인별로 분리


[ 예제 ] - Match 객체
▶ 일반적인 형식의 전화번호를 인삭하여 Match 객체가 지원하는 메소드 분석.

▶ 정규식 작성시 '(?<이름>..)' 형식으로 매칭 결과에 대해 이름을 부여하고,
groupdict() 메서드를 이용하여 사전 형태로 이름과 검색된 문자쌍을 얻음.


    
로우 문자열 표기법 (Raw string notation)
이스케이프 문자열을 표현하기 위하여 '\'(백슬래쉬)문자를 사용하기 때문에, 문자 '\'를 정규표현식
으로 표현하기 위해서는 '\\\\'로, 일반 문자열에선느 '\\'로 표현해야 합니다. 그래서 '\apple'이란
문자열을 검색하기 위해서는 아래와 같이 매우 복잡한 형식으로 표현해야 합니다.


로우 문자열 표기법은 문자열 앞에 'r'을 더한 것으로, \(백슬래쉬) 문자를 이스케이프 문자열로 처리
하지 않고 일반 문자와 동일하게 처리합니다. 이렇게 함으로써 정규표현식 및 문자열에서 '\'를 간단
하게 표현할 수 있습니다. 일반적으로 정규표현식에 사용되는 문자열에서는 이러한 편리함 때문에
많이 사용합니다.




출처 : http://devanix.tistory.com/296

Articles