# Python 성능: 네이티브 NumPy 루프, 콜백 및 변환 비용

Published: 2026-10-17

제어된 CPU 전용 수치 테스트는 CPython 루프, NumPy 산술, Python 콜백 래퍼 및 컴파일된 Numba 루프를 통해 동일한 값을 추적합니다. 준비된 입력 NumPy는 크게 앞섰지만, Python 리스트로의 변환 및 재변환은 그 이점을 거의 동점으로 줄였습니다. 이 에피소드는 실행 경계와 애플리케이션이 필요로 하는 결과를 측정하는 방법을 설명합니다.

Canonical: https://thedailydiff.dev/ko/video/2026-10-17-python-native-loops/

## 이 동영상에서 다루는 내용

- CPython은 소스를 바이트코드로 컴파일합니다. 반복되는 인터프리터/객체 작업과 유형이 지정된 네이티브 숫자 루프는 다른 실행 방식입니다.
- 숫자 NumPy 산술은 컴파일된 작업에 유형이 지정된 배치를 전달할 수 있습니다. 산술 루프는 여전히 존재하며, 뷰는 비연속적인 레이아웃을 가질 수 있습니다.
- 변환 및 필요한 출력 표현을 측정하십시오. 이 실행에서는 Python 리스트를 배열로 변환하고 다시 되돌리는 것이 준비된 배열의 큰 이점을 상쇄했습니다.
- 작은 워크로드는 일반 Python에 유리할 수 있습니다. 고정된 배열 호출 비용이 중요하기 때문입니다. 8개의 값에 대한 컴프리헨션이 이 측정된 비교에서 승리했습니다.
- np.vectorize는 연속적인 요소에 대해 Python을 호출하는 편의성 래퍼입니다. 배치 인터페이스만으로는 네이티브 숫자 실행을 확립하지 못합니다.
- 컴파일된 Python 구문 루프도 빠를 수 있습니다. 호출자에게 중요할 경우 콜드 컴파일/설정 비용을 포함하십시오.
- 네이티브 실행, CPU SIMD, 멀티스레딩 및 GPU 백엔드는 별개의 개념입니다. 사용 가능한 CPU 기능은 특정 실행에 사용된 명령어를 추적하지 않습니다.

## 챕터

- 0:00 같은 CPU가 왜 더 빨리 끝낼 수 있을까요?
- 0:52 Python 루프는 어떤 작업을 반복하나요?
- 1:38 NumPy는 루프를 어디로 옮기나요?
- 2:13 빠르다는 것이 GPU 또는 많은 CPU 코어를 의미하나요?
- 2:49 인상적인 속도 향상이 언제 사라지나요?
- 3:28 벤치마크에 어떤 경계가 포함되어야 할까요?
- 4:00 컴파일된 Python 루프도 빠를 수 있을까요?
- 4:56 np.vectorize는 실제로 무엇을 실행하나요?
- 5:41 코드에서 무엇을 변경해야 할까요?

## 번역된 스크립트

원래 영어 내레이션에서 번역되었습니다. 사용 가능한 오디오 및 캡션은 YouTube에서 제어합니다.

### 같은 CPU가 왜 더 빨리 끝낼 수 있을까요?

0:00 Python이 빨라지려면 새로운 언어가 필요하다고 생각합니다. 루프가 이동한 후 같은 CPU가 배치 작업을 더 빨리 실행했습니다. 작업은 어디로 갔을까요? 속도 향상은 언제 사라지나요? 어떤 지름길이 Python을 되돌려 놓을까요? 여기는 The Daily Diff, 내부의 이야기입니다. 그 지름길을 계속 주시하십시오. CPython은 표준 Python 구현입니다.

0:17 소스를 바이트코드로 컴파일한 다음, 해당 명령어를 인터프리터에서 실행합니다. NumPy는 선언된 데이터 유형을 가진 값을 저장하는 수치 라이브러리입니다. 선언된 데이터 유형을 가집니다. 내장된 산술 연산은 전체 배치를 컴파일된 작업에 전달할 수 있습니다. 백만 개의 부동 소수점 값을 가져와 각 값을 제곱하고, 그런 다음 원래 값을 더합니다. 모든 버전은 동일한 값을 받고 일치하는 답변을 생성합니다. 값은 반올림 차이가 경주에서 제외될 만큼 충분히 작습니다.

0:40 이 기계에서 컴프리헨션은 약 33밀리초가 걸렸습니다. NumPy는 1밀리초 미만이 걸렸습니다. 둘 다 CPU에서 실행되었습니다. 이는 이 워크로드에 유용한 증거이며, 프로그래밍 언어에 대한 끔찍한 부고입니다. 일반 루프부터 시작하겠습니다. 프로그래밍 언어에 대한 끔찍한 부고입니다. 일반 루프부터 시작하겠습니다.

### Python 루프는 어떤 작업을 반복하나요?

0:53 Python은 다음 값을 가져와 산술 연산을 수행하고 결과를 추가합니다. 그런 다음 다시 반복합니다. 곱셈은 저렴합니다. 다음 곱셈을 준비하고, 그 주변의 Python 객체를 처리하는 것은 매번 작업을 추가합니다. 이것은 테스트된 함수의 실제 역어셈블리입니다. 반복, 곱셈 및 덧셈은 바이트코드 명령어로 나타납니다. Python 버전과 인터프리터는 다른 선택을 합니다.

1:14 이 타이밍은 화면에 표시된 버전, 즉 Python 3.10에 속합니다. Python 리스트는 객체에 대한 참조를 저장하므로 다른 종류의 것들이 하나의 리스트를 공유할 수 있습니다. 우리의 산술 작업은 전체적으로 숫자를 사용합니다. 반복적인 서비스를 요청하면서 일반적인 배열에 대한 비용을 지불합니다. 백만 번 같은 커피를 주문하고 모든 컵에 대해 새로운 양식을 작성한다고 상상해 보십시오. 모든 컵에 대해 새로운 양식을 작성한다고 상상해 보십시오. 커피 머신은 괜찮습니다.

1:36 서류 작업은 자체 경력을 발전시켰습니다.

### NumPy는 루프를 어디로 옮기나요?

1:38 NumPy는 계약을 변경합니다. 이 배열은 하나의 선언된 유형을 가진 고정 크기의 부동 소수점 값을 저장합니다. 우리의 배열은 연속적이므로 그 값들은 메모리에 함께 위치합니다. 뷰는 다른 레이아웃을 가질 수 있습니다. 산술 연산은 어떤 종류의 데이터를 받는지 알고 있습니다. 호출은 배치를 네이티브 연산에 전달합니다. 루프는 컴파일된 코드 내에서 값을 계속 방문합니다. 해당 작업 전반에 걸쳐 유형에 대한 지식을 재사용할 수 있습니다.

2:00 Python은 작업을 전달하는 사람으로 남아 있습니다. Itamar Turner Trauring은 Python Speed에서 이 차이를 설명합니다. 배치 인터페이스, 네이티브 구현 및 프로세서 벡터 명령은 별개의 것입니다. 함수 이름은 증거를 거의 제공하지 않습니다.

### 빠르다는 것이 GPU 또는 많은 CPU 코어를 의미하나요?

2:13 이 결과는 CPU에서 NumPy를 사용했습니다. CuPy와 같은 GPU 백엔드는 배열과 연산을 위한 다른 저장소를 선택합니다. GPU를 구입해도 이 일반 Python 루프는 이미 실행되던 곳에서 계속 실행됩니다. CPU 벡터 명령어는 여러 값을 함께 처리할 수 있습니다. NumPy는 지원되는 커널을 선택하는 메커니즘을 가지고 있습니다. 우리의 런타임은 사용 가능한 기능을 보고하지만, 이 표현의 명령어를 추적하지는 않습니다. 측정된 전체 이득을 벡터 명령어 탓으로 돌리는 것은 증거를 과장하는 것입니다.

2:40 GPU 쇼핑 카트는 닫아도 됩니다. 구매를 취소하면서 성능 문제를 해결하는 것은 깊은 만족감을 줍니다. 불행히도 벤치마크는 계속됩니다.

### 인상적인 속도 향상이 언제 사라지나요?

2:49 여덟 개의 값으로는 컴프리헨션이 승리했습니다. 배열 메커니즘을 호출하는 비용이 작은 작업보다 더 들었습니다. 더 큰 준비된 배열이 앞섰습니다. 우리는 반복적인 시도와 웜업된 프로세스를 사용했습니다. 새로운 Python 빌드는 교차점을 변경할 수 있습니다. 그리고 애플리케이션이 필요로 하는 것을 변경합니다. Python 리스트로 시작하여 새로운 Python 리스트로 끝납니다. NumPy는 입력을 배열로 변환하고 답변을 다시 Python

3:11 객체로 바꿔야 합니다. 이러한 단계는 애플리케이션에 필요하다면 비용 청구서에 포함되어야 합니다. 백만 개의 값에서 이 전체 경로는 약 28밀리초가 걸렸습니다. 컴프리헨션도 약 28밀리초가 걸렸습니다. 우리의 극적인 리드는 거의 동점이 되었습니다. 빠른 부분은 진짜였습니다. 이점은 데이터를 수치 형태로 유지하는 데 달려 있었습니다.

### 벤치마크에 어떤 경계가 포함되어야 할까요?

3:28 데이터를 따르십시오. 다음 연산이 배열을 허용하면 배열을 유지하십시오. 리스트와 배열 사이를 반복적으로 변환하는 경우, 그 변환 비용을 측정하십시오. 비싼 핸드오프 전에 멈추는 것은 잘못된 질문에 대한 정확한 숫자를 제공합니다. 이 배열을 곱하면 임시 결과가 생성됩니다. 원래 값을 더하면 출력이 생성됩니다. 네이티브 실행은 여전히 메모리가 필요하고 데이터를 이동합니다.

3:47 일부 연산은 출력 버퍼를 허용하여 코드가 안전하게 재사용할 수 있을 때 할당을 줄입니다. 안전하게 재사용할 수 있을 때 할당을 줄입니다. 하나의 예쁜 줄이 여러 개의 큰 버퍼를 주문할 수 있습니다. Python Speed도 이 문제를 지적합니다. 할당자는 당신의 미학에 놀랍도록 무관심합니다.

### 컴파일된 Python 루프도 빠를 수 있을까요?

4:00 또한 루프 구문을 유지하고 실행을 변경할 수 있습니다. Numba는 지원되는 수치 Python 함수를 네이티브 코드로 컴파일할 수 있습니다. 다음은 컴파일러 데코레이터가 있는 테스트의 루프입니다. 여전히 'for'라고 적혀 있습니다. 비싼 인터프리터 경로는 변경되었습니다. 컴파일 후 이 루프는 준비된 배열로 0.5밀리초 미만이 걸렸습니다. 이 실행에서 컴파일 자체는 약 1초가 걸렸습니다. 해당 설정 비용은 반복된 타이밍에서 제외되었으므로,

4:22 아주 작은 일회성 작업은 다른 비교가 필요합니다. Numba의 자체 가이드는 명시적인 루프를 기꺼이 보여줍니다. 핫 경로를 찾기 전에 언어 마이그레이션을 시작하는 것은 프로파일러를 여는 것을 피하는 정교한 방법입니다. 먼저 무엇이 시간을 소비하는지 찾으십시오. 네트워크 대기, 객체 구문 분석 및 대량 산술은 다른 해결책을 요구합니다. 계산과 데이터가 일치할 때 네이티브 배열 연산이 도움이 됩니다.

4:43 호출자가 실제로 필요로 하는 결과를 측정하십시오. 이제 그 지름길에 대해 알아보겠습니다. Python 함수가 있고 배열을 받아들이게 하고 싶습니다. vectorize라는 래퍼는 누락된 성능 버튼처럼 들립니다. 그 문서는 중요한 문장을 담고 있습니다.

### np.vectorize는 실제로 무엇을 실행하나요?

4:56 NumPy는 vectorize 래퍼가 주로 편의를 위해 제공된다고 말합니다. 그 구현은 본질적으로 for 루프입니다. 래퍼는 배치를 받아들이지만, 연속적인 요소에 대해 Python 함수를 계속 호출합니다. 인터페이스는 짧아졌습니다. 실행 경계는 계속 바빴습니다. 동일한 값이 Python 콜백을 통해 다시 돌아오는 것을 보십시오. 준비된 배열로 이 버전은 약 87밀리초가 걸렸습니다. 우리 컴프리헨션보다 느렸습니다.

5:18 유용한 브로드캐스팅 래퍼는 당신이 제거하려고 했던 정확한 요소별 작업을 유지할 수 있습니다. 배치 인터페이스 뒤의 구현을 확인하고, 호환되는 데이터를 유용한 형태로 유지하며, 변환을 비교에 포함하십시오. 유용한 형태로 유지하며, 변환을 비교에 포함하십시오. 네이티브, 콜백 및 컴파일된 루프는 호출자의 입장에서 비슷하게 보일 수 있습니다. vectorize라는 이름은 스톱워치가 서명하지 않은 약속을 했습니다. 편의성이 필요할 때 그것을 유지하십시오. 성능을 위해서는 실행되는 루프를 확인하십시오.

### 코드에서 무엇을 변경해야 할까요?

5:41 내 평결은 측정된 수치 핫 경로를 배치하는 것은 SHIP IT입니다. 이미 잘 작동하는 일반 루프는 유지하십시오. 작업이 어디에서 일어나는지, 그리고 다음 호출자가 무엇을 필요로 하는지 확인하십시오. 구독하고, 알림 벨을 누르고, 다르게 처리했을지 댓글로 알려주십시오. 그리고 이것이 오늘의 diff입니다. 저는 Axrisi의 Niko입니다. 책임감 있게 병합하십시오.

## 출처

- [Universal functions (ufunc) basics — NumPy 2.2](https://numpy.org/doc/2.2/user/basics.ufuncs.html) — NumPy
- [The N-dimensional array — NumPy 2.2](https://numpy.org/doc/2.2/reference/arrays.ndarray.html) — NumPy
- [numpy.vectorize — convenience and performance notes](https://numpy.org/doc/2.2/reference/generated/numpy.vectorize.html) — NumPy
- [CPU/SIMD optimizations — NumPy 2.2](https://numpy.org/doc/2.2/reference/simd/index.html) — NumPy
- [dis — Disassembler for Python bytecode](https://docs.python.org/3.10/library/dis.html) — Python Software Foundation
- [CPython 3.10.12 list representation](https://github.com/python/cpython/blob/v3.10.12/Include/cpython/listobject.h) — CPython contributors
- [The limits of Python vectorization as a performance technique](https://pythonspeed.com/articles/vectorization-python-alternatives/) — Itamar Turner-Trauring / Python⇒Speed
- [Performance Tips](https://numba.readthedocs.io/en/stable/user/performance-tips.html) — Numba
- [Basics of CuPy](https://docs.cupy.dev/en/stable/user_guide/basic.html) — CuPy
