Python은 왜 이렇게 느릴까?

토즐라·2022년 9월 6일
2

1D1Q

목록 보기
6/7
post-thumbnail

Question

Python은 왜 이렇게 느릴까? 🤔

00. 개요

(출저: https://github.com/niklas-heer/speed-comparison)


같은 환경 하에서 파이썬은 다른 주요한 언어들(C, Java, C++, Javascript 등)에 비해 실행 속도가 느리다고 알려져 있습니다. C랑 비교를 했을 때, 일반적으로 C언어로 구현된 코드는 파이썬으로 구현된 코드보다 100~300배정도 빠르다고 합니다. 그렇다면 파이썬은 실행 속도가 왜 실행 속도가 느린 건지 차근차근 알아보겠습니다.


01. 동적 타이핑 언어

동적 타이핑 언어란?

A statically-typed language is a language (such as Java, C, or C++) where variable types are known at compile time. In most of these languages, types must be expressly indicated by the programmer; in other cases (such as OCaml), type inference allows the programmer to not indicate their variable types.
Dynamically-typed languages are those(like JavaScript) where the interpreter assigns variables a type at runtime based on the variable's value at the time.


MDN Web Docs Glossary

정적 타이핑 언어란 자료형을 컴파일할 때 결정하는 것으로, 변수에 들어갈 값의 형태에 따라 자료형을 사전에 지정해야 합니다. 정적 타이핑을 이용하는 대표적인 언어로는 C, C++, Java등이 있습니다.

동적 타이핑 언어란 자료형을 컴파일이 아닌 런타임 시 결정하는 것으로, 자료형의 명시 없이 변수명만 가지고 프로그램이 알아서 선언 및 값을 전달하는 것이 가능합니다. 동적 타이핑을 이용하는 대표적인 언어로는 Python, JavaSript, Ruby등이 있습니다.

파이썬은 동적 타이핑 언어로, 변수의 자료형을 정해주지 않아도 프로그램을 실행시키면 자동으로 할당됩니다. 이는 코딩하는 사람의 입장에서는 매우 편리한데, 이에 대한 tradeoff로 프로그램이 실행이 되었을 때, 컴퓨터가 해야 하는 연산이 많아져 실행 속도는 느려집니다.


파이썬에서 모든 것은 객체이다.

이전 포스팅을 참조하시면 보다 자세한 내용을 보실 수 있습니다.

a = 10

파이썬에서 위와 같이 a라는 변수에 10이라는 정수를 할당했다고 합시다. C나 JAVA와 같은 정적 타이핑 언어에서 동일하게 변수를 선언하게 되면 메모리상 특정 공간에 지정해준 자료형에 필요한 공간을 확보하고, "이 공간은 a 변수를 위한 공간이야" 라고 선언해버리는 것이죠. 하지만 앞서 살펴보았듯, 파이썬에서는 이러한 과정이 이루어지지 않습니다.

파이썬에서 모든 것은 객체로 이루어져 있습니다. 변수도 마찬가지입니다. 따라서, 우리가 코딩한 a라는 변수는 단순히 "4byte의 메모리상 공간"을 참조하는 것이 아닌, 다양한 정보를 담은 객체를 참조하는 것입니다.

파이썬의 변수가 참조하는 객체는 다음과 같은 네 가지 정보를 담고 있습니다..

  • ob_refcnt: [참조 횟수] 객체의 라이프 사이클을 관리하기 위해 자신을 가리키고 있는 변수들의 참조 계수를 저장해놓은 값. 즉, 얼마나 많은 변수들이 자신을 참조하고 있는지를 알려주는 값.
  • ob_type: [자료형] 객체 자신이 가지고 있는 타입에 대한 정보를 가지고 있는 또 다른 객체를 가리키는 변수. 10은 정수이므로, 이 곳에는 정수 타입에 대한 정보를 가지고 있는 객체를 가리키고 있는 내부 변수가 있음.
  • ob_size: [크기] ob_digit크기
  • ob_digit: [내용] 숫자 10 자체

따라서,C에서 다음 코드 실행 시 간단하게 진행되는 과정이 python에서는 복잡해져 시간이 오래 걸리게 되는 것입니다.


C


# include stdio.h

int main()
{
	int number;
    number = 10;
    printf("%d", number);
}
  1. 컴파일시 확보한 공간 number에 10 할당
  2. 할당된 공간을 방문해 값 출력

Python

if __name__ == "__main__":
	number = 10
    print(number)
  1. number 객체 생성
  2. number 객체의 자료형 정보 int로 할당 (number -> ob_type)
  3. number 객체의 내용 10으로 할당 (number -> ob_digit)
  4. number 객체의 내용 확인해(number -> ob_digit) 출력

결국, 파이썬은 실행 시 동적으로 변수를 할당하는 과정에서 많은 연산이 필수적으로 수반되므로 실행 속도가 느려집니다.


02. (조금 특이한)인터프리터 언어

컴파일 언어와 인터프리터 언어의 소개, 파이썬의 컴파일/인터프리팅 과정은 내용이 너무 방대하므로, 긴 설명은 본 포스팅에 담지 못하는 점 양해 부탁드립니다.

컴파일 언어의 대표격인 C에서 소스코드를 컴파일하게 되면 최종적으로 CPU의 instruction인 머신 코드로 바뀌어 실행 파일로 바로 작성해 CPU에 전달됩니다. 따라서, 소스코드를 실행시키면 만들어 놓은 실행 파일만 실행하면 되는 것이죠.

파이썬은 이와 달리 컴파일과 인터프리팅을 동시에 사용하여 소스코드를 CPU에 전달합니다.

파이썬 프로그램은 소스코드가 실행되면 먼저 한 줄씩 컴파일하여 최종적으로 바이트 코드(byte code) 로 변환해 오류가 없는 경우 인터프리터(PVM)로 전달합니다. 이후, PVM은 이 바이트코드를 머신 코드로 바꾸어 CPU에 전달됩니다.

결국, 파이썬은 실행시 컴파일과 인터프리팅을 동시에 하는 과정에서 많은 연산을 해야 하므로 속도가 느린 것입니다.


03. GIL로 인한 멀티쓰레드 제한

In CPython, the global interpreter lock, of GIL is a mutex that protects access to Python objects, preventing multiple threads from executing Python bytecodes at once. This lock is necessary mainly because CPython's memory management is not thread-safe.

GIL이란, Global Interpreter Lock의 약자로, 파이썬의 객체들에 대한 접근을 보호하는 일종의 mutex로써, 여러 개의 스레드가 파이썬 코드를 동시에 실행하지 못하도록 하는 기능입니다. 즉 GIL이란 파이썬 인터프리터가 한 쓰레드만 하나의 바이트코드를 실행시킬 수 있도록 해주는 Lock이라고 할 수 있습니다. 하나의 스레드에 모든 자원을 허락하고 그 후에는 Lock을 걸어 다른 스레드는 실행할 수 없게 막아버리는 것이죠.

CPU 연산의 비중이 작은 작업을 할 때에는 멀티쓰레딩이 좋은 효율을 보여줘 간단한 작업에 있어서는 멀티쓰레딩을 막는 GIL의 존재가 파이썬의 속도를 저하시킨다고 할 수 있습니다.


04. 개선 방법

위의 이유로 인한 파이썬의 속도 문제는 예전부터 고질적으로 제기되어왔고, 이를 해결하기 위해 파이썬에서 직접 제시한 몇 가지 방법들을 간단히 소개해드리겠습니다.

Pypy

pypy 공식 사이트
pypy는 기본 파이썬 인터프리터를 이용하지 않고, pypy3 JIT compiler를 이용해 코드를 해석시켜 속도를 향상시킵니다.

Numba

numba 공식 사이트
numba는 기본 파이썬 인터프리터를 이용하지 않고 LLVM compiler를 이용해 코드를 해석시켜 수치연산을 가속해주는 라이브러리입니다.

Cython

Cython 공식 사이트
Cython은 pypy, numba와 달리 아예 파이썬 코드를 컴파일해 import할 수 있는 C 확장 라이브러리를 만들어 속도를 향상시켜주는 라이브러리입니다.


05. 정리

Python은 왜 이렇게 느릴까? 🤔

💡 파이썬은 동적 타이핑 언어이고, 자료형이 전부 객체라 실행시 연산량이 많다.
💡 파이썬은 실행시 실행과 컴파일, 인터프리팅이 동시에 이루어지므로 연산량이 많다.
💡 파이썬의 GIL 기능이 멀티쓰레딩을 막아 간단한 연산을 효율적으로 하지 못한다.


Reference

https://towardsdatascience.com/why-is-python-so-slow-and-how-to-speed-it-up-485b5a84154e
https://stackoverflow.com/questions/3033329/why-are-python-programs-often-slower-than-the-equivalent-program-written-in-c-or
https://www.python.org/success-stories/
https://medium.com/analytics-vidhya/is-python-really-very-slow-2-major-problems-to-know-which-makes-python-very-slow-9e92653265ea
https://hkim-data.tistory.com/267
http://jakevdp.github.io/blog/2014/05/09/why-python-is-slow/

profile
Work Hard, Play Hard 🔥🔥🔥

0개의 댓글