Skip to content

벤치마크

SafeC가 몇 가지 대표적인 마이크로벤치마크에서 실행 시간과 최대 메모리 사용량 면에서 C, C++, Rust, Zig, Go, Python과 어떻게 비교되는지 살펴봅니다 — The Computer Language Benchmarks Gameprogramming-language-benchmarks.vercel.app의 정신을 따르되, CI 규모 머신 한 대에 맞도록 축소했습니다. 가장 빠른 셀과 가장 가벼운 셀은 벤치마크별로 굵게 표시했습니다.

아래 수치를 보기 전에 읽어 주세요

단일 머신, 단일 세션, 3회 중 최선(best-of-3) 측정입니다 — Benchmarks Game의 더 엄격한 다중 실행 방법론이 아닙니다. 모든 숫자를 "이 언어에 대한 보편적 진실"이 아니라 "이 머신, 이 날짜에 대략 이 정도"로 받아들이세요. 세 가지 벤치마크는 이 워크로드들을 특징지을 뿐, 언어 전반의 성능을 특징짓지 않습니다.

방법론

  • 주 머신: Apple M1 Pro, 코어 10개, RAM 32GB, macOS 26.5.1 — safec 1.0.0 · Apple Clang 21.0.0(C/C++) · Go 1.26.5 · Zig 0.16.0 · Rust 1.97.1 · Python 3.14.6(SIMD 항목은 + NumPy 2.5.1).
  • 보조 머신 (fib/n-body/binary-trees/멀티스레딩/SIMD/웹 서비스, 릴리스 빌드만): 동일한 AMD Ryzen 7 7800X3D 머신(GPU 없음)을, 한 번은 WSL2 Ubuntu 22.04(clang 19.1.7, Go/Zig/Rust는 동일 버전)에서, 한 번은 Windows 11(clang/LLVM ~19, MSVC 타깃)에서 네이티브로. Mac과는 CPU 아키텍처 자체가 다르므로, 머신 간 차이는 통제된 실험이 아니라 참고 방향으로만 받아들이세요.
  • 빌드 플래그 — 디버그: -O0(C/C++), 최적화하지 않은 IR(SafeC), go build -gcflags="all=-N -l", Zig의 Debug 모드, 순수 rustc. 릴리스: -O2(C/C++, 그리고 SafeC의 clang -O2 백엔드 — safeguard build --release가 실제로 하는 것과 일치) · 순수 go build · zig build-exe -O ReleaseFast · rustc -O. Python은 디버그/릴리스 구분이 없습니다.
  • 타이밍: 3회 실행 중 최선(가장 낮은) 실행 시간 — macOS/WSL2는 /usr/bin/time -l, Windows는 System.Diagnostics.Stopwatch(동등한 /usr/bin/time이 없음). 메모리: 같은 실행들의 최대 RSS, macOS/WSL2만 — Windows의 Process.PeakWorkingSet64는 짧게 실행되는 이 프로세스들에서 새로고침 후에도 계속 0을 반환했습니다(원인이 해결되지 않은 API상의 특이 동작). 그래서 신뢰할 수 없는 값을 보여주는 대신 Windows 메모리는 전체적으로 생략했습니다.
  • 정확성: 결과에 포함되기 전에 모든 바이너리의 출력을 해당 워크로드의 알려진 정답과 비교해 검증했습니다.
  • 사용된 모든 소스 파일은 결과 옆에 인라인으로 링크되어 있습니다.

fib(37) — 재귀 피보나치

단순 재귀 피보나치 — 순수한 함수 호출 및 정수 산술 오버헤드만 있으며, 할당도 I/O도 없습니다.

언어디버그릴리스릴리스 최대 메모리릴리스 컴파일 시간
SafeC0.14s0.08s1.3 MB (가장 가벼움)0.13s
C0.13s0.07s (가장 빠름)1.3 MB (가장 가벼움)0.09s
C++0.13s0.07s (가장 빠름)1.3 MB (가장 가벼움)0.09s
Rust0.18s0.09s1.5 MB0.18s
Zig0.16s0.08s1.4 MB5.60s
Go0.11s (가장 빠름)0.08s4.0 MB0.08s
Python2.70s2.70s14.5 MBN/A (인터프리트)

릴리스, 플랫폼 간 비교:

언어macOS (M1 Pro)WSL2 (7800X3D)Windows (7800X3D)
SafeC0.080s0.040s (가장 빠름)0.046s
C0.070s0.040s (가장 빠름)0.046s
C++0.070s0.040s (가장 빠름)0.047s
Rust0.090s0.040s (가장 빠름)0.049s
Zig0.080s0.040s (가장 빠름)0.053s
Go0.080s0.080s0.092s
Python2.700s3.330s2.485s

출처: fib.sc · fib.c · fib.cpp · fib.rs · fib.zig · fib.go · fib.py

n-body — 5체 궤도 시뮬레이션

Benchmarks Game의 고전적인 n-body 테스트(태양/목성/토성/천왕성/해왕성), 2,000,000 스텝. 부동소수점 연산이 많고, 할당은 없으며, 작업 집합이 작습니다.

언어디버그릴리스릴리스 최대 메모리릴리스 컴파일 시간
SafeC0.63s0.11s1.3 MB (가장 가벼움)0.13s
C0.42s0.10s (가장 빠름)1.3 MB0.10s
C++0.41s0.11s1.3 MB (가장 가벼움)0.12s
Rust0.70s0.11s1.5 MB0.16s
Zig0.56s0.11s1.5 MB5.69s
Go0.28s (가장 빠름)0.10s (가장 빠름)4.1 MB0.09s
Python9.89s9.89s15.2 MBN/A (인터프리트)

릴리스, 플랫폼 간 비교:

언어macOS (M1 Pro)WSL2 (7800X3D)Windows (7800X3D)
SafeC0.110s0.090s (가장 빠름)0.102s
C0.100s0.090s (가장 빠름)0.103s
C++0.110s0.090s (가장 빠름)0.104s
Rust0.110s0.100s0.113s
Zig0.110s0.110s0.116s
Go0.100s0.100s0.113s
Python9.890s12.150s11.922s

출처: nbody.sc · nbody.c · nbody.cpp · nbody.rs · nbody.zig · nbody.go · nbody.py

binary-trees — 할당/해제 스트레스 테스트

작은 이진 트리 수백만 개를 만들고 버립니다(최대 깊이 18) — 산술이 아니라 메모리 관리 방식을 시험하는 벤치마크입니다. SafeC는 std::alloc/힙 대신 region/arena<R>(포인터 증가 방식 할당, arena_reset<R>()이 리전 전체를 O(1)에 폐기)를 사용합니다 — 이 워크로드는 전부 수명이 짧고 같은 스코프 안에서 끝나는 할당이라, 리전이 정확히 위해 만들어진 상황입니다.

언어디버그릴리스릴리스 최대 메모리릴리스 컴파일 시간
SafeC0.29s (가장 빠름)0.21s (가장 빠름)25.4 MB0.14s
C1.67s1.56s17.4 MB (가장 가벼움)0.10s
C++1.87s1.86s17.4 MB (가장 가벼움)0.10s
Rust3.16s1.74s17.6 MB0.16s
Zig4.85s1.57s17.6 MB5.28s
Go2.52s1.26s40.8 MB0.09s
Python20.93s20.93s87.0 MBN/A (인터프리트)

참고로 SafeC의 순수 힙 버전(아레나 대신 std::alloc): 릴리스 1.15초, 최대 25.4MB — 아레나가 여기서 약 5.5배 빠르고 메모리도 약 23% 적게 씁니다.

릴리스, 플랫폼 간 비교 (공정한 비교를 위해 SafeC는 세 플랫폼 모두 아레나 사용):

언어macOS (M1 Pro)WSL2 (7800X3D)Windows (7800X3D)
SafeC (아레나)0.210s0.130s (가장 빠름)0.147s
C1.560s0.820s1.965s
C++1.860s0.930s2.025s
Rust1.740s0.900s2.184s
Zig1.570s0.880s2.020s
Go1.260s1.150s1.125s
Python20.930s20.130s10.019s

SafeC 순수 힙 버전, 동일 세 플랫폼: macOS 1.150s, WSL2 0.620s (가장 빠름), Windows 1.119s.

출처: binarytrees.sc · binarytrees_arena.sc · binarytrees.c · binarytrees.cpp · binarytrees.rs · binarytrees.zig · binarytrees.go · binarytrees.py

컬렉션 — std::collections 처리량 (1,000,000개 원소)

연산처리량
bst_insert초당 1,577,110회
list_push_back초당 65,427,899회
map_insert초당 4,365,783회
map_get초당 8,468,404회

bench_collections.sc

멀티스레드 — binary-trees, 8스레드

동일한 binary-trees 워크로드를 8개 워커 스레드로 병렬화 — 각 스레드가 특정 깊이의 트리 개수 중 독립된 구간을 만들고 체크섬을 계산한 뒤, 다음 깊이로 넘어가기 전에 조인합니다. 릴리스만. SafeC: 스레드마다 region/arena<R> 하나씩(아레나 상태는 공유/잠금이 없으므로 스레드마다 자기 것이 필요) — macOS에서 순수 힙 버전과 비교하면 0.68s → 0.09s(약 7.5배).

언어macOS 8스레드 시간최대 메모리싱글 스레드 대비
SafeC0.09s (가장 빠름)89.7 MB2.33배
C0.61s72.3 MB2.56배
C++0.63s72.5 MB2.95배
Rust0.68s72.2 MB (가장 가벼움)2.56배
Zig0.54s74.1 MB2.91배
Go0.44s144.2 MB2.86배
Python24.43s372.0 MB0.86배 (싱글 스레드보다 느림)

8스레드 시간, 플랫폼 간 비교 (SafeC는 세 플랫폼 모두 아레나 사용):

언어macOS (M1 Pro)WSL2 (7800X3D)Windows (7800X3D)
SafeC (아레나)0.090s0.075s0.045s (가장 빠름)
C0.610s0.274s0.549s
C++0.630s0.290s0.566s
Rust0.680s0.319s0.591s
Zig0.540s0.280s0.563s
Go0.440s0.358s0.320s
Python24.430s26.626s10.055s

SafeC 순수 힙 버전: macOS 0.680s, WSL2 0.308s (가장 빠름), Windows 0.367s.

출처: binarytrees_mt.sc · binarytrees_mt_arena.sc · binarytrees_mt.c · binarytrees_mt.cpp · binarytrees_mt.rs · binarytrees_mt.zig · binarytrees_mt.go · binarytrees_mt.py

SIMD — 2천만 개 f64 값의 제곱합

순수 스칼라 루프와 각 언어의 명시적 벡터 타입을 -O2/릴리스에서 비교 — 백엔드의 자동 벡터화가 스칼라 루프에 이미 해주는 것 "위에" 명시적 SIMD가 추가로 벌어주는 것이 무엇인지를 가려냅니다. "벡터화 대 일부러 망가뜨린 코드"의 비교가 아닙니다. SafeC: 네이티브 vec<double,4> 타입(LLVM의 타깃에 무관한 FixedVectorType으로 낮춰지므로 아키텍처별 소스가 따로 필요 없음). C/C++: GCC/Clang 벡터 확장. Zig: @Vector. Rust: macOS에서는 stable 채널의 AArch64 NEON 인트린식(std::simd는 nightly 필요) — 아래 WSL2/Windows 열은 이 비교를 위해 새로 작성한 별도의 x86_64/SSE2 소스(simd_vec_x86_64.rs)를 씁니다. Go는 이식 가능한 SIMD 타입이 없어 스칼라만 있습니다.

언어macOS 스칼라macOS 명시적 SIMD배속
SafeC0.04s0.03s (가장 빠름)1.33배
C0.04s0.03s (가장 빠름)1.33배
C++0.04s0.03s (가장 빠름)1.33배
Rust0.04s0.03s (가장 빠름)1.33배
Zig0.04s0.03s (가장 빠름)1.33배
Go0.06sN/A
Python3.50sN/A (NumPy 사용 시 0.14s, 25.00배)

플랫폼 간 비교 — 이 벤치마크는 모든 언어에서 Apple Silicon이 큰 차이로 압승합니다. 이 특정 메모리 바운드 마이크로벤치마크가 이 하드웨어에서 낸 실제 결과로 받아들이되, "M1이 Ryzen을 이긴다"는 일반적 주장으로 확대 해석하지는 마세요. 절대 시간(수십 밀리초)이 작다 보니 다른 벤치마크보다 프로세스 시작 노이즈가 상대적으로 더 크게 반영됩니다.

언어스칼라: macOS / WSL2 / Windows명시적 SIMD: macOS / WSL2 / Windows
SafeC0.040s (가장 빠름) / 0.099s / 0.067s0.030s (가장 빠름) / 0.092s / 0.060s
C0.040s (가장 빠름) / 0.099s / 0.077s0.030s (가장 빠름) / 0.092s / 0.060s
C++0.040s (가장 빠름) / 0.101s / 0.073s0.030s (가장 빠름) / 0.092s / 0.073s
Rust0.040s (가장 빠름) / 0.099s / 0.044s0.030s (가장 빠름) / 0.093s / 0.039s
Zig0.040s (가장 빠름) / 0.096s / 0.065s0.030s (가장 빠름) / 0.089s / 0.069s
Go0.060s / 0.105s / 0.074sN/A
Python3.500s / 3.144s / 3.406s0.140s / 0.213s / 0.313s (NumPy)

출처: simd_scalar.sc · simd_vec.sc · simd_scalar.c · simd_vec.c · simd_scalar.cpp · simd_vec.cpp · simd_scalar.rs · simd_vec.rs(macOS/NEON) · simd_vec_x86_64.rs(WSL2/Windows/SSE2) · simd_scalar.zig · simd_vec.zig · simd_scalar.go · simd_numpy.py · simd_scalar.py

웹 서비스 — JSON "hello world" 엔드포인트

{"message":"Hello, World!"}를 반환하는 GET / — TechEmpower의 "JSON serialization" 테스트와 같은 형태입니다. macOS/WSL2에서는 Apache Bench(ab -n 20000 -c 50, keep-alive 없음)로 각 언어 고유의 HTTP 서빙 방식을 측정합니다: SafeC의 std::http_serve_reactor; C/C++/Zig의 최소한의 raw-socket accept 루프(macOS 전용 — Winsock으로는 아직 이식되지 않음); Go의 net/http; Python의 FastAPI+uvicorn; Rust의 axum(Dioxus가 아님 — Dioxus의 풀스택 서버 레이어는 내부적으로 axum이므로, axum을 직접 측정하는 것이 Dioxus의 요청이 실제로 거쳐 가는 경로를 재는 것과 같습니다). Windows에는 ab가 없어서, SafeC/Go/Rust/Python은 그 대신 작은 커스텀 Go 로드 제너레이터를 -n 5000 -c 20 규모로 사용합니다(더 작은 규모인 이유: 같은 포트로 빠르게 재시작하면 Windows의 긴 TIME_WAIT에 걸립니다). Windows 열은 ab 수치와 직접 비교하지 말고, 그 열 안에서의 상대적 크기로만 보세요.

언어macOS req/s (p50/p99)WSL2 req/s (p50/p99)Windows req/s (p50/p99)macOS 최대 메모리
SafeC36055 (가장 빠름) (1/5ms)17650 (3/4ms)2313 (7.5/19.6ms)7.2 MB
C25303 (2/2ms)N/AN/A1.4 MB (가장 가벼움)
C++24148 (2/4ms)N/AN/A1.6 MB
Rust27270 (2/2ms)18077 (3/3ms)465 (33.0/474.3ms)3.4 MB
Zig25742 (2/2ms)N/AN/A1.5 MB
Go25274 (2/3ms)18758 (가장 빠름) (3/4ms)2467 (가장 빠름) (7.8/16.9ms)19.7 MB
Python4790 (10/33ms)2357 (21/27ms)528 (37.3/52.5ms)54.9 MB

모든 언어가 모든 플랫폼에서 개별 측정 시 요청 실패 없이 전부 완료했습니다.

출처: server.sc · server_reactor.sc · server.c · server.cpp · main.rs · server.zig · server.go · server.py · io_nb_bsd.sc · io_nb.h · task.h

머신러닝 — 소형 MLP, 학습과 추론

2계층 MLP(relu(X @ W1) @ W2, 128→256→64, 배치 64, MSE 손실, 직접 작성한 SGD, 바이어스 없음) — 학습 100스텝 후 추론 전용 1000회 패스, 고정 시드. std::ml은 CPU(Accelerate BLAS)와 MPS를 다룹니다(이 크기는 GPU가 CPU BLAS를 이기기엔 너무 작습니다, SafeC도 마찬가지 — MPS 행은 경쟁을 위해서가 아니라 완전성을 위해 포함했습니다).

프레임워크디바이스학습 (100스텝)처리량추론 (1000회)손실 (정합성 확인)
SafeC (Accelerate BLAS)CPU16.8ms (가장 빠름)초당 380499개26.1ms100.333420
SafeCMPS39.4ms초당 162581개61.1ms100.333290
PyTorchCPU23.1ms초당 277232개13.5ms (가장 빠름)100.018272
PyTorchMPS131.6ms초당 48649개358.7ms103.423889
TensorFlowCPU83.0ms초당 77103개245.0ms106.182991
TensorFlowGPU245.3ms초당 26086개924.6ms106.182999
MLXGPU72.2ms초당 88685개309.1ms96.632431

CPU 학습/추론, 플랫폼 간 비교 (SafeC는 세 플랫폼 모두 BLAS 사용 — macOS는 Accelerate, 나머지는 OpenBLAS; 손실 값이 플랫폼마다 다른 이유는 Windows에 drand48이 없어서 데이터/초기화 난수 스트림 자체가 다르기 때문입니다 — 여전히 시드는 고정되어 있습니다. train_blas.sc 참고):

프레임워크macOS 학습/추론WSL2 학습/추론Windows 학습/추론
SafeC (BLAS)16.8ms(가장 빠름) / 26.1ms38.8ms / 74.0ms37.7ms / 101.9ms
PyTorch23.1ms / 13.5ms (가장 빠름)25.4ms / 33.5ms21.7ms / 34.8ms

tensor_blas.h · tensor_blas.sc · train_blas.sc · tensor.h · tensor.sc · tensor_gpu.h · tensor_gpu.sc · gpu_mps.h · gpu_mps.sc · gpu_mps_kernels.metal · gen_mps_metallib.sh · time.sc · time.h · math.sc · math.h · train.sc · train_gpu_small.sc · PyTorch train.py · TensorFlow train.py · MLX train.py

머신러닝, 더 큰 모델 — 512→1024→256

같은 형태를 약 50배로 키운 것(배치 128). 학습 50스텝, 추론 200회 패스.

프레임워크디바이스학습 (50스텝)처리량추론 (200회)손실 (정합성 확인)
SafeC (Accelerate BLAS)CPU89.2ms초당 71713개61.4ms95.428856
SafeCMPS47.9ms (가장 빠름)초당 133433개47.3ms (가장 빠름)95.428223
PyTorchCPU67.8ms초당 94440개48.2ms106.548233
PyTorchMPS110.5ms초당 57926개86.4ms108.710899
TensorFlowCPU148.8ms초당 43003개197.6ms316.556885
TensorFlowGPU154.1ms초당 41539개218.3ms317.200012
MLXGPU54.3ms초당 117953개74.4ms88.762100

gpu_mps.h · gpu_mps.sc · gpu_mps_kernels.metal · tensor_gpu.h · tensor_gpu.sc · train_cpu.sc · train_cpu_blas.sc · train_gpu.sc · PyTorch train.py · TensorFlow train.py · MLX train.py

머신러닝, GPU 백엔드 — CUDA, ROCm, Vulkan/SPIR-V, WebGPU

이 머신에는 NVIDIA/AMD GPU도, CUDA/ROCm 툴체인도, Vulkan SDK도, WebGPU 라이브러리도 없습니다 — 이 절의 수치는 하나도 실측되지 않았습니다. 모든 함수는 실제 벤더 C ABI에 맞춰 직접 작성되었고 safec 아래에서 타입 검사는 통과하지만, 이 환경에서는 링크도 실행도 할 수 없습니다 — "맞을 것"이지 "맞다고 확인됨"은 아닙니다. 백엔드마다 막히는 지점이 다릅니다:

백엔드원소별 연산Matmul (단순 커널)Matmul (벤더 BLAS)막히는 이유
CUDA실제 동작(PTX, 텍스트 IR)실제 동작(PTX)실제 동작(cuBLAS)NVIDIA GPU가 없을 뿐
ROCm항상 0 반환항상 0 반환실제 동작(rocBLAS)HSACO는 바이너리 IR이라 커널 이미지를 컴파일할 ROCm 툴체인이 없음
Vulkan/SPIR-V항상 0 반환항상 0 반환해당 없음SPIR-V는 바이너리 IR이라 컴파일할 glslc/glslangValidator가 없음
WebGPU실제 동작(WGSL, 텍스트 IR)실제 동작(WGSL)해당 없음wgpu-native/Dawn 라이브러리가 없을 뿐

gpu_cuda.h · gpu_cuda.sc · gpu_rocm.h · gpu_rocm.sc · gpu_spirv.h · gpu_spirv.sc · gpu_webgpu.h · gpu_webgpu.sc · tensor_cuda.h · tensor_cuda.sc · tensor_rocm.h · tensor_rocm.sc

머신러닝, fp16 / bf16 지원

SafeC의 타입 시스템에는 네이티브 16비트 부동소수점이 없습니다(Type.hTypeKindFloat32/Float64만 있음 — 새 타입을 추가하려면 렉서, 파서, sema의 산술 승격 규칙, LLVM 코드 생성까지 건드려야 하는 진짜 컴파일러 기능이지, 표준 라이브러리만의 변경이 아닙니다). 대신 컴파일러 네이티브 half 타입이 없을 때 쓰는 표준적인 방법을 사용합니다: fp16/bf16 값을 unsigned short에 원시 비트로 저장하고, float과의 명시적이고 정확히 반올림되는(round-to-nearest-even) 변환을 제공합니다 — 그리고 MPS 백엔드에서는 저장 공간만 절반으로 줄인 게 아니라 진짜 네이티브 half/bfloat GPU 연산을 씁니다.

검사결과
알려진 비트 패턴(fp16: 1.0, -1.0, 0.0, -0.0, 2.0, 0.5, 최대 정규값 65504, 오버플로→inf, 최소 비정규값, inf; bf16: 1.0, -1.0, 0.0, 2.0, π)모두 정확히 일치
fp16 비정규값 항등성 스윕(가수부 1–1023 전체, fp16→f32→fp16)1023/1023 정확히 일치
fp16 정규 범위 항등성 스윕(모든 지수부 × 샘플링한 가수부, 330개 패턴)330/330 정확히 일치
fp16 표현 가능한 값의 라운드트립정확히 일치
bf16 라운드트립(가수부 7비트라 구조적으로 손실 있음)예상대로 상대 오차 약 0.4% 이내

float16.h · float16.sc · gpu_mps.h · gpu_mps.sc · gpu_mps_kernels.metal · gpu_cuda.h · gpu_cuda.sc · gpu_webgpu.h · gpu_webgpu.sc · train_gpu_f16.sc · train_gpu_bf16.sc

머신러닝, 디바이스 선택

모든 백엔드가 자신의 연산 이름을 명시적으로 씁니다(tensor_matmul_blas_gpu_cuda) — 정확하지만, 디바이스를 한 번만 고르고 싶은 호출자 입장에서는 번거롭습니다. 대신 tensor_matmul_on(a, b, device) / tensor_relu_on(a, device)Device 열거형으로 분기합니다 — 이 샌드박스에서 실행 가능한 모든 백엔드(CPU, CPU+BLAS, MPS)에서 비트 단위로 동일함을 검증했습니다:

디바이스Y[0][0]Y[0][1]
CPU0.9000001.300000
CPU + BLAS0.9000001.300000
MPS0.9000001.300000

이 작업 중 실제 이름 충돌을 하나 발견했습니다: activations.sc(순전파 전용 연산)와 tensor_nn.sc(모든 GPU 백엔드에 필요한 오토그라드 연산) 둘 다 tensor_sigmoid/tensor_relu 등을 정의하고 있어서, 어떤 프로그램도 둘 다 링크할 수 없었고, activations.sc 기반 레이어는 GPU 백엔드를 쓸 수 없었습니다. activations.h의 순전파 전용 함수 이름에 _fwd 접미사를 붙이고 attention.sc/transformer.sc/rnn.sc의 호출부를 갱신해서 고쳤습니다.

jit_block_forward_on(block, x, device)는 실제 레이어(JiTBlock의 Q/K/V/출력 프로젝션 + FFN)를 통해 디바이스 선택을 전달합니다 — CPU/CPU+BLAS/MPS 전체에서 기준값과 비트 단위로 동일함을 검증했습니다:

경로y[0]y[1]y[2]
jit_block_forward (기준)-0.479727-0.589425-0.537808
..._on(DEVICE_CPU)-0.479727-0.589425-0.537808
..._on(DEVICE_CPU_BLAS)-0.479727-0.589425-0.537808
..._on(DEVICE_MPS)-0.479727-0.589425-0.537808

아직 디바이스 라우팅이 안 된 것: attention 내부의 QK^T/softmax/·V 행렬곱, DiTBlock, cnn.sc.

tensor_dispatch.h · tensor_dispatch.sc · transformer_dispatch.h · transformer_dispatch.sc · activations.h · activations.sc · attention.h · attention.sc · transformer.h · transformer.sc

메모리 할당 — std::alloc/dealloc이 순수 malloc/free보다 느릴까요?

std::alloc은 크기 클래스별로 캐시하는 할당자입니다(PyTorch의 CPU/CUDA 캐싱 할당자, MLX의 Metal 버퍼 캐시와 같은 발상): 해제된 블록은 2의 거듭제곱 크기 클래스별로 나뉜 스레드-로컬 프리 리스트로 들어가고, 같은 클래스의 다음 alloc() 요청은 malloc()/free()를 전혀 거치지 않고 바로 거기서 처리됩니다. 이중 해제/UAF(use-after-free) 탐지에는 영향이 없습니다(캐시된 블록도 재사용되기 전까지는 "해제됨" 매직 워드를 그대로 갖고 있습니다).

std::alloc/dealloc은 작고 크기가 같은 수명이 짧은 할당이 많은 binarytrees 형태의 워크로드에서 순수 malloc/free보다 실제로 더 빠릅니다(1210ms 대 1518ms, 약 20% 빠름). alloc/free를 번갈아 호출하는 마이크로벤치마크에서는 약 3.3배 빠릅니다(호출당 11–12ns 대 37–38ns). region/arena<R>는 힙보다 더 빠릅니다(약 5.9배).

mem.h · mem.sc

MIT 라이선스로 배포됩니다.