2023. 1. 2. 17:27ㆍ기술 공부/Python
이번엔 조금 재밌는 것을 다뤄볼 것이다.
바로 내가 만든 custom_min()과
Python이 제공하는 내장 함수 min()의 속도 대결을 붙여볼 것이다.
결론부터 말하자면, Python만을 이용해 만든 함수는
Python이 제공하는 내장 함수를 절-대 이길 수 없다.
바로 도전적으로 코드를 만들어보자.
우선 최솟값을 골라낼 리스트를 먼저 소개하겠다.
import random
_list = random.sample(range(-1000000000, 1000000001), k=100000000)
-10억부터 10억까지의 범위에서 랜덤 하게 1억 개의 숫자를 뽑아서 만든 리스트이다.
그리고 오늘의 도전자. custom_min().
Python이 제공하는 min() 함수를 직접 만들어 보았다.
가장 원초적인 모습으로 만들었다. 직접 따로 만들어보아도 된다.어차피 우승은 내장 함수;
def custom_min(l: list):
minimum = l[0]
for n in l[1:]:
if minimum > n:
minimum = n
return minimum
print(custom_min(_list))
다음으로 챔피언, 내장 함수 min().
print(min(_list))
시간 측정을 위해 time으로 둘둘 말아주고, 바로 돌려보자.
(시간은 컴퓨터의 잉여 자원에 따라 달라질 수 있다.
내장 함수 min()의 속도가 더 빠르다는 사실만 참고하자.)
( + 직접 돌려보는 것은 그다지 추천하지 않는다.)
import time
import random
def custom_min(l: list):
minimum = l[0]
for n in l[1:]:
if minimum > n:
minimum = n
return minimum
_list = random.sample(range(-1000000000, 1000000001), k=100000000)
print('custom_min()')
st = time.time()
print(custom_min(_list))
print(f'{time.time() - st:0.10f} sec')
print()
print('내장 함수 min()')
st = time.time()
print(min(_list))
print(f'{time.time() - st:0.10f} sec')
출력 결과 :
custom_min()
-999999999
6.8395049572 sec
내장 함수 min()
-999999999
2.3972830772 sec
약 4.5초나 차이난다. 왜 어째서일까.
그 이유는 Python의 내장 함수 코드를 보면 알 수 있다.
https://github.com/python/cpython/blob/main/Python/bltinmodule.c
GitHub - python/cpython: The Python programming language
The Python programming language. Contribute to python/cpython development by creating an account on GitHub.
github.com
시작부터 낌새가 이상하다. '. c'로 끝나는 파일이라니.
그렇다. 내장 함수는 Python으로 만들어져 있지 않다.기본적으로 c 언어로 만들어져 있다.
이 Git File의 1837번째 줄에 있는 코드를 보자.
static PyObject *
builtin_min(PyObject *self, PyObject *args, PyObject *kwds)
{
return min_max(args, kwds, Py_LT);
}
builtin_min은 다시 min_max를 호출한다.
그리고 1727번째 줄의 min_max는 대충 이렇게 생겼다.
static PyObject *
min_max(PyObject *args, PyObject *kwds, int op)
{
PyObject *v, *it, *item, *val, *maxitem, *maxval, *keyfunc=NULL;
PyObject *emptytuple, *defaultval = NULL;
static char *kwlist[] = {"key", "default", NULL};
const char *name = op == Py_LT ? "min" : "max";
const int positional = PyTuple_Size(args) > 1;
int ret;
if (positional) {
v = args;
}
else if (!PyArg_UnpackTuple(args, name, 1, 1, &v)) {
if (PyExceptionClass_Check(PyExc_TypeError)) {
PyErr_Format(PyExc_TypeError, "%s expected at least 1 argument, got 0", name);
}
return NULL;
}
emptytuple = PyTuple_New(0);
if (emptytuple == NULL)
return NULL;
ret = PyArg_ParseTupleAndKeywords(emptytuple, kwds,
(op == Py_LT) ? "|$OO:min" : "|$OO:max",
kwlist, &keyfunc, &defaultval);
Py_DECREF(emptytuple);
if (!ret)
return NULL;
if (positional && defaultval != NULL) {
PyErr_Format(PyExc_TypeError,
"Cannot specify a default for %s() with multiple "
"positional arguments", name);
return NULL;
}
it = PyObject_GetIter(v);
if (it == NULL) {
return NULL;
}
if (keyfunc == Py_None) {
keyfunc = NULL;
}
maxitem = NULL; /* the result */
maxval = NULL; /* the value associated with the result */
while (( item = PyIter_Next(it) )) {
/* get the value from the key function */
if (keyfunc != NULL) {
val = PyObject_CallOneArg(keyfunc, item);
if (val == NULL)
goto Fail_it_item;
}
/* no key function; the value is the item */
else {
val = Py_NewRef(item);
}
/* maximum value and item are unset; set them */
if (maxval == NULL) {
maxitem = item;
maxval = val;
}
/* maximum value and item are set; update them as necessary */
else {
int cmp = PyObject_RichCompareBool(val, maxval, op);
if (cmp < 0)
goto Fail_it_item_and_val;
else if (cmp > 0) {
Py_DECREF(maxval);
Py_DECREF(maxitem);
maxval = val;
maxitem = item;
}
else {
Py_DECREF(item);
Py_DECREF(val);
}
}
}
if (PyErr_Occurred())
goto Fail_it;
if (maxval == NULL) {
assert(maxitem == NULL);
if (defaultval != NULL) {
maxitem = Py_NewRef(defaultval);
} else {
PyErr_Format(PyExc_ValueError,
"%s() arg is an empty sequence", name);
}
}
else
Py_DECREF(maxval);
Py_DECREF(it);
return maxitem;
Fail_it_item_and_val:
Py_DECREF(val);
Fail_it_item:
Py_DECREF(item);
Fail_it:
Py_XDECREF(maxval);
Py_XDECREF(maxitem);
Py_DECREF(it);
return NULL;
}
c 언어이니 몰라도 사는 데에는 아무 지장 없다. 알면 좋을 뿐.
더 킹 받는 건, Python으로 만들어지지 않았기 때문에,
Python의 DocString도 존재하지 않는다.
그래서...
PyDoc_STRVAR(min_doc,
"min(iterable, *[, default=obj, key=func]) -> value\n\
min(arg1, arg2, *args, *[, key=func]) -> value\n\
\n\
With a single iterable argument, return its smallest item. The\n\
default keyword-only argument specifies an object to return if\n\
the provided iterable is empty.\n\
With two or more arguments, return the smallest argument.");
'c 언어 파일의' 1843번째 줄에 이렇게 적혀있다...
Python의 help 클래스를 사용해 확인해 보면,
help(min)
출력 결과 :
Help on built-in function min in module builtins:
min(...)
min(iterable, *[, default=obj, key=func]) -> value
min(arg1, arg2, *args, *[, key=func]) -> value
With a single iterable argument, return its smallest item. The
default keyword-only argument specifies an object to return if
the provided iterable is empty.
With two or more arguments, return the smallest argument.
토씨 하나 안 틀리고 그대로 나온다.
그렇다. 내장 함수 min()을 속도로 절-대 이길 수 없는 이유는.
c 언어로 만들어져 있기 때문이다.
그렇기 때문에 min()을 포함한 Python이 제공하는 (또는 cpython으로 구현된)
내장 함수를 사용하는 것이,
메모리 측면에서도 효율적으로 좋고, 속도 측면에서도 훨씬 빠르다.
그럼 왜 때문에 C 언어가 더 빠르고,
왜 때문에 Python을 쓰는 것일까.
우선 C 언어가 Python 보다 빠른 이유를 알아보자.
변수 a와 b에 1을 저장하고,
둘을 더해 변수 c에 저장하는
C 언어 코드가 있다.
int a = 1;
int b = 1;
int c = a + b;
이 코드는 아마도 이렇게 작동할 것이다.
1. inteager 형식의 1을 2~4 Bytes 크기의 메모리에 할당한다.
2. a에 주소값을 저장한다.
3. inteager 형식의 1을 2~4 Bytes 크기의 메모리에 할당하고
4. b에 주소값을 저장한다.
5. a와 b의 값을 더한다.
6. inteager 형식의 2를 2~4 Bytes 크기의 메모리에 할당한다.
7. c에 주소값을 저장한다.
똑같은 Python 언어 코드가 있다.
a = 1;
b = 1;
c = a + b;
이 코드는 아마도 이렇게 작동할 것이다.
1. 메모리에 PyObject를 생성한다.
2. 1을 생성된 PyObject에 할당한다.
3. 저장된 값의 형식을 검사한다.
4. 확인된 형식을 PyObject_HEAD에 저장한다.
5. PyObject가 저장된 주소 값을 a에 저장한다.
6. 메모리에 PyObject를 생성한다.
7. 1을 생성된 PyObject에 할당한다.
8. 저장된 값의 형식을 검사한다.
9. 확인된 형식을 PyObject_HEAD에 저장한다.
10. PyObject가 저장된 주소 값을 b에 저장한다.
11. a의 PyObject에 저장된 PyObject_HEAD의 형식이 정수인 것을 확인한다.
12. b의 PyObject에 저장된 PyObject_HEAD의 형식이 정수인 것을 확인한다.
13. a와 b의 값을 더한다.
14. 메모리에 PyObject를 생성한다.
15. 2를 생성된 PyObject에 할당한다.
16. 저장된 값의 형식을 검사한다.
17. 확인된 형식을 PyObject_HEAD에 저장한다.
18. PyObject가 저장된 주소 값을 c에 저장한다.
내용을 안 보고, 양만 보더라도 벌써 Python이 하는 일이 많다.
하는 일이 더 많다는 것은 시간이 더 오래 걸린다는 이야기와 같다.
그리고 하는 일이 더 많아, 시간이 더 오래 걸린다는 것이
바로 Python이 C 언어보다 느린 주된 이유이다.
그러면 Python은 왜 하는 일이 더 많아질 수밖에 없는 것일까.
그 해답은 Python의 '동적 타이핑'에 있다.
우리는 Python에게 무엇이든 저장하라고 말할 수 있다.
a = print
b = 1
c = 'c'
d = int()
e = 2 ** 65
f = 1.9128
g = list
h = []
i = [[]]
j = "Hi!"
어떻게? PyObject라는 상자를 사용해서.
그렇기 때문에 우리는 더 편하게 Python에게 일을 시킬 수 있다.Python님이 그만큼 더 일한다는 말이다.
Python이 안 하면 우리가 그만큼 상세하게 하나씩 타이핑해줘야 한다는 이야기이다.
빠르게 유튜브 영상을 하나 봐보자.
Python, C/C++, Assembly 언어의 프로그래밍 속도와 실행 속도를 비교한 영상이다.
Python이 코드 작성 속도가 가장 빠른 반면, 실행 속도는 꼴찌로 나오게 된다.빠르게 개발한 후 테스트 실행시켜 놓고 커피와 웹툰을 즐기려면 Python이 최고란 말이다.
추가로, Python은 아까 확인해 보았듯,
C 언어로도 구현이 되어 있고, C 언어가 실행되기도 한다.
이 말을 조금 응용해 보자면, 내장(구현)되어 있는 CPython이 아니어도,
누군가 만들어둔 C 언어 라이브러리를 가져와 사용할 수도 있다는 이야기이다.
즉, Python을 통해 우리는 빠릿빠릿한 C 언어도 누군가느님이 Python에서도 사용할 수 있게 만들어만 주신다면
손쉽게 사용이 가능하다는 이야기이다.
그리고 이 것이 과학 분야에서 Python이 가장 많이 사용되는 큰 이유라고 한다.이 것이 사실인지는 모르겠지만, 틀린 말 같지도 않다.(대부분의 (과학 분야를 포함한) 기계의 소프트웨어가 C 언어로 되어 있는 것 때문이지 않을까 추측해 본다. 아님 말고...)
결론을 지어보자.
C 언어로 구현된 Python 내장 함수는 적극적으로 애용하자.
Python이 메모리를 비효율적으로 많이 사용해도 미워하지 말자.
Python의 실행 속도는 느리지만, 프로그래밍 속도는 빠르다.워라밸에 최적화되어 있다. ㅋ.ㅋ
다 각자의 존재 이유가 있고, 매력이 있다.
정도가 되겠다.
출처
Python Git - cpython/bltinmodule.c at main
https://github.com/python/cpython/blob/main/Python/bltinmodule.c
GitHub - python/cpython: The Python programming language
The Python programming language. Contribute to python/cpython development by creating an account on GitHub.
github.com
Why Python is Slow: Looking Under the Hood
http://jakevdp.github.io/blog/2014/05/09/why-python-is-slow/
Why Python is Slow: Looking Under the Hood | Pythonic Perambulations
So Why Use Python?¶ Given this inherent inefficiency, why would we even think about using Python? Well, it comes down to this: Dynamic typing makes Python easier to use than C. It's extremely flexible and forgiving, this flexibility leads to efficient use
jakevdp.github.io
Python vs C/C++ vs Assembly side-by-side comparison
https://www.youtube.com/watch?v=3PcIJKd1PKU
'기술 공부 > Python' 카테고리의 다른 글
Decorators (0) | 2023.02.01 |
---|---|
Python은 어떻게 작동할까? (2) (2) | 2022.12.30 |
Python은 어떻게 작동할까? (1) (1) | 2022.12.25 |
Python - class, function, method (1) | 2022.12.23 |