Compare commits

..
Author SHA1 Message Date
geohot 2c90f3ea76 split test_advancedindex 2025-10-14 19:02:21 +08:00
geohot d99457657b svd nonfull in parallel 2025-10-14 18:50:11 +08:00
geohot 8a34a4e2c7 fix up some slow tests that launch python 2025-10-14 18:42:42 +08:00
274 changed files with 6579 additions and 30318 deletions
-3
View File
@@ -1,3 +0,0 @@
[run]
source = tinygrad
branch = True
-14
View File
@@ -41,10 +41,6 @@ inputs:
description: "Install LLVM?"
required: false
default: 'false'
mesa:
description: "Install mesa"
required: false
default: 'false'
runs:
using: "composite"
steps:
@@ -293,13 +289,3 @@ runs:
if: inputs.llvm == 'true' && runner.os == 'macOS'
shell: bash
run: brew install llvm@20
# **** mesa ****
- name: Install mesa (linux)
if: inputs.mesa == 'true' && runner.os == 'Linux'
shell: bash
run: sudo curl -L https://github.com/sirhcm/tinymesa/releases/download/tinymesa-32dc66c/libtinymesa_cpu-mesa-25.2.4-linux-amd64.so -o /usr/lib/libtinymesa_cpu.so
- name: Install mesa (macOS)
if: inputs.mesa == 'true' && runner.os == 'macOS'
shell: bash
run: brew install sirhcm/tinymesa/tinymesa_cpu
+2 -8
View File
@@ -2,7 +2,7 @@ name: Autogen
env:
# increment this when downloads substantially change to avoid the internet
DOWNLOAD_CACHE_VERSION: '12'
PYTHON_CACHE_VERSION: '4'
PYTHON_CACHE_VERSION: '3'
APT_CACHE_VERSION: '1'
BUILD_CACHE_VERSION: '1'
CAPTURE_PROCESS_REPLAY: 1
@@ -36,9 +36,8 @@ jobs:
cuda: 'true'
webgpu: 'true'
llvm: 'true'
pydeps: 'pyyaml mako'
- name: Install autogen support packages
run: sudo apt-get install -y --no-install-recommends llvm-14-dev libclang-14-dev llvm-20-dev
run: sudo apt-get install -y --no-install-recommends llvm-14-dev libclang-14-dev
- name: Verify OpenCL autogen
run: |
cp tinygrad/runtime/autogen/opencl.py /tmp/opencl.py.bak
@@ -90,8 +89,3 @@ jobs:
cp tinygrad/runtime/autogen/llvm.py /tmp/llvm.py.bak
./autogen_stubs.sh llvm
diff /tmp/llvm.py.bak tinygrad/runtime/autogen/llvm.py
- name: Verify mesa autogen
run: |
cp tinygrad/runtime/autogen/mesa.py /tmp/mesa.py.bak
./autogen_stubs.sh mesa
diff /tmp/mesa.py.bak tinygrad/runtime/autogen/mesa.py
+36 -33
View File
@@ -51,18 +51,17 @@ jobs:
rm -f /tmp/staging.db /tmp/staging.db-shm /tmp/staging.db-wal
- name: reset process replay
run: python3.11 test/external/process_replay/reset.py
- name: Print macOS version
run: sw_vers
- name: Run Stable Diffusion
run: BENCHMARK_LOG=stable_diffusion JIT=1 ASSERT_MIN_STEP_TIME=720 python3.11 examples/stable_diffusion.py --fp16 --seed 0 --noshow --timing | tee sd.txt
run: BENCHMARK_LOG=stable_diffusion JIT=1 ASSERT_MIN_STEP_TIME=800 python3.11 examples/stable_diffusion.py --fp16 --seed 0 --noshow --timing | tee sd.txt
- name: Run Stable Diffusion without fp16
run: BENCHMARK_LOG=stable_diffusion_fp32 JIT=1 ASSERT_MIN_STEP_TIME=800 python3.11 examples/stable_diffusion.py --seed 0 --noshow --timing | tee sd_no_fp16.txt
run: BENCHMARK_LOG=stable_diffusion_fp32 JIT=1 ASSERT_MIN_STEP_TIME=900 python3.11 examples/stable_diffusion.py --seed 0 --noshow --timing | tee sd_no_fp16.txt
- name: Run Stable Diffusion v2
# TODO: very slow step time
run: BENCHMARK_LOG=stable_diffusion_v2 JIT=1 ASSERT_MIN_STEP_TIME=4500 python3.11 examples/sdv2.py --fp16 --seed 0 --noshow --timing | tee sdv2.txt
run: BENCHMARK_LOG=stable_diffusion_v2 JIT=1 ASSERT_MIN_STEP_TIME=10000 python3.11 examples/sdv2.py --fp16 --seed 0 --noshow --timing | tee sdv2.txt
# process replay can't capture this, the graph is too large
- name: Run SDXL
run: BENCHMARK_LOG=stable_diffusion_xl ASSERT_MIN_STEP_TIME=5000 CAPTURE_PROCESS_REPLAY=0 JIT=1 python3.11 examples/sdxl.py --seed 0 --noshow --timing | tee sdxl.txt
# TODO: too slow
# - name: Run SDXL
# run: BENCHMARK_LOG=stable_diffusion_xl ASSERT_MIN_STEP_TIME=5000 CAPTURE_PROCESS_REPLAY=0 JIT=1 python3.11 examples/sdxl.py --seed 0 --noshow --timing | tee sdxl.txt
- name: Run model inference benchmark
run: METAL=1 python3.11 test/external/external_model_benchmark.py
- name: Test speed vs torch
@@ -131,7 +130,7 @@ jobs:
- name: UsbGPU copy speeds
run: sudo -E PYTHONPATH=. AMD=1 AMD_IFACE=USB python3.11 test/external/external_test_usb_asm24.py TestDevCopySpeeds
#- name: UsbGPU openpilot test
# run: sudo -E PYTHONPATH=. AMD=1 AMD_IFACE=USB GRAPH_ONE_KERNEL=1 python3.11 examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/9118973ed03c1ae1d40cf69a29507ec2cc78efd7/selfdrive/modeld/models/supercombo.onnx
# run: sudo -E PYTHONPATH=. AMD=1 AMD_IFACE=USB NOLOCALS=0 IMAGE=0 GRAPH_ONE_KERNEL=1 python3.11 examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/9118973ed03c1ae1d40cf69a29507ec2cc78efd7/selfdrive/modeld/models/supercombo.onnx
- uses: actions/upload-artifact@v4
with:
name: Speed (Mac)
@@ -199,7 +198,7 @@ jobs:
- name: Test speed vs torch
run: NV=1 CAPTURE_PROCESS_REPLAY=0 HALF=1 BIG=2 TORCHCUDA=1 python3 test/speed/external_test_speed_v_torch.py | tee torch_speed.txt
- name: Test speed vs theoretical
run: NV=1 IGNORE_BEAM_CACHE=1 DISABLE_COMPILER_CACHE=1 BEAM_DEBUG=1 DEBUG=1 python -m pytest -rA test/external/speed_v_theoretical.py --durations=20
run: NV=1 IGNORE_BEAM_CACHE=1 BEAM_DEBUG=1 DEBUG=1 python -m pytest -rA test/external/speed_v_theoretical.py --durations=20
- name: Test benchmark allreduce
run: NV=1 python test/external/external_benchmark_multitensor_allreduce.py
- name: Test tensor cores
@@ -211,7 +210,6 @@ jobs:
CUDA=1 SHOULD_USE_TC=1 HALF=1 DEBUG=2 python3 extra/gemm/simple_matmul.py | tee matmul.txt
CUDA=1 SHOULD_USE_TC=1 BFLOAT16=1 DEBUG=2 python3 extra/gemm/simple_matmul.py | tee matmul_bfloat16.txt
CUDA=1 SHOULD_USE_TC=1 ALLOW_TF32=1 DEBUG=2 ATOL=2e-2 python3 extra/gemm/simple_matmul.py | tee matmul_tf32.txt
CUDA=1 SHOULD_USE_TC=1 FP8E4M3=1 DEBUG=2 python3 extra/gemm/simple_matmul.py | tee matmul_fp8.txt
- name: Run Tensor Core GEMM (PTX)
run: NV=1 NV_PTX=1 SHOULD_USE_TC=1 HALF=1 DEBUG=2 python3 extra/gemm/simple_matmul.py | tee matmul_ptx.txt
- name: Run Tensor Core GEMM (NV)
@@ -239,8 +237,6 @@ jobs:
run: BENCHMARK_LOG=llama3_beam NV=1 JITBEAM=2 IGNORE_BEAM_CACHE=1 python3 examples/llama3.py --size 8B --model weights/LLaMA-3/8B-SF-DPO/ --benchmark --temperature 0 | tee llama3_beam.txt
- name: Run LLaMA-3 8B on 4 GPUs with BEAM
run: BENCHMARK_LOG=llama3_beam_4gpu NV=1 JITBEAM=2 IGNORE_BEAM_CACHE=1 CAPTURE_PROCESS_REPLAY=0 python3 examples/llama3.py --size 8B --shard 4 --model weights/LLaMA-3/8B-SF-DPO/ --benchmark --temperature 0 | tee llama3_four_gpu.txt
- name: Run quantized LLaMA3
run: BENCHMARK_LOG=llama3_fp8 python3 examples/llama3.py --size 8B --model weights/LLaMA-3/8B-SF-DPO/ --temperature 0 --benchmark --quantize fp8 | tee llama3_fp8.txt
# - name: Run LLaMA-3 8B on 6 GPUs
# run: NV=1 CAPTURE_PROCESS_REPLAY=0 python3 examples/llama3.py --size 8B --shard 6 --model weights/LLaMA-3/8B-SF-DPO/ --benchmark --temperature 0 | tee llama3_six_gpu.txt
# - name: Run LLaMA-2 70B
@@ -274,7 +270,6 @@ jobs:
llama3_beam.txt
llama3_four_gpu.txt
llama3_six_gpu.txt
llama3_fp8.txt
llama_2_70B.txt
mixtral.txt
gpt2_unjitted.txt
@@ -323,9 +318,9 @@ jobs:
- name: Run 10 CIFAR training steps
run: BENCHMARK_LOG=cifar_10steps ASSERT_MIN_STEP_TIME=270 NV=1 STEPS=10 python3 examples/hlb_cifar10.py | tee train_cifar.txt
- name: Run 10 CIFAR training steps w HALF
run: BENCHMARK_LOG=cifar_10steps_half ASSERT_MIN_STEP_TIME=240 NV=1 STEPS=10 DEFAULT_FLOAT=HALF python3 examples/hlb_cifar10.py | tee train_cifar_half.txt
run: BENCHMARK_LOG=cifar_10steps_half ASSERT_MIN_STEP_TIME=310 NV=1 STEPS=10 DEFAULT_FLOAT=HALF python3 examples/hlb_cifar10.py | tee train_cifar_half.txt
- name: Run 10 CIFAR training steps w BF16
run: BENCHMARK_LOG=cifar_10steps_bf16 ASSERT_MIN_STEP_TIME=270 NV=1 STEPS=10 DEFAULT_FLOAT=BFLOAT16 python3 examples/hlb_cifar10.py | tee train_cifar_bf16.txt
run: BENCHMARK_LOG=cifar_10steps_bf16 ASSERT_MIN_STEP_TIME=310 NV=1 STEPS=10 DEFAULT_FLOAT=BFLOAT16 python3 examples/hlb_cifar10.py | tee train_cifar_bf16.txt
# TODO: too slow
# - name: Run 10 CIFAR training steps w winograd
# run: BENCHMARK_LOG=cifar_10steps_half_wino ASSERT_MIN_STEP_TIME=350 NV=1 CAPTURE_PROCESS_REPLAY=0 WINO=1 STEPS=10 DEFAULT_FLOAT=HALF python3 examples/hlb_cifar10.py | tee train_cifar_wino.txt
@@ -409,7 +404,7 @@ jobs:
# python3 -c "import torch; print(torch.__version__)"
# LD_PRELOAD="/opt/rocm/lib/libhsa-runtime64.so" HSA=1 BIG=2 TORCHCUDA=1 python3 test/speed/external_test_speed_v_torch.py | tee torch_speed.txt
- name: Test speed vs theoretical
run: AMD=1 IGNORE_BEAM_CACHE=1 DISABLE_COMPILER_CACHE=1 BEAM_DEBUG=1 DEBUG=1 python -m pytest -rA test/external/speed_v_theoretical.py --durations=20
run: AMD=1 IGNORE_BEAM_CACHE=1 BEAM_DEBUG=1 DEBUG=1 python -m pytest -rA test/external/speed_v_theoretical.py --durations=20
- name: Test tensor cores
run: |
AMD=1 AMD_LLVM=0 python3 test/opt/test_tensor_cores.py
@@ -527,7 +522,7 @@ jobs:
- name: Run 10 CIFAR training steps
run: BENCHMARK_LOG=cifar_10steps ASSERT_MIN_STEP_TIME=330 AMD=1 STEPS=10 python3 examples/hlb_cifar10.py | tee train_cifar.txt
- name: Run 10 CIFAR training steps w HALF
run: BENCHMARK_LOG=cifar_10steps_half ASSERT_MIN_STEP_TIME=390 AMD=1 STEPS=10 DEFAULT_FLOAT=HALF python3 examples/hlb_cifar10.py | tee train_cifar_half.txt
run: BENCHMARK_LOG=cifar_10steps_half ASSERT_MIN_STEP_TIME=330 AMD=1 STEPS=10 DEFAULT_FLOAT=HALF python3 examples/hlb_cifar10.py | tee train_cifar_half.txt
# - name: Run 10 CIFAR training steps w BF16
# run: BENCHMARK_LOG=cifar_10steps_bf16 ASSERT_MIN_STEP_TIME=288 AMD=1 STEPS=10 DEFAULT_FLOAT=BFLOAT16 python3 examples/hlb_cifar10.py | tee train_cifar_bf16.txt
# TODO: too slow
@@ -623,24 +618,22 @@ jobs:
rm -f /tmp/staging.db /tmp/staging.db-shm /tmp/staging.db-wal
- name: reset process replay
run: test/external/process_replay/reset.py
- name: benchmark openpilot 0.9.9 driving_vision
run: BENCHMARK_LOG=openpilot_0_9_9_vision PYTHONPATH=. NOLOCALS=1 FLOAT16=1 IMAGE=2 QCOM=1 taskset -c 4-7 python3 test/external/external_benchmark_openpilot.py https://github.com/commaai/openpilot/raw/v0.9.9/selfdrive/modeld/models/driving_vision.onnx
- name: benchmark openpilot 0.9.9 driving_policy
run: BENCHMARK_LOG=openpilot_0_9_9_policy PYTHONPATH=. NOLOCALS=1 FLOAT16=1 IMAGE=2 QCOM=1 taskset -c 4-7 python3 test/external/external_benchmark_openpilot.py https://github.com/commaai/openpilot/raw/v0.9.9/selfdrive/modeld/models/driving_policy.onnx
- name: benchmark openpilot 0.9.9 dmonitoring
run: BENCHMARK_LOG=openpilot_0_9_9_dmonitoring PYTHONPATH=. NOLOCALS=1 FLOAT16=1 IMAGE=2 QCOM=1 taskset -c 4-7 python3 test/external/external_benchmark_openpilot.py https://github.com/commaai/openpilot/raw/v0.9.9/selfdrive/modeld/models/dmonitoring_model.onnx
- name: openpilot compile3 0.9.9 driving_vision
run: BENCHMARK_LOG=openpilot_0_9_9_vision PYTHONPATH=. NOLOCALS=1 FLOAT16=1 IMAGE=2 QCOM=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/v0.9.9/selfdrive/modeld/models/driving_vision.onnx
run: PYTHONPATH="." ASSERT_MIN_STEP_TIME=18 QCOM=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/v0.9.9/selfdrive/modeld/models/driving_vision.onnx
- name: openpilot compile3 0.9.9 driving_policy
run: BENCHMARK_LOG=openpilot_0_9_9_policy PYTHONPATH=. NOLOCALS=1 FLOAT16=1 IMAGE=2 QCOM=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/v0.9.9/selfdrive/modeld/models/driving_policy.onnx
run: PYTHONPATH="." ASSERT_MIN_STEP_TIME=7 QCOM=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/v0.9.9/selfdrive/modeld/models/driving_policy.onnx
- name: openpilot compile3 0.9.9 dmonitoring
run: BENCHMARK_LOG=openpilot_0_9_9_dmonitoring PYTHONPATH=. NOLOCALS=1 FLOAT16=1 IMAGE=2 QCOM=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/v0.9.9/selfdrive/modeld/models/dmonitoring_model.onnx
- name: openpilot compile3 0.10.0 driving_policy
run: BENCHMARK_LOG=openpilot_0_10_0_policy PYTHONPATH="." ASSERT_MIN_STEP_TIME=4 DEV=QCOM FLOAT16=1 IMAGE=2 NOLOCALS=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/v0.10.0/selfdrive/modeld/models/driving_policy.onnx
- name: openpilot compile3 0.10.0 dmonitoring
run: BENCHMARK_LOG=openpilot_0_10_0_dmonitoring PYTHONPATH="." ASSERT_MIN_STEP_TIME=12 DEV=QCOM FLOAT16=1 IMAGE=2 NOLOCALS=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/v0.10.0/selfdrive/modeld/models/dmonitoring_model.onnx
- name: openpilot compile3 0.10.1 driving_vision
# TODO: ASSERT_MIN_STEP_TIME=17
run: BENCHMARK_LOG=openpilot_0_10_1_vision PYTHONPATH="." ASSERT_MIN_STEP_TIME=21 DEV=QCOM FLOAT16=1 IMAGE=2 NOLOCALS=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/720392c9a5b986981fdbed1bb8c47a6c5573a50e/selfdrive/modeld/models/driving_vision.onnx
- name: openpilot compile3 0.10.1 driving_policy
run: BENCHMARK_LOG=openpilot_0_10_1_policy PYTHONPATH="." ASSERT_MIN_STEP_TIME=4 DEV=QCOM FLOAT16=1 IMAGE=2 NOLOCALS=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/720392c9a5b986981fdbed1bb8c47a6c5573a50e/selfdrive/modeld/models/driving_policy.onnx
- name: openpilot compile3 0.10.1 dmonitoring
# TODO: ASSERT_MIN_STEP_TIME=10
run: BENCHMARK_LOG=openpilot_0_10_1_dmonitoring PYTHONPATH="." ASSERT_MIN_STEP_TIME=12 DEV=QCOM FLOAT16=1 IMAGE=2 NOLOCALS=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/720392c9a5b986981fdbed1bb8c47a6c5573a50e/selfdrive/modeld/models/dmonitoring_model.onnx
run: PYTHONPATH="." ASSERT_MIN_STEP_TIME=12 QCOM=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/v0.9.9/selfdrive/modeld/models/dmonitoring_model.onnx
- name: openpilot compile3 Space Lab policy + vision
run: |
PYTHONPATH="." ASSERT_MIN_STEP_TIME=4 QCOM=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://gitlab.com/commaai/openpilot-lfs.git/gitlab-lfs/objects/22aec22a10ce09384d4a4af2a0bbff08d54af7e0c888503508f356fae4ff0e29
PYTHONPATH="." ASSERT_MIN_STEP_TIME=26 QCOM=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://gitlab.com/commaai/openpilot-lfs.git/gitlab-lfs/objects/c824f68646a3b94f117f01c70dc8316fb466e05fbd42ccdba440b8a8dc86914b
- name: benchmark MobileNetV2 on DSP
run: |
# generate quantized weights
@@ -648,9 +641,19 @@ jobs:
ln -s /data/home/tiny/tinygrad/testsig-*.so .
PYTHONPATH=. CC=clang-19 CPU=1 CPU_LLVM=0 QUANT=1 CNT=0 python3 examples/test_onnx_imagenet.py https://github.com/xamcat/mobcat-samples/raw/refs/heads/master/onnx_runtime/InferencingSample/InferencingSample/mobilenetv2-7.onnx /tmp/model.quant.onnx
# benchmark on DSP with NOOPT=1, the devectorizer has issues
PYTHONPATH=. CC=clang-19 DSP=1 NOOPT=1 CNT=2 DEBUG=2 python3 examples/test_onnx_imagenet.py /tmp/model.quant.onnx
PYTHONPATH=. CC=clang-19 DSP=1 DONT_REALIZE_EXPAND=1 NOOPT=1 CNT=2 DEBUG=2 python3 examples/test_onnx_imagenet.py /tmp/model.quant.onnx
- name: Run process replay tests
run: cp test/external/process_replay/process_replay.py ./process_replay.py && git fetch origin master && git -c advice.detachedHead=false checkout origin/master && PYTHONPATH=. python3 process_replay.py
- uses: actions/upload-artifact@v4
with:
name: Speed (comma)
path: |
openpilot_compile_0_9_4.txt
openpilot_compile_0_9_7.txt
openpilot_0_9_4.txt
openpilot_0_9_7.txt
openpilot_image_0_9_4.txt
openpilot_image_0_9_7.txt
testreddriverbenchmark:
name: AM Benchmark
+2 -2
View File
@@ -12,7 +12,7 @@ jobs:
run_script_job:
runs-on: [self-hosted, Linux, tinybox]
if: github.repository_owner == 'tinygrad'
timeout-minutes: 720
timeout-minutes: 360
steps:
- name: Checkout Code
@@ -27,4 +27,4 @@ jobs:
run: |
rm "~/.cache/tinygrad/cache_mlperf.db" || true
BENCHMARK_LOG=mlpert_train_resnet LOGMLPERF=0 CACHEDB="~/.cache/tinygrad/cache_mlperf.db" examples/mlperf/training_submission_v5.1/tinycorp/benchmarks/resnet/implementations/tinybox_red/run_and_time.sh
rm "~/.cache/tinygrad/cache_mlperf.db"
rm "~/.cache/tinygrad/cache_mlperf.db"
+91 -107
View File
@@ -2,7 +2,7 @@ name: Unit Tests
env:
# increment this when downloads substantially change to avoid the internet
DOWNLOAD_CACHE_VERSION: '12'
PYTHON_CACHE_VERSION: '4'
PYTHON_CACHE_VERSION: '3'
APT_CACHE_VERSION: '1'
BUILD_CACHE_VERSION: '1'
CAPTURE_PROCESS_REPLAY: 1
@@ -89,65 +89,64 @@ jobs:
clang -O2 recognize.c -lm -o recognize
cat test/models/efficientnet/Chicken.jpg | ./recognize | grep cock
# TODO: fix the torch backend and reenable
# torchbackend:
# name: Torch Backend Tests
# runs-on: ubuntu-latest
# timeout-minutes: 15
# steps:
# - name: Checkout Code
# uses: actions/checkout@v4
# - name: Setup Environment
# uses: ./.github/actions/setup-tinygrad
# with:
# key: torch-backend-pillow-torchvision-et-pt
# deps: testing_minimal
# pydeps: "pillow torchvision expecttest"
# llvm: 'true'
# - name: Install ninja
# run: |
# sudo apt update || true
# sudo apt install -y --no-install-recommends ninja-build
# - name: Lint with ruff
# run: |
# pip3 install --upgrade --force-reinstall ruff==0.11.0
# python3 -m ruff check extra/torch_backend/backend.py
# - name: Test one op
# run: FORWARD_ONLY=1 TINY_BACKEND=1 python3 test/test_ops.py TestOps.test_add
# - name: Test ResNet-18
# run: DEBUG=2 python3 extra/torch_backend/example.py
# - name: My (custom) tests
# run: python3 extra/torch_backend/test.py
# - name: Test one op in torch tests
# run: DEBUG=2 python3 extra/torch_backend/torch_tests.py TestTinyBackendPRIVATEUSE1.test_unary_log_tiny_float32
# - name: Test Ops with TINY_BACKEND
# run: CPU=1 CPU_LLVM=1 LLVMOPT=0 TINY_BACKEND=1 python3 -m pytest -n auto test/test_ops.py --durations=20
# - name: Test in-place operations on views
# run: TORCH_DEBUG=1 python3 extra/torch_backend/test_inplace.py
# - name: Test multi-gpu
# run: CPU=1 CPU_LLVM=1 GPUS=4 TORCH_DEBUG=1 python3 extra/torch_backend/test_multigpu.py
torchbackend:
name: Torch Backend Tests
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Setup Environment
uses: ./.github/actions/setup-tinygrad
with:
key: torch-backend-pillow-torchvision-et-pt
deps: testing_minimal
pydeps: "pillow torchvision expecttest"
llvm: 'true'
- name: Install ninja
run: |
sudo apt update || true
sudo apt install -y --no-install-recommends ninja-build
- name: Lint with ruff
run: |
pip3 install --upgrade --force-reinstall ruff==0.11.0
python3 -m ruff check extra/torch_backend/backend.py
- name: Test one op
run: FORWARD_ONLY=1 TINY_BACKEND=1 python3 test/test_ops.py TestOps.test_add
- name: Test ResNet-18
run: DEBUG=2 python3 extra/torch_backend/example.py
- name: My (custom) tests
run: python3 extra/torch_backend/test.py
- name: Test one op in torch tests
run: DEBUG=2 python3 extra/torch_backend/torch_tests.py TestTinyBackendPRIVATEUSE1.test_unary_log_tiny_float32
- name: Test Ops with TINY_BACKEND
run: CPU=1 CPU_LLVM=1 LLVMOPT=0 TINY_BACKEND=1 python3 -m pytest -n auto test/test_ops.py --durations=20
- name: Test in-place operations on views
run: TORCH_DEBUG=1 python3 extra/torch_backend/test_inplace.py
- name: Test multi-gpu
run: CPU=1 CPU_LLVM=1 GPUS=4 TORCH_DEBUG=1 python3 extra/torch_backend/test_multigpu.py
# torchbackendmore:
# name: Torch Backend Tests More
# runs-on: ubuntu-latest
# timeout-minutes: 15
# steps:
# - name: Checkout Code
# uses: actions/checkout@v4
# - name: Setup Environment
# uses: ./.github/actions/setup-tinygrad
# with:
# key: torch-backend-pillow-torchvision-et-pt
# deps: testing_minimal
# llvm: 'true'
# - name: Install ninja
# run: |
# sudo apt update || true
# sudo apt install -y --no-install-recommends ninja-build
# - name: Test beautiful_mnist in torch with TINY_BACKEND
# run: STEPS=20 CPU=1 TARGET_EVAL_ACC_PCT=90.0 TINY_BACKEND=1 python3 examples/other_mnist/beautiful_mnist_torch.py
# - name: Test some torch tests (expect failure)
# run: python3 -m pytest extra/torch_backend/torch_tests.py -v --tb=no || true
torchbackendmore:
name: Torch Backend Tests More
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Setup Environment
uses: ./.github/actions/setup-tinygrad
with:
key: torch-backend-pillow-torchvision-et-pt
deps: testing_minimal
llvm: 'true'
- name: Install ninja
run: |
sudo apt update || true
sudo apt install -y --no-install-recommends ninja-build
- name: Test beautiful_mnist in torch with TINY_BACKEND
run: CPU=1 CPU_LLVM=1 TARGET_EVAL_ACC_PCT=96.0 TINY_BACKEND=1 python3 examples/other_mnist/beautiful_mnist_torch.py
- name: Test some torch tests (expect failure)
run: python3 -m pytest extra/torch_backend/torch_tests.py -v --tb=no || true
bepython:
name: Python Backend
@@ -204,7 +203,7 @@ jobs:
DEBUG=2 EMULATE=CUDA FORWARD_ONLY=1 PYTHON=1 python3 test/test_ops.py TestOps.test_gemm_fp16
DEBUG=2 EMULATE=CUDA ALLOW_TF32=1 FORWARD_ONLY=1 PYTHON=1 python3 test/test_ops.py TestOps.test_gemm
DEBUG=2 EMULATE=CUDA_SM75 FORWARD_ONLY=1 PYTHON=1 python3 test/test_ops.py TestOps.test_gemm_fp16
DEBUG=2 EMULATE=CUDA_SM89 ALLOW_TF32=1 FORWARD_ONLY=1 PYTHON=1 python3 test/opt/test_tensor_cores.py
DEBUG=2 EMULATE=CUDA ALLOW_TF32=1 FORWARD_ONLY=1 PYTHON=1 python3 test/opt/test_tensor_cores.py
- name: Test emulated INTEL OpenCL tensor cores
run: DEBUG=2 EMULATE=INTEL FORWARD_ONLY=1 PYTHON=1 HALF=1 N=64 python3 ./extra/gemm/simple_matmul.py
- name: Test emulated AMX tensor cores
@@ -230,7 +229,7 @@ jobs:
uses: ./.github/actions/setup-tinygrad
with:
key: linting-only
python-version: '3.11'
python-version: '3.10'
deps: linting
- name: Lint bad-indentation and trailing-whitespace with pylint
run: python -m pylint --disable=all -e W0311 -e C0303 --jobs=0 --indent-string=' ' --recursive=y .
@@ -243,9 +242,8 @@ jobs:
run: |
python -m mypy --strict-equality --lineprecision-report .
cat lineprecision.txt
# broken because of UPatAny
#- name: Run TYPED=1
# run: TYPED=1 python -c "import tinygrad"
- name: Run TYPED=1
run: TYPED=1 python -c "import tinygrad"
unittest:
name: Unit Tests
@@ -265,6 +263,8 @@ jobs:
run: python -c "from tinygrad import Device; assert Device.DEFAULT == 'CPU', Device.DEFAULT"
- name: Run unit tests
run: CPU=1 python -m pytest -n=auto test/unit/ --durations=20
- name: Check SPEC=1
run: SPEC=1 python3 test/test_tiny.py
- name: Run targetted tests on NULL backend
run: NULL=1 python3 -m unittest test.test_multitensor.TestMultiTensor.test_data_parallel_resnet_train_step test/device/test_null.py
# TODO: too slow
@@ -272,8 +272,6 @@ jobs:
# run: NULL=1 DEBUG=1 python3 examples/sdxl.py --seed 0 --noshow --timing --fakeweights
- name: Run Clip tests for SD MLPerf on NULL backend
run: NULL=1 python -m pytest -n=auto test/external/mlperf_stable_diffusion/external_test_models.py::TestOpenClip --durations=20
- name: Run AMD emulated BERT training on NULL backend
run: EMULATE=AMD_RDNA4 NULL=1 CAPTURE_PROCESS_REPLAY=0 DEFAULT_FLOAT=HALF BENCHMARK=10 BS=66 GPUS=1 BERT_LAYERS=2 MODEL=bert python3 examples/mlperf/model_train.py
# TODO: support fake weights
#- name: Run LLaMA 7B on 4 fake devices
# run: NULL=1 python3 examples/llama.py --gen 1 --size 7B --shard 4 --prompt "Hello." --count 3 --temperature 0 --timing
@@ -290,27 +288,8 @@ jobs:
python extra/optimization/extract_dataset.py
gzip -c /tmp/sops > extra/datasets/sops.gz
#DEBUG=1 MIN_ASTS=1 python extra/optimization/get_action_space.py
- name: Repo line count < 18500 lines
run: MAX_LINE_COUNT=18500 python sz.py
spec:
strategy:
fail-fast: false
matrix:
group: [1, 2]
name: SPEC=2 (${{ matrix.group }})
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Setup Environment
uses: ./.github/actions/setup-tinygrad
with:
key: spec-unit
deps: testing_unit
- name: Test SPEC=2
run: IGNORE_OOB=0 SPEC=2 PYTHONPATH="." pytest --maxfail=10 -n auto --durations=30 --ignore=test/models --ignore test/unit/test_hashing.py --timeout 60 -k "not test_setitem_big" --splits 2 --group ${{ matrix.group }}
- name: Repo line count < 18000 lines
run: MAX_LINE_COUNT=18000 python sz.py
fuzzing:
name: Fuzzing
@@ -328,6 +307,10 @@ jobs:
run: python test/external/fuzz_symbolic.py
- name: Fuzz Test fast idiv
run: python test/external/fuzz_fast_idiv.py
- name: Fuzz Test shapetracker
run: CNT=50 python test/external/fuzz_shapetracker.py
- name: Fuzz Test shapetracker math
run: CNT=200 python test/external/fuzz_shapetracker_math.py
- name: Fuzz Test shape ops
run: python test/external/fuzz_shape_ops.py
@@ -369,7 +352,7 @@ jobs:
- name: Run Kernel Count Test
run: CL=1 python -m pytest -n=auto test/external/external_test_opt.py
- name: Run fused optimizer tests
run: CL=1 FUSE_OPTIM=1 python -m pytest -n=auto test/models/test_mnist.py test/test_optim.py -k "not muon"
run: CL=1 FUSE_OPTIM=1 python -m pytest -n=auto test/models/test_mnist.py
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
@@ -392,13 +375,17 @@ jobs:
llvm: 'true'
- name: Test openpilot model kernel count and gate usage
run: |
ALLOWED_KERNEL_COUNT=123 ALLOWED_READ_IMAGE=1452 ALLOWED_GATED_READ_IMAGE=122 FLOAT16=1 CL=1 IMAGE=2 python examples/openpilot/compile3.py https://gitlab.com/commaai/openpilot-lfs.git/gitlab-lfs/objects/cf6376aa9a090f0da26c280ef69eabf9bbdd51d1faac9ed392919c3db69be916
- name: Test openpilot CL compile fp16
run: FLOAT16=1 DEBUGCL=1 CL=1 IMAGE=2 python examples/openpilot/compile3.py https://gitlab.com/commaai/openpilot-lfs.git/gitlab-lfs/objects/cf6376aa9a090f0da26c280ef69eabf9bbdd51d1faac9ed392919c3db69be916
- name: Test openpilot CL compile fp32 (test correctness)
run: DEBUGCL=1 CL=1 IMAGE=2 SELFTEST=1 python examples/openpilot/compile3.py https://github.com/haraschax/filedump/raw/refs/heads/master/driving_vision_fp32.onnx
- name: Test openpilot LLVM compile fp16
run: FLOAT16=1 CPU=1 CPU_LLVM=1 python examples/openpilot/compile3.py https://gitlab.com/commaai/openpilot-lfs.git/gitlab-lfs/objects/cf6376aa9a090f0da26c280ef69eabf9bbdd51d1faac9ed392919c3db69be916
ALLOWED_KERNEL_COUNT=190 ALLOWED_READ_IMAGE=2081 ALLOWED_GATED_READ_IMAGE=28 FLOAT16=0 CL=1 IMAGE=2 python examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/v0.9.4/selfdrive/modeld/models/supercombo.onnx
- name: Test openpilot alt model correctness (float32)
run: FLOAT16=0 DEBUGCL=1 CL=1 IMAGE=2 python examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/3799fe46b3a629e491d4b8498b8ae83e4c88c304/selfdrive/modeld/models/supercombo.onnx
- name: Test openpilot fastvits model correctness (float32)
run: FLOAT16=0 DEBUGCL=1 CL=1 IMAGE=2 python examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/9118973ed03c1ae1d40cf69a29507ec2cc78efd7/selfdrive/modeld/models/supercombo.onnx
# - name: Test openpilot simple_plan vision model correctness (float32)
# run: FLOAT16=0 DEBUGCL=1 CL=1 IMAGE=2 python examples/openpilot/compile3.py https://gitlab.com/commaai/openpilot-lfs.git/gitlab-lfs/objects/35ff4f4577002f2685e50c8346addae33fe8da27a41dd4d6a0f14d1f4b1af81b
- name: Test openpilot LLVM compile
run: CPU=1 CPU_LLVM=1 LLVMOPT=1 JIT=2 BEAM=0 IMAGE=0 python examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/9118973ed03c1ae1d40cf69a29507ec2cc78efd7/selfdrive/modeld/models/supercombo.onnx
- name: Test openpilot compile4
run: NOLOCALS=1 CL=1 IMAGE=2 FLOAT16=1 DEBUG=2 python3 examples/openpilot/compile4.py
- name: Run process replay tests
uses: ./.github/actions/process-replay
@@ -540,11 +527,11 @@ jobs:
pydeps: "pillow"
llvm: "true"
- name: Test LLVM=1 DEVECTORIZE=0
run: CPU=1 CPU_LLVM=1 DEVECTORIZE=0 python3 -m pytest -n auto test/test_tiny.py test/test_ops.py
run: CPU=1 CPU_LLVM=1 DEVECTORIZE=0 python3 -m pytest -n auto test/test_tiny.py test/test_ops.py -k "not test_avg_pool3d_failure"
- name: Test LLVM=1 DEVECTORIZE=0 for model
run: CPU=1 CPU_LLVM=1 DEVECTORIZE=0 python3 test/models/test_efficientnet.py
- name: Test CPU=1 DEVECTORIZE=0
run: CPU=1 CPU_LLVM=0 DEVECTORIZE=0 python3 -m pytest -n auto test/test_tiny.py test/test_ops.py
run: CPU=1 CPU_LLVM=0 DEVECTORIZE=0 python3 -m pytest -n auto test/test_tiny.py test/test_ops.py -k "not test_avg_pool3d_failure"
testdsp:
name: Linux (DSP)
@@ -645,7 +632,6 @@ jobs:
run: TRANSCENDENTAL=2 python -m pytest -n=auto test/test_ops.py::TestOps::test_sin test/test_ops.py::TestOps::test_cos test/test_ops.py::TestOps::test_tan test/test_ops.py::TestOps::test_exp test/test_ops.py::TestOps::test_log --durations=20
- name: Run TestOps.test_add with SQTT
run: |
VIZ=1 PMC=1 DEBUG=5 python3 test/test_ops.py TestOps.test_add
VIZ=1 SQTT=1 DEBUG=5 python3 test/test_ops.py TestOps.test_add
extra/sqtt/rgptool.py create "/tmp/profile.pkl.$USER" -o /tmp/gpu0.rgp
- name: Run process replay tests
@@ -689,7 +675,7 @@ jobs:
strategy:
fail-fast: false
matrix:
backend: [llvm, cpu, opencl, lvp]
backend: [llvm, cpu, opencl]
name: Linux (${{ matrix.backend }})
runs-on: ubuntu-22.04
@@ -703,10 +689,9 @@ jobs:
key: ${{ matrix.backend }}-minimal
deps: testing_minimal
opencl: ${{ matrix.backend == 'opencl' && 'true' }}
llvm: ${{ matrix.backend == 'llvm' || matrix.backend == 'lvp' }}
mesa: ${{ matrix.backend == 'lvp' && 'true' }}
llvm: ${{ matrix.backend == 'llvm' && 'true' }}
- name: Set env
run: printf "${{ matrix.backend == 'llvm' && 'CPU=1\nCPU_LLVM=1' || matrix.backend == 'cpu' && 'CPU=1\nCPU_LLVM=0\nCPU_COUNT=2' || matrix.backend == 'opencl' && 'CL=1' || matrix.backend == 'lvp' && 'CPU=1\nCPU_LVP=1' }}" >> $GITHUB_ENV
run: printf "${{ matrix.backend == 'llvm' && 'CPU=1\nCPU_LLVM=1' || matrix.backend == 'cpu' && 'CPU=1\nCPU_LLVM=0\nCPU_COUNT=2' || matrix.backend == 'opencl' && 'CL=1' }}" >> $GITHUB_ENV
- name: Check Device.DEFAULT and print some source
run: |
python3 -c "from tinygrad import Device; assert Device.DEFAULT in ['CPU','CL'], Device.DEFAULT"
@@ -908,7 +893,7 @@ jobs:
strategy:
fail-fast: false
matrix:
backend: [metal, llvm, cpu, lvp]
backend: [metal, llvm, cpu]
name: MacOS (${{ matrix.backend }})
runs-on: macos-15
timeout-minutes: 20
@@ -921,13 +906,12 @@ jobs:
key: macos-${{ matrix.backend }}-minimal
deps: testing_minimal
pydeps: "capstone"
llvm: ${{ matrix.backend == 'llvm' || matrix.backend == 'lvp' }}
mesa: ${{ matrix.backend == 'lvp' && 'true' }}
llvm: ${{ matrix.backend == 'llvm' && 'true' }}
- name: Set env
run: printf "${{ matrix.backend == 'llvm' && 'CPU=1\nCPU_LLVM=1' || matrix.backend == 'cpu' && 'CPU=1\nCPU_LLVM=0\nCPU_COUNT=2' || matrix.backend == 'metal' && 'METAL=1' || matrix.backend == 'lvp' && 'CPU=1\nCPU_LVP=1' }}" >> $GITHUB_ENV
run: printf "${{ matrix.backend == 'llvm' && 'CPU=1\nCPU_LLVM=1' || matrix.backend == 'cpu' && 'CPU=1\nCPU_LLVM=0\nCPU_COUNT=2' || matrix.backend == 'metal' && 'METAL=1'}}" >> $GITHUB_ENV
- name: Check Device.DEFAULT and print some source
run: |
python -c "from tinygrad import Device; assert Device.DEFAULT == {'LLVM':'CPU','LVP':'CPU'}.get(x:='${{ matrix.backend }}'.upper(), x), Device.DEFAULT"
python -c "from tinygrad import Device; assert Device.DEFAULT == {'LLVM':'CPU'}.get(x:='${{ matrix.backend }}'.upper(), x), Device.DEFAULT"
DEBUG=4 python3 test/test_tiny.py TestTiny.test_plus
- name: Run pytest (${{ matrix.backend }})
run: python3 -m pytest -n=auto test/ --ignore=test/models --ignore=test/unit --durations=20
-3
View File
@@ -38,7 +38,6 @@ extra/huggingface_onnx/models/*
extra/huggingface_onnx/*.yaml
extra/weights
venv
venv_sd_mlperf
examples/**/net.*[js,json]
examples/**/*.safetensors
node_modules
@@ -63,5 +62,3 @@ profile_stats
*.log
target
.mypy_cache
mutants
.mutmut-cache
+2 -2
View File
@@ -28,7 +28,7 @@ repos:
pass_filenames: false
- id: tests
name: subset of tests
entry: env OMP_NUM_THREADS=1 PYTHONPATH="." python3 -m pytest -n=6 test/test_ops.py test/test_dtype.py test/test_schedule.py test/test_assign.py
entry: env PYTHONPATH="." python3 -m pytest -n=8 test/test_ops.py test/test_dtype.py test/test_schedule.py test/test_assign.py
language: system
always_run: true
pass_filenames: false
pass_filenames: false
+8 -87
View File
@@ -31,9 +31,7 @@ $(for p in "$@"; do echo " $p,"; done)
]
def _try_dlopen_$name():
library = ctypes.util.find_library("$name")
if library:
try: return ctypes.CDLL(library)
except OSError: pass
if library: return ctypes.CDLL(library)
for candidate in PATHS_TO_TRY:
try: return ctypes.CDLL(candidate)
except OSError: pass
@@ -188,7 +186,6 @@ nv_status_codes = {}
extra/nv_gpu_driver/g_rpc-message-header.h \
extra/nv_gpu_driver/gsp_static_config.h \
extra/nv_gpu_driver/vbios.h \
extra/nv_gpu_driver/pci_exp_table.h \
--clang-args="-DRPC_MESSAGE_STRUCTURES -DRPC_STRUCTURES -include $NVKERN_SRC/src/common/sdk/nvidia/inc/nvtypes.h -I$NVKERN_SRC/src/nvidia/generated -I$NVKERN_SRC/src/common/inc -I$NVKERN_SRC/src/nvidia/inc -I$NVKERN_SRC/src/nvidia/interface/ -I$NVKERN_SRC/src/nvidia/inc/kernel -I$NVKERN_SRC/src/nvidia/inc/libraries -I$NVKERN_SRC/src/nvidia/arch/nvalloc/common/inc -I$NVKERN_SRC/kernel-open/nvidia-uvm -I$NVKERN_SRC/kernel-open/common/inc -I$NVKERN_SRC/src/common/sdk/nvidia/inc -I$NVKERN_SRC/src/nvidia/arch/nvalloc/unix/include -I$NVKERN_SRC/src/common/sdk/nvidia/inc/ctrl" \
-o $BASE/nv/nv.py
@@ -435,13 +432,11 @@ generate_sqtt() {
$ROCPROF_SRC/include/rocprof_trace_decoder.h \
$ROCPROF_SRC/include/trace_decoder_instrument.h \
$ROCPROF_SRC/include/trace_decoder_types.h \
-o $BASE/rocprof.py
fixup $BASE/rocprof.py
sed -i '1s/^/# pylint: skip-file\n/' $BASE/rocprof.py
sed -i "s/import ctypes/import ctypes, ctypes.util/g" $BASE/rocprof.py
patch_dlopen $BASE/rocprof.py rocprof-trace-decoder "'/usr/local/lib/librocprof-trace-decoder.so'" "'/usr/local/lib/librocprof-trace-decoder.dylib'"
sed -i "s/def _try_dlopen_rocprof-trace-decoder():/def _try_dlopen_rocprof_trace_decoder():/g" $BASE/rocprof.py
sed -i "s|FunctionFactoryStub()|_try_dlopen_rocprof_trace_decoder()|g" $BASE/rocprof.py
-o extra/sqtt/rocprof/rocprof.py
fixup extra/sqtt/rocprof/rocprof.py
sed -i '1s/^/# pylint: skip-file\n/' extra/sqtt/rocprof/rocprof.py
sed -i "s/import ctypes/import ctypes, ctypes.util/g" extra/sqtt/rocprof/rocprof.py
sed -i "s|FunctionFactoryStub()|ctypes.CDLL(ctypes.util.find_library('rocprof-trace-decoder'))|g" extra/sqtt/rocprof/rocprof.py
}
generate_webgpu() {
@@ -466,80 +461,6 @@ generate_libusb() {
python3 -c "import tinygrad.runtime.autogen.libusb"
}
generate_mesa() {
MESA_TAG="mesa-25.2.4"
MESA_SRC=/tmp/mesa-$MESA_TAG
TINYMESA_TAG=tinymesa-32dc66c
TINYMESA_DIR=/tmp/tinymesa-$MESA_TAG-$TINYMESA_TAG/
TINYMESA_SO=$TINYMESA_DIR/libtinymesa_cpu.so
if [ ! -d "$MESA_SRC" ]; then
git clone --depth 1 --branch $MESA_TAG https://gitlab.freedesktop.org/mesa/mesa.git $MESA_SRC
pushd .
cd $MESA_SRC
git reset --hard $MESA_COMMIT_HASH
# clang 14 doesn't support packed enums
sed -i "s/enum \w\+ \(\w\+\);$/uint8_t \1;/" $MESA_SRC/src/nouveau/headers/nv_device_info.h
sed -i "s/enum \w\+ \(\w\+\);$/uint8_t \1;/" $MESA_SRC/src/nouveau/compiler/nak.h
sed -i "s/nir_instr_type \(\w\+\);/uint8_t \1;/" $MESA_SRC/src/compiler/nir/nir.h
mkdir -p gen/util/format
python3 src/util/format/u_format_table.py src/util/format/u_format.yaml --enums > gen/util/format/u_format_gen.h
python3 src/compiler/nir/nir_opcodes_h.py > gen/nir_opcodes.h
python3 src/compiler/nir/nir_intrinsics_h.py --outdir gen
python3 src/compiler/nir/nir_intrinsics_indices_h.py --outdir gen
python3 src/compiler/nir/nir_builder_opcodes_h.py > gen/nir_builder_opcodes.h
python3 src/compiler/nir/nir_intrinsics_h.py --outdir gen
python3 src/compiler/builtin_types_h.py gen/builtin_types.h
popd
fi
if [ ! -d "$TINYMESA_DIR" ]; then
mkdir $TINYMESA_DIR
curl -L https://github.com/sirhcm/tinymesa/releases/download/$TINYMESA_TAG/libtinymesa_cpu-$MESA_TAG-linux-amd64.so -o $TINYMESA_SO
fi
clang2py -k cdefstu \
$MESA_SRC/src/compiler/nir/nir.h \
$MESA_SRC/src/compiler/nir/nir_builder.h \
$MESA_SRC/src/compiler/nir/nir_shader_compiler_options.h \
$MESA_SRC/src/compiler/nir/nir_serialize.h \
$MESA_SRC/gen/nir_intrinsics.h \
$MESA_SRC/src/nouveau/headers/nv_device_info.h \
$MESA_SRC/src/nouveau/compiler/nak.h \
$MESA_SRC/src/gallium/auxiliary/gallivm/lp_bld.h \
$MESA_SRC/src/gallium/auxiliary/gallivm/lp_bld_passmgr.h \
$MESA_SRC/src/gallium/auxiliary/gallivm/lp_bld_misc.h \
$MESA_SRC/src/gallium/auxiliary/gallivm/lp_bld_type.h \
$MESA_SRC/src/gallium/auxiliary/gallivm/lp_bld_init.h \
$MESA_SRC/src/gallium/auxiliary/gallivm/lp_bld_nir.h \
$MESA_SRC/src/gallium/auxiliary/gallivm/lp_bld_struct.h \
$MESA_SRC/src/gallium/auxiliary/gallivm/lp_bld_jit_types.h \
$MESA_SRC/src/gallium/auxiliary/gallivm/lp_bld_flow.h \
$MESA_SRC/src/gallium/auxiliary/gallivm/lp_bld_const.h \
$MESA_SRC/src/compiler/glsl_types.h \
$MESA_SRC/src/util/blob.h \
$MESA_SRC/src/util/ralloc.h \
--clang-args="-DHAVE_ENDIAN_H -DHAVE_STRUCT_TIMESPEC -DHAVE_PTHREAD -I$MESA_SRC/src -I$MESA_SRC/include -I$MESA_SRC/gen -I$MESA_SRC/src/compiler/nir -I$MESA_SRC/src/gallium/auxiliary -I$MESA_SRC/src/gallium/include -I$(llvm-config-20 --includedir)" \
-l $TINYMESA_SO \
-o $BASE/mesa.py
LVP_NIR_OPTIONS=$(./extra/mesa/lvp_nir_options.sh $MESA_SRC)
fixup $BASE/mesa.py
patch_dlopen $BASE/mesa.py tinymesa_cpu "(BASE:=os.getenv('MESA_PATH', f\"/usr{'/local/' if helpers.OSX else '/'}lib\"))+'/libtinymesa_cpu'+(EXT:='.dylib' if helpers.OSX else '.so')" "f'{BASE}/libtinymesa{EXT}'" "'/opt/homebrew/lib/libtinymesa_cpu.dylib'" "'/opt/homebrew/lib/libtinymesa.dylib'"
echo "lvp_nir_options = gzip.decompress(base64.b64decode('$LVP_NIR_OPTIONS'))" >> $BASE/mesa.py
sed -i "/in_dll/s/.*/try: &\nexcept (AttributeError, ValueError): pass/" $BASE/mesa.py
sed -i "s/import ctypes/import ctypes, ctypes.util, os, gzip, base64, subprocess, tinygrad.helpers as helpers/" $BASE/mesa.py
sed -i "s/ctypes.CDLL('.\+')/(dll := _try_dlopen_tinymesa_cpu())/" $BASE/mesa.py
echo "def __getattr__(nm): raise AttributeError('LLVMpipe requires tinymesa_cpu' if 'tinymesa_cpu' not in dll._name else f'attribute {nm} not found') if dll else FileNotFoundError(f'libtinymesa not found (MESA_PATH={BASE}). See https://github.com/sirhcm/tinymesa ($TINYMESA_TAG, $MESA_TAG)')" >> $BASE/mesa.py
sed -i "s/ctypes.glsl_base_type/glsl_base_type/" $BASE/mesa.py
# bitfield bug in clang2py
sed -i "s/('fp_fast_math', ctypes.c_bool, 9)/('fp_fast_math', ctypes.c_uint32, 9)/" $BASE/mesa.py
sed -i "s/('\(\w\+\)', pipe_shader_type, 8)/('\1', ctypes.c_ubyte)/" $BASE/mesa.py
sed -i "s/\([0-9]\+\)()/\1/" $BASE/mesa.py
sed -i '/struct_nir_builder._pack_ = 1 # source:False/d' "$BASE/mesa.py"
python3 -c "import tinygrad.runtime.autogen.mesa"
}
if [ "$1" == "opencl" ]; then generate_opencl
elif [ "$1" == "hip" ]; then generate_hip
elif [ "$1" == "comgr" ]; then generate_comgr
@@ -550,6 +471,7 @@ elif [ "$1" == "kfd" ]; then generate_kfd
elif [ "$1" == "nv" ]; then generate_nv
elif [ "$1" == "amd" ]; then generate_amd
elif [ "$1" == "am" ]; then generate_am
elif [ "$1" == "nvdrv" ]; then generate_nvdrv
elif [ "$1" == "sqtt" ]; then generate_sqtt
elif [ "$1" == "qcom" ]; then generate_qcom
elif [ "$1" == "io_uring" ]; then generate_io_uring
@@ -562,7 +484,6 @@ elif [ "$1" == "pci" ]; then generate_pci
elif [ "$1" == "vfio" ]; then generate_vfio
elif [ "$1" == "webgpu" ]; then generate_webgpu
elif [ "$1" == "libusb" ]; then generate_libusb
elif [ "$1" == "mesa" ]; then generate_mesa
elif [ "$1" == "all" ]; then generate_opencl; generate_hip; generate_comgr; generate_cuda; generate_nvrtc; generate_hsa; generate_kfd; generate_nv; generate_amd; generate_io_uring; generate_libc; generate_am; generate_webgpu; generate_mesa
elif [ "$1" == "all" ]; then generate_opencl; generate_hip; generate_comgr; generate_cuda; generate_nvrtc; generate_hsa; generate_kfd; generate_nv; generate_amd; generate_io_uring; generate_libc; generate_am; generate_webgpu
else echo "usage: $0 <type>"
fi
+3 -1
View File
@@ -53,7 +53,9 @@ b = Buffer(DEVICE, 1, dtypes.int32).allocate().copyin(memoryview(bytearray(struc
idx = UOp.const(dtypes.index, 0)
buf_1 = UOp(Ops.DEFINE_GLOBAL, dtypes.int32.ptr(), (), 1)
buf_2 = UOp(Ops.DEFINE_GLOBAL, dtypes.int32.ptr(), (), 2)
alu = buf_1.index(idx) + buf_2.index(idx)
ld_1 = UOp(Ops.LOAD, dtypes.int32, (buf_1.index(idx),))
ld_2 = UOp(Ops.LOAD, dtypes.int32, (buf_2.index(idx),))
alu = ld_1 + ld_2
output_buf = UOp(Ops.DEFINE_GLOBAL, dtypes.int32.ptr(), (), 0)
st_0 = UOp(Ops.STORE, dtypes.void, (output_buf.index(idx), alu))
s = UOp(Ops.SINK, dtypes.void, (st_0,))
+109
View File
@@ -0,0 +1,109 @@
# Kernel Creation
Tinygrad lazily builds up a graph of Tensor operations. The Tensor graph includes a mix of:
- Buffer and Assignment Ops: `BUFFER`, `BUFFER_VIEW`, `COPY`, `ASSIGN`
- Movement Ops: `RESHAPE`, `EXPAND`, `PERMUTE`, `PAD`, `SHRINK`, `FLIP`
- Compute Ops: `ADD`, `MUL`, `REDUCE_AXIS`, ...
`Tensor.kernelize` creates the kernels and buffers needed to realize the output Tensor(s).
## Kernelize flow
Let's see how a multiply add Tensor graph becomes a fused elementwise kernel.
```py
# initialize 3 input buffers on the device
a = Tensor([1]).realize()
b = Tensor([2]).realize()
c = Tensor([3]).realize()
# create the Tensor graph
mul = a*b
out = mul+c
print(mul) # <Tensor <UOp METAL (1,) int (<Ops.MUL: 48>, None)> on METAL with grad None>
print(out) # <Tensor <UOp METAL (1,) int (<Ops.ADD: 52>, None)> on METAL with grad None>
out.kernelize()
print(mul) # <Tensor <UOp METAL (1,) int (<Ops.MUL: 48>, None)> on METAL with grad None>
print(out) # <Tensor <UOp METAL (1,) int (<Ops.ASSIGN: 66>, None)> on METAL with grad None>
```
The multiply Tensor stays the same because it is fused. The output Tensor's UOp becomes a new ASSIGN UOp:
```py
print(out.uop)
```
The first source is the output BUFFER:
```
UOp(Ops.BUFFER, dtypes.int, arg=1, src=(
UOp(Ops.DEVICE, dtypes.void, arg='METAL', src=()),
UOp(Ops.UNIQUE, dtypes.void, arg=6, src=()),))
```
And the second source is the KERNEL and its 4 buffer edges (output_buffer, a, b, c):
```
UOp(Ops.KERNEL, dtypes.void, arg=<Kernel 12 SINK(<Ops.STORE: 45>,) (__add__, __mul__)>, src=(
UOp(Ops.BUFFER, dtypes.int, arg=1, src=(
x1:=UOp(Ops.DEVICE, dtypes.void, arg='METAL', src=()),
UOp(Ops.UNIQUE, dtypes.void, arg=6, src=()),)),
UOp(Ops.BUFFER, dtypes.int, arg=1, src=(
x1,
UOp(Ops.UNIQUE, dtypes.void, arg=1, src=()),)),
UOp(Ops.BUFFER, dtypes.int, arg=1, src=(
x1,
UOp(Ops.UNIQUE, dtypes.void, arg=3, src=()),)),
UOp(Ops.BUFFER, dtypes.int, arg=1, src=(
x1,
UOp(Ops.UNIQUE, dtypes.void, arg=5, src=()),)),))
```
KERNEL describes the compute AST, metadata and memory dependencies.
BUFFER holds a reference to the device memory where the output will be stored.
Once a Tensor is kernelized, all children will LOAD its BUFFER, instead of fusing it:
```py
child = out+2
child.kernelize()
print(child.uop.src[1].arg.ast)
```
```
UOp(Ops.SINK, dtypes.void, arg=None, src=(
UOp(Ops.STORE, dtypes.void, arg=None, src=(
UOp(Ops.DEFINE_GLOBAL, dtypes.int.ptr(1), arg=0, src=()),
x2:=UOp(Ops.VIEW, dtypes.void, arg=ShapeTracker(views=(View(shape=(1,), strides=(0,), offset=0, mask=None, contiguous=True),)), src=()),
UOp(Ops.ADD, dtypes.int, arg=None, src=(
UOp(Ops.LOAD, dtypes.int, arg=None, src=(
UOp(Ops.DEFINE_GLOBAL, dtypes.int.ptr(1), arg=1, src=()),
x2,)),
UOp(Ops.CONST, dtypes.int, arg=2, src=(
x2,)),)),)),))
```
`Tensor.realize` will execute the kernels and write outputs to memory:
```py
Tensor.realize(out)
print(out) # <Tensor <UOp METAL (1,) int (<Ops.BUFFER: 23>, <buf real:True device:METAL size:1 dtype:dtypes.int offset:0>)> on METAL with grad None>
print(out.item()) # 5
```
<hr />
**Summary**
- The large Tensor graph is built from a mix of data, compute and movement Ops.
- `Tensor.kernelize` splits the Tensor graph into data (BUFFER), compute (KERNEL) and links dependencies with ASSIGN.
- `Tensor.realize` executes KERNELs on device and replaces the Tensor graph with just a BUFFER.
- Kernelize can be called multiple times on a Tensor. This allows for incrementally building the kernel fusion layout of a large Tensor graph, without having to call `realize` or `schedule`.
+1 -1
View File
@@ -41,7 +41,7 @@ BEAM | [#] | number of beams in kernel beam search
DEFAULT_FLOAT | [HALF, ...]| specify the default float dtype (FLOAT32, HALF, BFLOAT16, FLOAT64, ...), default to FLOAT32
IMAGE | [1-2] | enable 2d specific optimizations
FLOAT16 | [1] | use float16 for images instead of float32
HCQ_VISIBLE_DEVICES | [list[int]]| restricts the HCQ devices that are available. The format is a comma-separated list of identifiers (indexing starts with 0).
VISIBLE_DEVICES | [list[int]]| restricts the NV/AMD devices that are available. The format is a comma-separated list of identifiers (indexing starts with 0).
JIT | [0-2] | 0=disabled, 1=[jit enabled](quickstart.md#jit) (default), 2=jit enabled, but graphs are disabled
VIZ | [1] | 0=disabled, 1=[viz enabled](https://github.com/tinygrad/tinygrad/tree/master/tinygrad/viz)
ALLOW_TF32 | [1] | enable TensorFloat-32 tensor cores on Ampere or newer GPUs.
+1 -1
View File
@@ -232,7 +232,7 @@ if __name__ == "__main__":
gpt2 = GPT2.build_gguf(args.model_size) if args.model_size.startswith("gpt2_gguf_") else GPT2.build(args.model_size)
if args.benchmark != -1:
gpt2.model(Tensor.randint(args.batch_size, args.benchmark), Variable("a", 0, MAX_CONTEXT).bind(0)).realize()
gpt2.model(Tensor.rand(args.batch_size, args.benchmark), Variable("a", 0, MAX_CONTEXT).bind(0)).realize()
else:
texts = gpt2.generate(args.prompt, args.count, args.temperature, timing=args.timing, batch_size=args.batch_size)
if not args.noshow:
+1 -37
View File
@@ -145,41 +145,6 @@ def NF4Linear(block_size):
return new_state_dict
return _NF4Linear
def quantize_to_fp8(x: Tensor, dtype=dtypes.fp8e4m3):
fp8_min = -448.0 if dtype == dtypes.fp8e4m3 else -57344.0
fp8_max = 448.0 if dtype == dtypes.fp8e4m3 else 57344.0
scale = fp8_max / x.abs().max()
x_scl_sat = (x * scale).clamp(fp8_min, fp8_max)
return x_scl_sat.cast(dtype), scale.float().reciprocal()
class FP8Linear:
def __init__(self, in_features, out_features, bias=True):
self.weight = Tensor.empty(out_features, in_features, dtype=dtypes.fp8e4m3)
self.bias = Tensor.empty(out_features, dtype=dtypes.float16) if bias else None
self.weight_scale = Tensor.empty((), dtype=dtypes.float16)
def __call__(self, x:Tensor):
y = x.dot(self.weight.T.cast(dtypes.float32)) * self.weight_scale
if self.bias is not None: y = y + self.bias.cast(y.dtype)
return y.cast(x.dtype)
@staticmethod
def quantize(tensors, device, scale_dtype=dtypes.float16, quantize_embeds=False):
assert not quantize_embeds
new_tensors = {}
for name,v in tensors.items():
if "feed_forward" in name or "attention.w" in name:
assert "weight" in name, name
fp8_weight, scale = quantize_to_fp8(v)
new_tensors[name] = fp8_weight
new_tensors[name.replace('weight', 'weight_scale')] = scale.cast(scale_dtype)
if isinstance(device, tuple):
new_tensors[name].shard_(device, axis=-1)
new_tensors[name.replace('weight', 'weight_scale')].shard_(device, axis=None)
else:
new_tensors[name] = v
return new_tensors
MODEL_PARAMS = {
"1B": {
"args": {"dim": 2048, "n_heads": 32, "n_kv_heads": 8, "n_layers": 16, "norm_eps": 1e-5, "rope_theta": 500000, "vocab_size": 128256, "hidden_dim": 8192},
@@ -202,7 +167,6 @@ def build_transformer(model_path: Path, model_size="8B", quantize=None, scale_dt
# build model
if quantize == "int8": linear, embedding, quantize_embeds = Int8Linear, Int8Embedding, True
elif quantize == "nf4": linear, embedding, quantize_embeds = NF4Linear(64), nn.Embedding, False
elif quantize == "fp8": linear, embedding, quantize_embeds = FP8Linear, nn.Embedding, False
else: linear, embedding, quantize_embeds = nn.Linear, nn.Embedding, False
model = Transformer(**MODEL_PARAMS[model_size]["args"], linear=linear, embedding=embedding, max_context=max_context, jit=True)
@@ -278,7 +242,7 @@ if __name__ == "__main__":
parser.add_argument("--model", type=Path, help="Model path")
parser.add_argument("--size", choices=["1B", "8B", "70B", "405B"], default="1B", help="Model size")
parser.add_argument("--shard", type=int, default=1, help="Shard the model across multiple devices")
parser.add_argument("--quantize", choices=["int8", "nf4", "float16", "fp8"], help="Quantization method")
parser.add_argument("--quantize", choices=["int8", "nf4", "float16"], help="Quantization method")
parser.add_argument("--no_api", action="store_true", help="Disable the api and run a cli test interface")
parser.add_argument("--host", type=str, default="0.0.0.0", help="Web server bind address")
parser.add_argument("--port", type=int, default=7776, help="Web server port")
@@ -2,7 +2,7 @@
export PYTHONPATH="." NV=1
export MODEL="bert"
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=96 EVAL_BS=96
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=90 EVAL_BS=90
export IGNORE_OOB=1
export REWRITE_STACK_LIMIT=500000
@@ -2,7 +2,7 @@
export PYTHONPATH="." NV=1
export MODEL="bert"
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=96 EVAL_BS=96
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=90 EVAL_BS=90
export IGNORE_OOB=1
export REWRITE_STACK_LIMIT=500000
@@ -5,7 +5,7 @@ set -o pipefail # Make pipeline fail if any command fails
export PYTHONPATH="." NV=1
export MODEL="bert"
export SUBMISSION_PLATFORM="tinybox_green"
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=96 EVAL_BS=96
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=90 EVAL_BS=90
export IGNORE_OOB=1
export REWRITE_STACK_LIMIT=500000
@@ -2,7 +2,7 @@
export PYTHONPATH="." AMD=1
export MODEL="bert"
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=96 EVAL_BS=96
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=90 EVAL_BS=90
export IGNORE_OOB=1
export REWRITE_STACK_LIMIT=500000
@@ -2,7 +2,7 @@
export PYTHONPATH="." AMD=1
export MODEL="bert"
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=96 EVAL_BS=96
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=90 EVAL_BS=90
export IGNORE_OOB=1
export REWRITE_STACK_LIMIT=500000
@@ -5,7 +5,7 @@ set -o pipefail # Make pipeline fail if any command fails
export PYTHONPATH="." AMD=1
export MODEL="bert"
export SUBMISSION_PLATFORM="tinybox_red"
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=96 EVAL_BS=96
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=90 EVAL_BS=90
export IGNORE_OOB=1
export REWRITE_STACK_LIMIT=500000
@@ -15,7 +15,7 @@ export IGNORE_JIT_FIRST_BEAM=1
export BASEDIR="/raid/datasets/wiki"
# pip install -e ".[mlperf]"
export LOGMLPERF=${LOGMLPERF:-1}
export LOGMLPERF=1
export SEED=$RANDOM
DATETIME=$(date "+%m%d%H%M")
+65 -45
View File
@@ -1,9 +1,15 @@
import os, sys, pickle, time, re
import numpy as np
if "FLOAT16" not in os.environ: os.environ["FLOAT16"] = "1"
if "IMAGE" not in os.environ: os.environ["IMAGE"] = "2"
if "NOLOCALS" not in os.environ: os.environ["NOLOCALS"] = "1"
if "JIT_BATCH_SIZE" not in os.environ: os.environ["JIT_BATCH_SIZE"] = "0"
from tinygrad import fetch, Tensor, TinyJit, Context, GlobalCounters, Device, dtypes
from tinygrad.helpers import DEBUG, getenv
from tinygrad.engine.realize import CompiledRunner
import onnx
from tinygrad.nn.onnx import OnnxRunner
OPENPILOT_MODEL = sys.argv[1] if len(sys.argv) > 1 else "https://github.com/commaai/openpilot/raw/v0.9.7/selfdrive/modeld/models/supercombo.onnx"
@@ -15,14 +21,11 @@ def compile(onnx_file):
input_shapes = {name: spec.shape for name, spec in run_onnx.graph_inputs.items()}
input_types = {name: spec.dtype for name, spec in run_onnx.graph_inputs.items()}
# Float inputs and outputs to tinyjits for openpilot are always float32
# TODO this seems dumb
input_types = {k:(dtypes.float32 if v is dtypes.float16 else v) for k,v in input_types.items()}
Tensor.manual_seed(100)
inputs = {k:Tensor(Tensor.randn(*shp, dtype=input_types[k]).mul(8).realize().numpy(), device='NPY') for k,shp in sorted(input_shapes.items())}
if not getenv("NPY_IMG"):
inputs = {k:Tensor(v.numpy(), device=Device.DEFAULT).realize() if 'img' in k else v for k,v in inputs.items()}
new_inputs = {k:Tensor.randn(*shp, dtype=input_types[k]).mul(8).realize() for k,shp in sorted(input_shapes.items())}
new_inputs_numpy = {k:v.numpy() for k,v in new_inputs.items()}
print("created tensors")
run_onnx_jit = TinyJit(lambda **kwargs:
@@ -30,6 +33,8 @@ def compile(onnx_file):
for i in range(3):
GlobalCounters.reset()
print(f"run {i}")
inputs = {**{k:v.clone() for k,v in new_inputs.items() if 'img' in k},
**{k:Tensor(v, device="NPY").realize() for k,v in new_inputs_numpy.items() if 'img' not in k}}
with Context(DEBUG=max(DEBUG.value, 2 if i == 2 else 1)):
ret = run_onnx_jit(**inputs).numpy()
# copy i == 1 so use of JITBEAM is okay
@@ -38,7 +43,7 @@ def compile(onnx_file):
np.testing.assert_equal(test_val, ret, "JIT run failed")
print("jit run validated")
# check gated read_image usage
# checks from compile2
kernel_count = 0
read_image_count = 0
gated_read_image_count = 0
@@ -64,9 +69,14 @@ def compile(onnx_file):
print(f"mdl size is {mdl_sz/1e6:.2f}M")
print(f"pkl size is {pkl_sz/1e6:.2f}M")
print("**** compile done ****")
return inputs, test_val
return test_val
def test_vs_compile(run, inputs, test_val=None):
def test_vs_compile(run, new_inputs, test_val=None):
new_inputs_numpy = {k:v.numpy() for k,v in new_inputs.items()}
# create fake "from_blob" tensors for the inputs, and wrapped NPY tensors for the numpy inputs (these have the same underlying memory)
inputs = {**{k:v for k,v in new_inputs.items() if 'img' in k},
**{k:Tensor(v, device="NPY").realize() for k,v in new_inputs_numpy.items() if 'img' not in k}}
# run 20 times
step_times = []
@@ -83,58 +93,68 @@ def test_vs_compile(run, inputs, test_val=None):
min_time = min(step_times)
assert min_time < assert_time, f"Speed regression, expected min step time of < {assert_time} ms but took: {min_time} ms"
print(out, val.shape, val.dtype)
if test_val is not None: np.testing.assert_equal(test_val, val)
print("**** test done ****")
# test that changing the numpy changes the model outputs
inputs_2x = {k: Tensor(v.numpy()*2, device=v.device) for k,v in inputs.items()}
out = run(**inputs_2x)
changed_val = out.numpy()
np.testing.assert_raises(AssertionError, np.testing.assert_array_equal, val, changed_val)
if any([x.device == 'NPY' for x in inputs.values()]):
for v in new_inputs_numpy.values(): v *= 2
out = run(**inputs)
changed_val = out.numpy()
np.testing.assert_raises(AssertionError, np.testing.assert_array_equal, val, changed_val)
return val
def test_vs_onnx(new_inputs, test_val, onnx_file, tol):
import onnx
import onnxruntime as ort
onnx_inputs = {k:v.numpy() for k,v in new_inputs.items()}
def test_vs_onnx(new_inputs, test_val, onnx_file, ort=False):
new_inputs_numpy = {k:v.numpy() for k,v in new_inputs.items()}
onnx_model = onnx.load(onnx_file)
ORT_TO_NP_DTYPES: dict[str, np.dtype] = {
'tensor(float)': np.dtype('float32'),
'tensor(float16)': np.dtype('float16'),
'tensor(uint8)': np.dtype('uint8'),
}
timings = []
onnx_session = ort.InferenceSession(onnx_file)
onnx_types = {x.name: ORT_TO_NP_DTYPES[x.type] for x in onnx_session.get_inputs()}
onnx_inputs = {k:onnx_inputs[k].astype(onnx_types[k]) for k in onnx_inputs}
if ort:
# test with onnxruntime
import onnxruntime as ort
onnx_session = ort.InferenceSession(onnx_file)
for _ in range(1 if test_val is not None else 5):
st = time.perf_counter()
onnx_output = onnx_session.run([onnx_model.graph.output[0].name], {k:v.astype(np.float16) for k,v in new_inputs_numpy.items()})
timings.append(time.perf_counter() - st)
new_torch_out = onnx_output[0]
else:
# test with torch
import torch
from onnx2torch import convert
inputs = {k.name:new_inputs_numpy[k.name] for k in onnx_model.graph.input}
torch_model = convert(onnx_model).float()
with torch.no_grad():
for _ in range(1 if test_val is not None else 5):
st = time.perf_counter()
torch_out = torch_model(*[torch.tensor(x) for x in inputs.values()])
timings.append(time.perf_counter() - st)
new_torch_out = torch_out.numpy()
for _ in range(1 if test_val is not None else 5):
st = time.perf_counter()
onnx_output = onnx_session.run([onnx_model.graph.output[0].name], onnx_inputs)
timings.append(time.perf_counter() - st)
np.testing.assert_allclose(onnx_output[0].reshape(test_val.shape), test_val, atol=tol, rtol=tol)
print("test vs onnx passed")
if test_val is not None:
np.testing.assert_allclose(new_torch_out.reshape(test_val.shape), test_val, atol=1e-4, rtol=1e-2)
print("test vs onnx passed")
return timings
def bench(run, inputs):
from extra.bench_log import WallTimeEvent, BenchEvent
for _ in range(10):
with WallTimeEvent(BenchEvent.STEP):
run(**inputs).numpy()
if __name__ == "__main__":
onnx_file = fetch(OPENPILOT_MODEL)
inputs, outputs = compile(onnx_file)
test_val = compile(onnx_file) if not getenv("RUN") else None
with open(OUTPUT, "rb") as f: pickle_loaded = pickle.load(f)
test_vs_compile(pickle_loaded, inputs, outputs)
if getenv("SELFTEST"):
test_vs_onnx(inputs, outputs, onnx_file, 1e-4)
# same randomness as compile
Tensor.manual_seed(100)
new_inputs = {nm:Tensor.randn(*st.shape, dtype=dtype).mul(8).realize() for nm, (st, _, dtype, _) in
sorted(zip(pickle_loaded.captured.expected_names, pickle_loaded.captured.expected_st_vars_dtype_device))}
test_val = test_vs_compile(pickle_loaded, new_inputs, test_val)
if getenv("BENCHMARK"):
for be in ["torch", "ort"]:
try:
timings = test_vs_onnx(new_inputs, None, onnx_file, be=="ort")
print(f"timing {be}: {min(timings)*1000:.2f} ms")
except Exception as e:
print(f"{be} fail with {e}")
if not getenv("FLOAT16"): test_vs_onnx(new_inputs, test_val, onnx_file, getenv("ORT"))
if getenv("BENCHMARK_LOG", ""):
bench(pickle_loaded, inputs)
+47
View File
@@ -0,0 +1,47 @@
import sys
from tinygrad import Tensor, fetch, GlobalCounters, dtypes
from tinygrad.uop.ops import UOp
from tinygrad.nn.onnx import OnnxRunner
from tinygrad.schedule.rangeify import get_rangeify_map
from tinygrad.engine.schedule import create_schedule_with_vars
from tinygrad.engine.realize import run_schedule
# NOLOCALS=1 CL=1 IMAGE=2 FLOAT16=1 VIZ=1 DEBUG=2 python3 examples/openpilot/compile4.py
OPENPILOT_MODEL = sys.argv[1] if len(sys.argv) > 1 else "https://github.com/commaai/openpilot/raw/v0.9.7/selfdrive/modeld/models/supercombo.onnx"
OUTPUT = sys.argv[2] if len(sys.argv) > 2 else "/tmp/openpilot.pkl"
if __name__ == "__main__":
onnx_file = fetch(OPENPILOT_MODEL)
run_onnx = OnnxRunner(onnx_file)
inputs = run_onnx.get_empty_input_data("npy", dtypes.float32)
out: Tensor = next(iter(run_onnx({k:v.to(None) for k,v in inputs.items()}).values())).to('cpu')
root = out.uop
targets = [x.uop for x in inputs.values()]
print(targets)
# TODO: abstract this from gradient?
# compute the target path (top down)
in_target_path: dict[UOp, bool] = {}
for u in root.toposort(): in_target_path[u] = any(x in targets or in_target_path[x] for x in u.src)
independent_set = {}
for u in root.toposort():
if in_target_path[u]:
for s in u.src:
if not in_target_path[s]:
independent_set[s] = None
independent = UOp.sink(*independent_set.keys())
kernelized = get_rangeify_map(independent)
independent = independent.substitute(kernelized)
schedule, var_vals = create_schedule_with_vars(independent)
run_schedule(schedule)
print("**** real ****")
GlobalCounters.reset()
out.uop = root.substitute(kernelized)
out.kernelize()
# realize
out.realize()
+8 -12
View File
@@ -99,7 +99,6 @@ if __name__ == "__main__":
parser.add_argument('--timing', action='store_true', help="Print timing per step")
parser.add_argument('--noshow', action='store_true', help="Don't show the image")
parser.add_argument('--fp16', action='store_true', help="Cast the weights to float16")
parser.add_argument('--fakeweights', action='store_true', help="Skip loading checkpoints and use fake weights")
args = parser.parse_args()
N = 1
@@ -113,22 +112,19 @@ if __name__ == "__main__":
model = StableDiffusionV2(**params)
with WallTimeEvent(BenchEvent.LOAD_WEIGHTS):
if not args.fakeweights:
default_weights_url = 'https://huggingface.co/stabilityai/stable-diffusion-2-1/resolve/main/v2-1_768-ema-pruned.safetensors'
weights_fn = args.weights_fn
if not weights_fn:
weights_url = args.weights_url if args.weights_url else default_weights_url
weights_fn = fetch(weights_url, os.path.basename(str(weights_url)))
default_weights_url = 'https://huggingface.co/stabilityai/stable-diffusion-2-1/resolve/main/v2-1_768-ema-pruned.safetensors'
weights_fn = args.weights_fn
if not weights_fn:
weights_url = args.weights_url if args.weights_url else default_weights_url
weights_fn = fetch(weights_url, os.path.basename(str(weights_url)))
load_state_dict(model, safe_load(weights_fn), strict=False)
with WallTimeEvent(BenchEvent.LOAD_WEIGHTS):
load_state_dict(model, safe_load(weights_fn), strict=False)
if args.fp16:
for k,v in get_state_dict(model).items():
if k.startswith("model"):
v.replace(v.cast(dtypes.float16))
Tensor.realize(*get_state_dict(model).values())
v.replace(v.cast(dtypes.float16).realize())
c = { "crossattn": model.cond_stage_model(args.prompt) }
uc = { "crossattn": model.cond_stage_model("") }
+1 -4
View File
@@ -263,16 +263,13 @@ if __name__ == "__main__":
parser.add_argument('--timing', action='store_true', help="Print timing per step")
parser.add_argument('--seed', type=int, help="Set the random latent seed")
parser.add_argument('--guidance', type=float, default=7.5, help="Prompt strength")
parser.add_argument('--fakeweights', action='store_true', help="Skip loading checkpoints and use fake weights")
args = parser.parse_args()
model = StableDiffusion()
# load in weights
with WallTimeEvent(BenchEvent.LOAD_WEIGHTS):
if not args.fakeweights:
model_bin = fetch('https://huggingface.co/CompVis/stable-diffusion-v-1-4-original/resolve/main/sd-v1-4.ckpt', 'sd-v1-4.ckpt')
load_state_dict(model, torch_load(model_bin)['state_dict'], verbose=False, strict=False, realize=False)
load_state_dict(model, torch_load(fetch('https://huggingface.co/CompVis/stable-diffusion-v-1-4-original/resolve/main/sd-v1-4.ckpt', 'sd-v1-4.ckpt'))['state_dict'], verbose=False, strict=False, realize=False)
if args.fp16:
for k,v in get_state_dict(model).items():
+2 -2
View File
@@ -19,8 +19,8 @@ from tinygrad.helpers import fetch, getenv
# QUANT=1 python3 examples/test_onnx_imagenet.py
# https://github.com/xamcat/mobcat-samples/raw/refs/heads/master/onnx_runtime/InferencingSample/InferencingSample/mobilenetv2-7.onnx
# python3 examples/test_onnx_imagenet.py /tmp/model.quant.onnx
# VIZ=1 python3 examples/benchmark_onnx.py /tmp/model.quant.onnx
# DONT_REALIZE_EXPAND=1 python3 examples/test_onnx_imagenet.py /tmp/model.quant.onnx
# VIZ=1 DONT_REALIZE_EXPAND=1 python3 examples/benchmark_onnx.py /tmp/model.quant.onnx
def imagenet_dataloader(cnt=0):
input_mean = Tensor([0.485, 0.456, 0.406]).reshape(1, -1, 1, 1)
+4 -5
View File
@@ -3,7 +3,7 @@
import sys, base64, multiprocessing, itertools, collections
from typing import Optional, Union, Literal, List
from tinygrad import Tensor, TinyJit, Variable, nn, dtypes
from tinygrad import Tensor, TinyJit, Variable, nn
from tinygrad.nn.state import torch_load, load_state_dict
from tinygrad.helpers import getenv, fetch
@@ -244,16 +244,15 @@ def transcribe_waveform(model: Whisper, enc, waveforms, truncate=False):
log_spec = prep_audio(waveforms, model.batch_size, truncate)
nsample = model.decoder.max_tokens_to_sample
nctx = model.decoder.max_self_attn_cache_len
def inferloop(ctx: Union[np.ndarray, List[np.ndarray]], encoded_audio):
pos, next_tokens = 0, ctx
for i in range(nsample):
next_tokens = model.decoder(Tensor(next_tokens, dtype=dtypes.int32), pos, encoded_audio)[:, -1].argmax(axis=-1).numpy().astype(np.int32).reshape(-1, 1)
for i in range((nsample-len(start_tokens))*2):
next_tokens = model.decoder(Tensor(next_tokens), pos, encoded_audio)[:, -1].argmax(axis=-1).numpy().astype(np.int32).reshape(-1, 1)
next_tokens[ctx[:, -1] == eot] = eot
ctx = np.concatenate((ctx, next_tokens), axis=1)
pos = ctx.shape[-1] - 1
if (next_tokens == eot).all() or pos == nctx: break
if (next_tokens == eot).all(): break
return ctx
def gettexttoks(line): return [tok for tok in line if tok < eot or tok > enc._special_tokens["<|notimestamps|>"]][-nsample+len(start_tokens):]
-4
View File
@@ -1,4 +0,0 @@
# source extra/cl_android.sh
export LD_LIBRARY_PATH=/data/data/com.termux/files/usr/lib:/system/vendor/lib64
export LD_PRELOAD=/system/vendor/lib64/libOpenCL.so
+316 -130
View File
@@ -1,168 +1,354 @@
from tinygrad import Tensor, Device, Context, GlobalCounters, dtypes
from tinygrad.uop.ops import UOp, KernelInfo, sint, AxisType
from tinygrad.engine.realize import ExecItem, get_runner
from tinygrad.uop.ops import UOp, Ops, KernelInfo, graph_rewrite, AxisType, PatternMatcher, UPat
from tinygrad.engine.realize import CompiledRunner, ExecItem, get_program
from tinygrad.dtype import AddrSpace
from tinygrad.helpers import getenv
from tinygrad.helpers import getenv, colored, prod, unwrap
from tinygrad.shape.shapetracker import ShapeTracker, View
from tinygrad.shape.view import strides_for_shape
from tinygrad.codegen.opt.kernel import axis_colors, Opt, OptOps
from tinygrad.codegen.opt.swizzler import merge_views, view_left
def to_colored(full_shape, axis_types): return '_'.join([colored(str(s), axis_colors[at]) for s,at in zip(full_shape, axis_types)])
N = 4096
M = K = N
run_count = 5
# ---------------------------
# launch/config constants
# ---------------------------
BN = 128
BM = 128
BK = 8
WARP_SIZE = 32
TN = 4
TM = 4
# Threadblock tile sizes (block-level tile of C that a block computes)
BLOCK_N = 128 # columns of C (N-dim) per block
BLOCK_M = 128 # rows of C (M-dim) per block
BLOCK_K = 8 # K-slice per block iteration
# NOTE: this is from testgrad
# change reduceop axes and input ShapeTrackers, view gets replaced with a reshape.
# src->r->view --> src->view->r
def swizzle_reduceop(src:UOp, r:UOp, view:UOp):
if r.tag is not None: return None
# confirm the input is in order
# TODO: replace this with a UOp that allows for nothing else then remove this
permute = tuple(i for i in range(len(src.shape)) if i not in r.axis_arg)+r.axis_arg
assert permute == tuple(range(len(permute))), f"reduce axis must already be in order, {permute} isn't"
# Register tile sizes (per-thread accumulator tile of C)
TN = 4 # columns per thread
TM = 4 # rows per thread
# append the reduce shape to each of the views
prshape = prod(rshape:=src.shape[-len(r.axis_arg):])
rstrides = strides_for_shape(rshape)
nv = [View.create(v.shape+rshape, tuple(x*prshape for x in v.strides)+rstrides, v.offset*prshape,
v.mask+tuple((0,s) for s in rshape) if v.mask is not None else None) for v in unwrap(view.st).views]
is_kernel5 = getenv("K5", 0)
THREADS_PER_BLOCK = 128 if is_kernel5 else 256
assert THREADS_PER_BLOCK % BLOCK_N == 0, "THREADS_PER_BLOCK must be divisible by BLOCK_N"
assert THREADS_PER_BLOCK % BLOCK_K == 0, "THREADS_PER_BLOCK must be divisible by BLOCK_K"
assert (BLOCK_N * BLOCK_K) % THREADS_PER_BLOCK == 0
assert (BLOCK_M * BLOCK_K) % THREADS_PER_BLOCK == 0
# no reshape required with shrinking REDUCE_AXIS
return UOp(Ops.REDUCE_AXIS, r.dtype, (src.view(ShapeTracker(tuple(nv))),),
(r.arg[0], tuple(range(len(view.shape), len(view.shape) + len(r.axis_arg)))))
WARPS_PER_BLOCK = THREADS_PER_BLOCK // WARP_SIZE
WAVE_TILE_N = 128 if is_kernel5 else 64
WAVE_TILE_M = BLOCK_N * BLOCK_M // WARPS_PER_BLOCK // WAVE_TILE_N
assert BLOCK_N % WAVE_TILE_N == 0, "BN must be a multiple of WN"
assert BLOCK_M % WAVE_TILE_M == 0, "BM must be a multiple of WM"
WAVES_IN_BLOCK_X = BLOCK_N // WAVE_TILE_N
WAVES_IN_BLOCK_Y = BLOCK_M // WAVE_TILE_M
assert WAVES_IN_BLOCK_X * WAVES_IN_BLOCK_Y == WARPS_PER_BLOCK, "wave grid must match warps/block"
pm = PatternMatcher([
(UPat(Ops.VIEW, src=(UPat(Ops.REDUCE_AXIS, src=(UPat.var("src"),), name="r"),), name="view"), swizzle_reduceop),
])
LANES_PER_WAVE_X = 8
LANES_PER_WAVE_Y = 4
ITERS_PER_WAVE_N = WAVE_TILE_N // (LANES_PER_WAVE_X * TN)
ITERS_PER_WAVE_M = WAVE_TILE_M // (LANES_PER_WAVE_Y * TM)
assert WAVE_TILE_N % (LANES_PER_WAVE_X * TN) == 0, "WAVE_TILE_N must be divisible by LANES_PER_WAVE_X*TN"
assert WAVE_TILE_M % (LANES_PER_WAVE_Y * TM) == 0, "WAVE_TILE_M must be divisible by LANES_PER_WAVE_Y*TM"
def rangeify_kernel3():
a = Tensor.empty(N,N)
b = Tensor.empty(N,N)
c = a@b
#c = c.reshape((32,2,16,4,32,2,16,4)).contiguous()
sink = c.schedule()[-1].ast
#print(sink)
def rngs_for_shape(shape:tuple[sint, ...], rng:int, axis_type=AxisType.LOOP): return [UOp.range(s, rng+i, axis_type) for i,s in enumerate(shape)]
def copy(dest:UOp, src:UOp, rng:int, set=False, upcast=False):
assert dest.shape == src.shape
rngs = rngs_for_shape(src.shape, rng, AxisType.UPCAST if upcast else AxisType.LOOP)
copy = dest[*rngs].store(src[*rngs]).end(*rngs)
return dest.after(copy) if set else copy
opts = [Opt(OptOps.UPCAST, 0, 4), Opt(OptOps.LOCAL, 0, 16), Opt(OptOps.UPCAST, 0, 2)]
opts += [Opt(OptOps.UPCAST, 1, 4), Opt(OptOps.LOCAL, 1, 16), Opt(OptOps.UPCAST, 1, 2)]
opts += [Opt(OptOps.UNROLL, 0, 8)]
def hand_spec_kernel3():
# ---------------------------
# block indices & placeholders
# ---------------------------
blockIdx_x = UOp.special(N // BLOCK_N, "gidx0")
blockIdx_y = UOp.special(N // BLOCK_M, "gidx1")
return sink.replace(arg=KernelInfo(opts_to_apply=tuple(opts)))
a = UOp.placeholder((N, N), dtypes.float, slot=1)
b = UOp.placeholder((N, N), dtypes.float, slot=2)
c = UOp.placeholder((N, N), dtypes.float, slot=0)
def top_spec_kernel3():
a = Tensor.empty(N,N)
b = Tensor.empty(N,N)
c = a@b
sink = c.schedule()[-1].ast
L = 16
sink = sink.reshape((N//L, L, N//L, L)) #.lift({0:UOp.range(N//BM, 0), 2:UOp.range(N//BN, 1)})
sink = graph_rewrite(sink, view_left+pm)
axis_types = (AxisType.GLOBAL, AxisType.LOCAL, AxisType.GLOBAL, AxisType.LOCAL, AxisType.REDUCE)
return sink.replace(arg=KernelInfo(name="top_"+to_colored(sink.full_shape, axis_types), axis_types=axis_types))
# index the output with the globals
c = c.reshape(M // BLOCK_M, BLOCK_M, N // BLOCK_N, BLOCK_N)[blockIdx_y, :, blockIdx_x, :]
def hl_spec_kernel3():
nbIterWaveM = 2
nbIterWaveN = 2
# open the main reduction range
k_tile_range = UOp.range(N // BLOCK_K, 0, AxisType.REDUCE)
a = a.reshape(M // BLOCK_M, BLOCK_M, N // BLOCK_K, BLOCK_K)[blockIdx_y, :, k_tile_range, :]
b = b.reshape(N // BLOCK_K, BLOCK_K, N // BLOCK_N, BLOCK_N)[k_tile_range, :, blockIdx_x, :]
# define buffers
# TODO: remove these views once the defines have a shape
a = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(N*N), arg=1).view(ShapeTracker.from_shape((N,N)))
b = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(N*N), arg=2).view(ShapeTracker.from_shape((N,N))).permute((1,0))
c = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(N*N), arg=0).view(ShapeTracker.from_shape((N,N)))
As = UOp(Ops.DEFINE_LOCAL, dtypes.float.ptr(BK*BM, AddrSpace.LOCAL), arg=0).view(ShapeTracker.from_shape((BK, BM))).permute((1,0))
Bs = UOp(Ops.DEFINE_LOCAL, dtypes.float.ptr(BK*BN, AddrSpace.LOCAL), arg=1).view(ShapeTracker.from_shape((BK, BN))).permute((1,0))
A_col = UOp(Ops.DEFINE_REG, dtypes.float.ptr(nbIterWaveM * TM, AddrSpace.REG), arg=0).view(ShapeTracker.from_shape((nbIterWaveM * TM,)))
B_row = UOp(Ops.DEFINE_REG, dtypes.float.ptr(nbIterWaveN * TN, AddrSpace.REG), arg=1).view(ShapeTracker.from_shape((nbIterWaveN * TN,)))
# globals are no longer used, they are already in the indexes
del blockIdx_y, blockIdx_x
# shape buffers. TODO: permutes
full_shape = (N//BM, nbIterWaveM, BM//(nbIterWaveM * TM), TM, N//BN, nbIterWaveN, BN//(nbIterWaveN * TN), TN, N//BK, BK)
a = a.reshape((N//BM, nbIterWaveM, BM//(nbIterWaveM * TM), TM, 1, 1, 1, 1, N//BK, BK)).expand(full_shape)
b = b.reshape((1, 1, 1, 1, N//BN, nbIterWaveN, BN//(nbIterWaveN * TN), TN, N//BK, BK)).expand(full_shape)
c = c.reshape((N//BM, nbIterWaveM, BM//(nbIterWaveM * TM), TM, N//BN, nbIterWaveN, BN//(nbIterWaveN * TN), TN, 1, 1))
As = As.reshape((1, nbIterWaveM, BM//(nbIterWaveM * TM), TM, 1, 1, 1, 1, 1, BK)).expand(full_shape)
Bs = Bs.reshape((1, 1, 1, 1, 1, nbIterWaveN, BN//(nbIterWaveN * TN), TN, 1, BK)).expand(full_shape)
A_col = A_col.reshape((1, nbIterWaveM, 1, TM, 1, 1, 1, 1, 1, 1)).expand(full_shape)
B_row = B_row.reshape((1, 1, 1, 1, 1, nbIterWaveN, 1, TN, 1, 1)).expand(full_shape)
# ---------------------------
# GLOBAL -> LOCAL (As, Bs)
# ---------------------------
tid = UOp.special(THREADS_PER_BLOCK, "lidx0")
# U1 L2 L3 L4 L5 U6 U7 U9 L10 L11 L12 L13 U14 U15 U17 U18 U19
expanded_shape = (32, 2, 2, 2, 2, 2, 2, 2, 32, 2, 2, 2, 2, 2, 2, 2, 512, 2, 2, 2)
assert len(expanded_shape) == 20
permute_a = list(range(len(expanded_shape)))
permute_b = permute_a[:]
# A: read BM x BK tiles (permute on store into locals)
BM_As_stride = (BLOCK_M + 4) if is_kernel5 else BLOCK_M
As = UOp.placeholder((BLOCK_K, BM_As_stride), dtypes.float, slot=0, addrspace=AddrSpace.LOCAL).shrink_to((BLOCK_K, BLOCK_M))
As_store = copy(As.permute((1,0)).reshape(-1, THREADS_PER_BLOCK)[:, tid], a.reshape(-1, THREADS_PER_BLOCK)[:, tid], rng=100)
# this makes all the global loads match
# this can also be more simply done by rebinding the RANGEs
# but sadly, rebinding the RANGEs doesn't work to change the order of the local axes
permute_a[17:20] = [11,12,13]
permute_a[11:14] = [17,18,19]
permute_a[7], permute_a[10] = permute_a[10], permute_a[7]
permute_a[2:7] = [3,4,5,6,2]
# B: read BK x BN tiles
Bs = UOp.placeholder((BLOCK_K, BLOCK_N), dtypes.float, slot=1, addrspace=AddrSpace.LOCAL)
Bs_store = copy(Bs.reshape(-1, THREADS_PER_BLOCK)[:, tid], b.reshape(-1, THREADS_PER_BLOCK)[:, tid], rng=200)
permute_b[2:16] = [19,9,10,11,17,18,8,2,12,13,14,15,3,4]
permute_b[17:20] = [5,6,7]
# TODO: can we automate barrier?
barrier = UOp.barrier(As_store, Bs_store)
As, Bs = As.after(barrier), Bs.after(barrier)
a_permute = a.reshape(expanded_shape).permute(tuple(permute_a)).reshape(full_shape)
As_permute = As.reshape(expanded_shape).permute(tuple(permute_a)).reshape(full_shape)
# open inner k range
k = UOp.range(BLOCK_K, 3, AxisType.REDUCE)
b_permute = b.reshape(expanded_shape).permute(tuple(permute_b)).reshape(full_shape)
Bs_permute = Bs.reshape(expanded_shape).permute(tuple(permute_b)).reshape(full_shape)
# ---------------------------
# LOCAL -> REG (per-wave tiles)
# ---------------------------
waveIdx = (tid // WARP_SIZE) % WAVES_IN_BLOCK_X
waveIdy = (tid // WARP_SIZE) // WAVES_IN_BLOCK_X
assert waveIdy.vmax+1 == WAVES_IN_BLOCK_Y
#out = (a.load() * b.load()).r(Ops.ADD, (8, 9))
out = (As.load(As_permute.store(a_permute.load())) * Bs.load(Bs_permute.store(b_permute.load()))).r(Ops.ADD, (8, 9))
#out = (A_col.load(A_col.store(As.load(As.store(a.load())))) * B_row.load(B_row.store(Bs.load(Bs.store(b.load()))))).r(Ops.ADD, (8, 9))
laneIdx = (tid % WARP_SIZE) % LANES_PER_WAVE_X
laneIdy = (tid % WARP_SIZE) // LANES_PER_WAVE_X
assert laneIdy.vmax+1 == LANES_PER_WAVE_Y
axis_types = (
AxisType.GLOBAL, AxisType.UPCAST, AxisType.LOCAL, AxisType.UPCAST,
AxisType.GLOBAL, AxisType.UPCAST, AxisType.LOCAL, AxisType.UPCAST,
AxisType.REDUCE, AxisType.REDUCE)
A_col = UOp.placeholder((ITERS_PER_WAVE_M, TM), dtypes.float, slot=0, addrspace=AddrSpace.REG)
A_col = copy(A_col, As[k, :].reshape(WAVES_IN_BLOCK_Y, ITERS_PER_WAVE_M, LANES_PER_WAVE_Y, TM)[waveIdy, :, laneIdy, :], 300, set=True, upcast=True)
sink = c.store(out).sink(arg=KernelInfo(name="tg_"+to_colored(full_shape, axis_types), axis_types=axis_types))
sink = graph_rewrite(sink, merge_views)
return sink
B_row = UOp.placeholder((ITERS_PER_WAVE_N, TN), dtypes.float, slot=1, addrspace=AddrSpace.REG)
B_row = copy(B_row, Bs[k, :].reshape(WAVES_IN_BLOCK_X, ITERS_PER_WAVE_N, LANES_PER_WAVE_X, TN)[waveIdx, :, laneIdx, :], 400, set=True, upcast=True)
def hand_spec_kernel3(kernel4=getenv("K4", 0), kernel5=getenv("K5", 0)):
BLOCK_SIZE = 128 if kernel5 else 256
# ---------------------------
# FMA: c_regs += A_col * B_row
# ---------------------------
c_regs = UOp.placeholder((ITERS_PER_WAVE_M, TM, ITERS_PER_WAVE_N, TN), dtypes.float, slot=2, addrspace=AddrSpace.REG)
i = UOp.range(c_regs.size, 16)
c_regs = c_regs.after(c_regs.flatten()[i].store(0.0).end(i))
nbWaves = BLOCK_SIZE // 32
WN = 128 if kernel5 else 64
WM = BN * BM // nbWaves // WN
# TODO: why don't these work as upcast?
# why if the ranges merge is it slow?!? (if you change the order on end, they will merge. big slowdown on METAL)
iterWaveM, yt, iterWaveN, xt = rngs = rngs_for_shape(c_regs.shape, 500)
sink = c_regs[*rngs].store(c_regs.after(k)[*rngs] + A_col[iterWaveM, yt] * B_row[iterWaveN, xt]).end(iterWaveM, iterWaveN, yt, xt)
nbWaveX = BN // WN
nbWaveY = BM // WM
# Close k, sync, and close K tiles
sink = sink.end(k).barrier().end(k_tile_range)
threadIdx_x = UOp(Ops.SPECIAL, dtypes.int, arg=("lidx0", BLOCK_SIZE))
waveIndex = threadIdx_x // 32
waveIdx = waveIndex % nbWaveX
waveIdy = waveIndex // nbWaveX
indexInWave = threadIdx_x % 32
# ---------------------------
# REG -> GLOBAL (epilogue)
# ---------------------------
c = c.reshape(WAVES_IN_BLOCK_Y, ITERS_PER_WAVE_M, LANES_PER_WAVE_Y, TM,
WAVES_IN_BLOCK_X, ITERS_PER_WAVE_N, LANES_PER_WAVE_X, TN)
c = c[waveIdy, :, laneIdy, :,
waveIdx, :, laneIdx, :]
sink = copy(c, c_regs.after(sink), rng=600)
nbThreadXPerWave = 8
nbThreadYPerWave = 4
return sink.sink(arg=KernelInfo(opts_to_apply=())).simplify()
idxInWave = indexInWave % nbThreadXPerWave
idyInWave = indexInWave // nbThreadXPerWave
def test_matmul(sink:UOp, N=N):
with Context(DEBUG=0):
a = Tensor.randn(N, N)
b = Tensor.randn(N, N)
hc = Tensor.empty(N, N)
Tensor.realize(a, b, hc)
nbIterWaveN = WN // (nbThreadXPerWave * TN)
nbIterWaveM = WM // (nbThreadYPerWave * TM)
ei = ExecItem(get_runner(Device.DEFAULT, sink), [t.uop.buffer for t in [hc, a, b]])
SUBWN = WN // nbIterWaveN
SUBWM = WM // nbIterWaveM
GlobalCounters.reset()
ets = []
with Context(DEBUG=2):
for _ in range(run_count):
ets.append(ei.run(wait=True))
print(f"REAL TFLOPS {N * N * N * 2 / min(ets) * 1e-12:.2f}")
# Thread mapping to read BKxBN block from A
rAIdx = threadIdx_x % BK
rAIdy = threadIdx_x // BK
# Thread mapping to read BNxBK block from B
rBIdx = threadIdx_x % BN
rBIdy = threadIdx_x // BN
GlobalCounters.reset()
with Context(DEBUG=2):
tc = (a @ b).realize()
with Context(DEBUG=0):
err = (hc - tc).square().mean().item()
print(f"mean squared error {err}")
if err > 1e-06:
raise RuntimeError("matmul is wrong!")
strideReadB = BLOCK_SIZE // BN
strideReadA = BLOCK_SIZE // BK
nbReadsB = BN * BK // BLOCK_SIZE
nbReadsA = BM * BK // BLOCK_SIZE
blockIdx_x = UOp(Ops.SPECIAL, dtypes.int, arg=("gidx0", N//BN))
blockIdx_y = UOp(Ops.SPECIAL, dtypes.int, arg=("gidx1", N//BM))
a = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(N*N), arg=1)
b = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(N*N), arg=2)
c = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(N*N), arg=0)
A_col = UOp(Ops.DEFINE_REG, dtypes.float.ptr(nbIterWaveM * TM, AddrSpace.REG), arg=0)
B_row = UOp(Ops.DEFINE_REG, dtypes.float.ptr(nbIterWaveN * TN, AddrSpace.REG), arg=1)
BM_As_stride = (BM+4) if kernel5 else BM
As = UOp(Ops.DEFINE_LOCAL, dtypes.float.ptr(BK*BM_As_stride, AddrSpace.LOCAL), arg=0)
Bs = UOp(Ops.DEFINE_LOCAL, dtypes.float.ptr(BK*BN, AddrSpace.LOCAL), arg=1)
c_regs = UOp(Ops.DEFINE_REG, dtypes.float.ptr(TM * nbIterWaveM * TN * nbIterWaveN), arg=2)
i = UOp.range(c_regs.dtype.size, 16)
init_store = c_regs[i].store(UOp.const(dtypes.float, 0.0), i)
if kernel4:
regA = UOp(Ops.DEFINE_REG, dtypes.float.ptr(nbReadsA, AddrSpace.REG), arg=3)
regB = UOp(Ops.DEFINE_REG, dtypes.float.ptr(nbReadsB, AddrSpace.REG), arg=4)
# initial load from globals into locals (0)
kId = 0
# load from globals into locals
i = UOp.range(nbReadsB, 0)
index_x = BN * blockIdx_x + rBIdx
index_y = rBIdy + i * strideReadB + kId
Bs_store = Bs[(index_y % BK) * BN + index_x % BN].store(b[N * index_y + index_x].load(), i)
i = UOp.range(nbReadsA, 1)
index_x = rAIdx + kId
index_y = BM * blockIdx_y + rAIdy + i * strideReadA
As_store = As[(index_x % BK) * BM_As_stride + index_y % BM].store(a[N * index_y + index_x].load(), i)
# iterate over the middle chunk
kId_range = UOp.range(N//BK-1, 2)
kId = kId_range*BK
barrier = UOp.barrier(As_store, Bs_store)
# load from globals into registers (next round)
i = UOp.range(nbReadsB, 3)
index_x = BN * blockIdx_x + rBIdx
index_y = rBIdy + i * strideReadB + kId + BK
regB_store = regB[i].store(b[N * index_y + index_x].load(), i)
i = UOp.range(nbReadsA, 4)
index_x = rAIdx + kId + BK
index_y = BM * blockIdx_y + rAIdy + i * strideReadA
regA_store = regA[i].store(a[N * index_y + index_x].load(), i)
def inner_loop(first_range, inp_dep=()):
# inner unroll
k = UOp.range(BK, first_range+0)
# load from locals into registers
iterWave = UOp.range(nbIterWaveN, first_range+1)
i = UOp.range(TN, first_range+2)
index = waveIdx * WN + iterWave * SUBWN + TN * idxInWave + i
B_row_store = B_row[iterWave*TN + i].store(Bs[k*BN + index].load(*inp_dep), iterWave, i)
iterWave = UOp.range(nbIterWaveM, first_range+3)
i = UOp.range(TM, first_range+4)
index = waveIdy * WM + iterWave * SUBWM + TM * idyInWave + i
A_col_store = A_col[iterWave*TM + i].store(As[k*BM_As_stride + index].load(*inp_dep), iterWave, i)
# do the GEMM math
iterWaveM = UOp.range(nbIterWaveM, first_range+5)
yt = UOp.range(TM, first_range+6)
iterWaveN = UOp.range(nbIterWaveN, first_range+7)
xt = UOp.range(TN, first_range+8)
x = iterWaveN * TN + xt
y = iterWaveM * TM + yt
c_regs_idx = c_regs[y * TN * nbIterWaveN + x]
# sketchy, this should end the kId_range but it doesn't
sink = c_regs_idx.store(c_regs_idx.load(init_store) + A_col[y].load(A_col_store) * B_row[x].load(B_row_store),
iterWaveM, iterWaveN, yt, xt, k)
return sink
# TODO: kId_range should endrange after a barrier
sink = inner_loop(5, (barrier, regB_store, regA_store)).barrier()
# load from registers into locals
i = UOp.range(nbReadsB, 14)
index_x = BN * blockIdx_x + rBIdx
index_y = rBIdy + i * strideReadB + kId + BK
Bs_store = Bs[(index_y % BK) * BN + index_x % BN].store(regB[i].load(sink), i, kId_range)
i = UOp.range(nbReadsA, 15)
index_x = rAIdx + kId + BK
index_y = BM * blockIdx_y + rAIdy + i * strideReadA
As_store = As[(index_x % BK) * BM_As_stride + index_y % BM].store(regA[i].load(sink), i, kId_range)
# final iteration without the copy
sink = inner_loop(16, (UOp.barrier(Bs_store, As_store),))
else:
kId_range = UOp.range(N//BK, 0)
kId = kId_range*BK
# load from globals into locals
i = UOp.range(nbReadsB, 1)
index_x = BN * blockIdx_x + rBIdx
index_y = rBIdy + i * strideReadB + kId
Bs_store = Bs[(index_y % BK) * BN + index_x % BN].store(b[N * index_y + index_x].load(), i)
i = UOp.range(nbReadsA, 2)
index_x = rAIdx + kId
index_y = BM * blockIdx_y + rAIdy + i * strideReadA
As_store = As[(index_x % BK) * BM_As_stride + index_y % BM].store(a[N * index_y + index_x].load(), i)
barrier = UOp.barrier(As_store, Bs_store)
k = UOp.range(BK, 3)
# load from locals into registers
iterWave = UOp.range(nbIterWaveN, 4)
i = UOp.range(TN, 5)
index = waveIdx * WN + iterWave * SUBWN + TN * idxInWave + i
B_row_store = B_row[iterWave*TN + i].store(Bs[k*BN + index].load(barrier), iterWave, i)
iterWave = UOp.range(nbIterWaveM, 6)
i = UOp.range(TM, 7)
index = waveIdy * WM + iterWave * SUBWM + TM * idyInWave + i
A_col_store = A_col[iterWave*TM + i].store(As[k*BM_As_stride + index].load(barrier), iterWave, i)
# do the GEMM math
iterWaveM = UOp.range(nbIterWaveM, 8)
yt = UOp.range(TM, 9)
iterWaveN = UOp.range(nbIterWaveN, 10)
xt = UOp.range(TN, 12)
x = iterWaveN * TN + xt
y = iterWaveM * TM + yt
c_regs_idx = c_regs[y * TN * nbIterWaveN + x]
sink = c_regs_idx.store(c_regs_idx.load(init_store) + A_col[y].load(A_col_store) * B_row[x].load(B_row_store),
iterWaveM, iterWaveN, yt, xt, k, kId_range)
# store c_regs into c
iterWaveM = UOp.range(nbIterWaveM, 1000)
yt = UOp.range(TM, 1001)
iterWaveN = UOp.range(nbIterWaveN, 1002)
xt = UOp.range(TN, 1003)
xOut = blockIdx_x * BN + waveIdx * WN + iterWaveN * SUBWN + TN * idxInWave
yOut = blockIdx_y * BM + waveIdy * WM + iterWaveM * SUBWM + TM * idyInWave
indexC = N * (yOut + yt) + xOut + xt
sink = c[indexC].store(c_regs[TN * nbIterWaveN * (iterWaveM * TM + yt) + (iterWaveN * TN + xt)].load(sink),
iterWaveM, iterWaveN, yt, xt)
return sink.sink(arg=KernelInfo(name="tinygemm"))
if __name__ == "__main__":
test_matmul(hand_spec_kernel3(), N=N)
HL = getenv("HL")
if HL == 3: hprg = rangeify_kernel3()
elif HL == 2: hprg = top_spec_kernel3()
elif HL == 1: hprg = hl_spec_kernel3()
else: hprg = hand_spec_kernel3()
if HL == 3:
with Context(BLOCK_REORDER=0):
prg = get_program(hprg, Device.default.renderer)
else:
prg = get_program(hprg, Device.default.renderer)
print(prg.src)
if getenv("SRC"): exit(0)
hrunner = CompiledRunner(prg)
a = Tensor.randn(N, N).realize()
b = Tensor.randn(N, N).realize()
hc = Tensor.zeros(N, N).contiguous().realize()
GlobalCounters.reset()
with Context(DEBUG=2):
for _ in range(run_count): tc = (a@b).realize()
GlobalCounters.reset()
buffers = [hc.uop.buffer, a.uop.buffer, b.uop.buffer]
ei = ExecItem(hrunner, buffers)
with Context(DEBUG=2):
for _ in range(run_count): ei.run(wait=True)
err = (hc-tc).square().mean().item()
print(f"hrunner {err}")
if err > 1e-06: raise RuntimeError("matmul is wrong!")
+17 -5
View File
@@ -1,11 +1,17 @@
import numpy as np, os
from tinygrad.helpers import getenv, flat_mv
from tinygrad import dtypes
from typing import Optional, List, Tuple, cast, Dict, Final, DefaultDict, Self
from tinygrad.engine.realize import get_program
# for copied uops
from tinygrad import dtypes
from tinygrad.dtype import DTYPES_DICT
from tinygrad.codegen.opt.kernel import Kernel, KernelOptError
from tinygrad.uop.ops import UOp, Ops, BinaryOps, UnaryOps, TernaryOps, KernelInfo
from tinygrad.codegen.opt.search import Opt, OptOps
from tinygrad import Device, dtypes, Tensor
from tinygrad.dtype import PtrDType, DType, DTYPES_DICT
from tinygrad.shape.shapetracker import ShapeTracker
from tinygrad.shape.view import View
script_dir = os.path.dirname(os.path.abspath(__file__))
@@ -47,6 +53,12 @@ def randoms():
nc = nc.astype(np.bfloat16 if DTYPE_IN == dtypes.bfloat16 else np.float16)
return na, nb, nc
def ast_to_cuda_prog(compiler, ast, opts):
k = Kernel(ast)
k.apply_opts(opts)
p = get_program(k.ast, k.opts, k.applied_opts)
return CUDAProgram(device, p.function_name, compiler.compile(p.src))
if __name__ == "__main__":
print(f"gemm variation: {GEMM_VARIATION=} {M=} {N=} {K=} {DTYPE_IN=} {DTYPE_OUT=} {DTYPE_ACC=}")
prog, global_size, local_size = None, None, None
@@ -177,11 +189,11 @@ if __name__ == "__main__":
tms = []
na, nb, nc = randoms()
cudaalloc._copyin(a, memoryview(bytearray(na)))
cudaalloc._copyin(b, memoryview(bytearray(nb)))
cudaalloc.copyin(a, bytearray(na))
cudaalloc.copyin(b, bytearray(nb))
for i in range(CNT):
tms.append(prog(*args, **kwargs))
cudaalloc._copyout(flat_mv(nc.data), c)
cudaalloc.copyout(flat_mv(nc.data), c)
comp = na.astype(np.float32) @ nb.astype(np.float32)
result = nc.reshape(M, N).astype(np.float32)
-42
View File
@@ -1,42 +0,0 @@
from tinygrad import UOp, dtypes
from tinygrad.uop.ops import AxisType, Ops, KernelInfo, AddrSpace
from extra.gemm.amd_uop_matmul import test_matmul
N = 2048
# metal has an 8x8 tensor core. this is the indexing
def mat_idx(buf, g0, g1, warp, u):
l = [(warp//2**i)%2 for i in range(5)]
return buf[g0, l[4]*4 + l[2]*2 + l[1], g1, l[3]*4 + l[0]*2 + u]
def hand_spec_tc_cores():
gx = UOp.special(N // 8, "gidx0")
gy = UOp.special(N // 8, "gidx1")
warp = UOp.special(32, "lidx0")
c = UOp.placeholder((N, N), dtypes.float, slot=0).reshape((N//8, 8, N//8, 8))
a = UOp.placeholder((N, N), dtypes.float, slot=1).reshape((N//8, 8, N//8, 8))
b = UOp.placeholder((N, N), dtypes.float, slot=2).reshape((N//8, 8, N//8, 8))
gk = UOp.range(N // 8, 0, AxisType.REDUCE)
a_tc = UOp.vectorize(*[mat_idx(a, gx, gk, warp, i) for i in range(2)])
b_tc = UOp.vectorize(*[mat_idx(b, gk, gy, warp, i) for i in range(2)])
acc = UOp.placeholder((2,), dtypes.float, slot=0, addrspace=AddrSpace.REG)
acc = acc[0].set(0.0)
acc = acc[1].set(0.0)
# TODO: make this simple
wmma_arg = ('WMMA_8_8_8_float_float', (8, 8, 8), dtypes.float, dtypes.float, 'METAL', 32, (((3, 2),), ((3, 2),), ((3, 2),)), ())
acc_load = UOp.vectorize(acc.after(gk)[0], acc.after(gk)[1])
out = UOp(Ops.WMMA, dtypes.float.vec(2), (a_tc, b_tc, acc_load), arg=wmma_arg)
end_loop = UOp.group(*[acc[i].store(out.gep(i)) for i in range(2)]).end(gk)
sink = UOp.group(*[mat_idx(c.after(end_loop), gx, gy, warp, i).store(acc[i]) for i in range(2)])
return sink.sink(arg=KernelInfo(name="custom_metal_matmul", opts_to_apply=())).simplify()
if __name__ == "__main__":
test_matmul(hand_spec_tc_cores(), N=N)
-229
View File
@@ -1,229 +0,0 @@
import os
import numpy as np
np.set_printoptions(linewidth=1000000)
os.environ["AMD_LLVM"] = "0"
from tinygrad import Tensor, Context, dtypes, UOp, GlobalCounters
from tinygrad.helpers import DEBUG, getenv
from tinygrad.dtype import AddrSpace
from tinygrad.uop.ops import AxisType, KernelInfo, Ops
WARP_SIZE = 64
# Reg tile sizes (tensor cores)
TC_M = 16
TC_N = 16
TC_K = 32
# 1024 matrix cores
# 16 cycle mfma
# 2.2 GHz
# 16x16x32x2 FLOPS/mma = 16384
# 2.2*1e9*16384*1024/16*1e-12 TFLOPS = 2306 TFLOPS
#N,M,K = 256,256,64
N,M,K = 4096,4096,4096
# Threadblock tile sizes (block-level tile of C that a block computes)
#BLOCK_M = 128 # rows of C (M-dim) per block
#BLOCK_N = 128 # columns of C (N-dim) per block
#BLOCK_K = 128 # K-slice per block iteration
BLOCK_M = 64
BLOCK_N = 64
BLOCK_K = 128
WARPGROUP_SIZE = 1
BLOCK_M = BLOCK_M * WARPGROUP_SIZE
# TODO: improve the syntax of this. better syntax, faster iteration
# -- DONE: add working slice a[gx, :, i] -> shape of the : (aka (16,16,32) becomes (16,))
# -- DONE(ish): add argfix to movement (traits shared with Tensor)
# -- fix WMMA to not require all the junk
# -- improve syntax for vectorized loads/stores (both with DEVECTORIZE and without)
# -- DONE: be able to use CONTRACT on a range
# -- fix upcasted RANGE on an already vectorized buffer
# -- improve "all ranges not ended error" / fix the bug with after on ended ranges (if you are after end of range, range is closed)
CUS_PER_GPU = 256
assert ((M//BLOCK_M) * (N//BLOCK_N)) >= CUS_PER_GPU, "not enough globals"
def custom_gemm(C:UOp, A:UOp, B:UOp) -> UOp:
# A = (M x K)
# B = (K x N)
# C = (M x N)
# check it's proper matmul
assert C.shape[0] == A.shape[0]
assert C.shape[1] == B.shape[1]
assert A.shape[1] == B.shape[0]
gx, gy = UOp.special(M//BLOCK_M, "gidx0"), UOp.special(N//BLOCK_N, "gidx1")
warp = UOp.special(WARP_SIZE, "lidx0")
warpgroup = UOp.special(WARPGROUP_SIZE, "lidx1")
# generic copy logic (not good)
def generic_copy(glbl, gargs, lcl, rng):
# Fully coalesced 128-bit loads/stores.
INNER_SIZE = 8
cp_i = UOp.range(lcl.size//(WARPGROUP_SIZE*WARP_SIZE*INNER_SIZE), rng)
cp_inner = UOp.range(INNER_SIZE, rng+1, AxisType.UPCAST)
idx_i = cp_i*WARPGROUP_SIZE*WARP_SIZE*INNER_SIZE + warpgroup*WARP_SIZE*INNER_SIZE + warp*INNER_SIZE + cp_inner
return lcl[idx_i].store(glbl[*gargs, idx_i]).end(cp_i, cp_inner)
# split out the globals into blocks
C = C.reshape((M//BLOCK_M, BLOCK_M, N//BLOCK_N, BLOCK_N))
A = A.reshape((M//BLOCK_M, BLOCK_M, K//BLOCK_K, BLOCK_K))
B = B.reshape((K//BLOCK_K, BLOCK_K, N//BLOCK_N, BLOCK_N))
# this is the big accumulator
acc = UOp.placeholder((BLOCK_N//TC_N, BLOCK_M//TC_M//WARPGROUP_SIZE), dtypes.float.vec(4), 0, AddrSpace.REG)
assert acc.size*WARP_SIZE*WARPGROUP_SIZE*4 == BLOCK_M*BLOCK_N
acc = acc[init_l:=UOp.range(acc.size, 500)].set(UOp.const(dtypes.float.vec(4), 0.0), end=init_l)
# create locals (note A is permuted, and the stride is changed to avoid bank conflicts)
def make_locals(slot) -> tuple[UOp, UOp]:
BM_As_stride = (BLOCK_M + 1)
BN_Bs_stride = (BLOCK_N + 0)
INNER_SLICE = 8
As = UOp.placeholder((BLOCK_K//INNER_SLICE, BM_As_stride, INNER_SLICE), dtypes.half, slot=slot, addrspace=AddrSpace.LOCAL)
INNER_SLICE = 1
Bs = UOp.placeholder((BLOCK_K//INNER_SLICE, BN_Bs_stride, INNER_SLICE), dtypes.half, slot=slot+1, addrspace=AddrSpace.LOCAL)
As = As.permute((0,2,1)).reshape((BLOCK_K, BM_As_stride)).shrink_to((BLOCK_K, BLOCK_M))
Bs = Bs.permute((0,2,1)).reshape((BLOCK_K, BN_Bs_stride)).shrink_to((BLOCK_K, BLOCK_N))
return As, Bs
# load from globals into locals (TODO: use the warpgroup)
def load_to_locals(l_K_outer_loop:UOp, Asl:UOp, Bsl:UOp, rng:int, barrier=True) -> tuple[UOp, UOp]:
if getenv("FAKE"):
return Asl[0].set(0), Bsl[0].set(0)
else:
pA = A.permute((0,2,1,3)).reshape((M//BLOCK_M, K//BLOCK_K, BLOCK_M*BLOCK_K))
pas = Asl.permute((1,0)).reshape((BLOCK_M*BLOCK_K,))
As_store = generic_copy(pA, (gx, l_K_outer_loop), pas, rng)
pB = B.permute((0,2,1,3)).reshape((K//BLOCK_K, N//BLOCK_N, BLOCK_K*BLOCK_N))
pbs = Bsl.reshape((BLOCK_K*BLOCK_N,))
Bs_store = generic_copy(pB, (l_K_outer_loop, gy), pbs, rng+2)
barrier = UOp.barrier(As_store, Bs_store) if barrier else UOp.group(As_store, Bs_store)
return Asl.after(barrier), Bsl.after(barrier)
def compute_on_locals(acc:UOp, Asl:UOp, Bsl:UOp, rng:int, afters:tuple[UOp, ...]=()) -> UOp:
K_inner_loop = UOp.range(BLOCK_K//TC_K, rng, AxisType.REDUCE)
# load from locals into registers
Ar = UOp.placeholder((BLOCK_M//TC_M//WARPGROUP_SIZE,), dtypes.half.vec(8), slot=1, addrspace=AddrSpace.REG)
Br = UOp.placeholder((BLOCK_N//TC_N,), dtypes.half.vec(8), slot=2, addrspace=AddrSpace.REG)
M_load_loop = UOp.range(BLOCK_M//TC_M//WARPGROUP_SIZE, rng+10)
Asl = Asl.reshape((BLOCK_K//TC_K, TC_K, BLOCK_M//TC_M//WARPGROUP_SIZE, WARPGROUP_SIZE, TC_M))
load_rng = UOp.range(8, rng+11, axis_type=AxisType.UPCAST)
A_in = Asl[K_inner_loop, (warp//16)*8+load_rng, M_load_loop, warpgroup, warp%16].contract(load_rng)
Ar = Ar[M_load_loop].set(A_in, end=M_load_loop)
N_load_loop = UOp.range(BLOCK_N//TC_N, rng+20)
Bsl = Bsl.reshape((BLOCK_K//TC_K, TC_K, BLOCK_N//TC_N, TC_N))
load_rng = UOp.range(8, rng+21, axis_type=AxisType.UPCAST)
B_in = Bsl[K_inner_loop, (warp//16)*8+load_rng, N_load_loop, warp%16].contract(load_rng)
Br = Br[N_load_loop].set(B_in, end=N_load_loop)
M_inner_loop = UOp.range(BLOCK_M//TC_M//WARPGROUP_SIZE, rng+30)
N_inner_loop = UOp.range(BLOCK_N//TC_N, rng+31)
# load values
acc_after = acc.after(*afters, M_inner_loop, N_inner_loop, K_inner_loop)
acc_load = acc_after[N_inner_loop, M_inner_loop]
# do WMMA
wmma_arg = ('WMMA_16_16_32_half_float', (16, 16, 32), dtypes.half, dtypes.float, 'AMD', 64, ((), (), ((3, 2), (2, 2))), ())
out = UOp(Ops.WMMA, dtypes.float.vec(4), (Ar[M_inner_loop], Br[N_inner_loop], acc_load), arg=wmma_arg)
# store back the acc
acc_store = acc[N_inner_loop, M_inner_loop].store(out)
return acc_store.end(M_inner_loop, N_inner_loop, K_inner_loop)
# **** START INNER LOOP *****
# inner loop -- locals -> regs
# no pipeline
if not getenv("PIPELINE"):
As, Bs = make_locals(slot=0)
K_outer_loop = UOp.range(K//BLOCK_K, 0, AxisType.REDUCE)
As, Bs = load_to_locals(K_outer_loop, As, Bs, 1000, barrier=True)
acc_store = compute_on_locals(acc, As, Bs, 1500, afters=(K_outer_loop,))
acc = acc.after(acc_store.barrier().end(K_outer_loop))
else:
# this doesn't work
As0, Bs0 = make_locals(slot=0)
As1, Bs1 = make_locals(slot=2)
As0, Bs0 = load_to_locals(0, As0, Bs0, 1000)
K_outer_loop = UOp.range((K//BLOCK_K-2)//2, 0, AxisType.REDUCE)
As1, Bs1 = load_to_locals(K_outer_loop+1, As1, Bs1, 2000, barrier=False)
acc_store = compute_on_locals(acc, As0, Bs0, 1500, afters=(K_outer_loop,))
As0, Bs0 = load_to_locals(K_outer_loop+2, As0, Bs0, 3000, barrier=False)
acc_store = compute_on_locals(acc, As1, Bs1, 2500, afters=(acc_store, As0, Bs0))
acc = acc.after(acc_store.barrier().end(K_outer_loop))
#acc_store = compute_on_locals(acc, As0, Bs0, 3500, afters=(acc_store.barrier().end(K_outer_loop)))
"""
As1, Bs1 = load_to_locals(K//BLOCK_K-1, As1, Bs1, 4000)
acc_store = compute_on_locals(acc, As1, Bs1, 4500, afters=(acc_store))
"""
#acc = acc.after(acc_store)
# **** END LOOPS *****
# store the acc into gmem
cp_i, cp_j = UOp.range(BLOCK_M//TC_M//WARPGROUP_SIZE, 10004), UOp.range(BLOCK_N//TC_N, 10005)
c_load = lambda i: C[gx, cp_i*TC_M*WARPGROUP_SIZE + warpgroup*TC_M + (warp//16)*4+i, gy, cp_j*TC_N + warp%16]
store = UOp.group(*[c_load(i).store(acc[cp_j, cp_i].gep(i)) for i in range(4)])
store = store.end(cp_i, cp_j)
return store.sink(arg=KernelInfo(name="custom_gemm", opts_to_apply=())).simplify()
# simplest WMMA
"""
# init the acc
acc = UOp.placeholder((4,), dtypes.float, 0, AddrSpace.REG)
acc = acc[init_l:=UOp.range(4, 1)].set(0.0, end=init_l)
# do the wmma
acc_load = UOp.vectorize(*[acc.after(K_loop)[i] for i in range(4)])
wmma_arg = ('WMMA_16_16_32_half_float', (16, 16, 32), dtypes.half, dtypes.float, 'AMD', 64, ((), (), ((3, 2), (2, 2))), ())
out = UOp(Ops.WMMA, dtypes.float.vec(4), (A_in, B_in, acc_load), arg=wmma_arg)
# store back the acc
acc = acc.after(UOp.group(*[acc[i].store(out.gep(i)) for i in range(4)]).end(K_loop))
# store the acc into gmem
store = UOp.group(*[C[gx, (warp//16)*4+i, gy, warp%16].store(acc[i]) for i in range(4)])
"""
if __name__ == "__main__":
a = Tensor.randn(M, K, dtype=dtypes.half)
b = Tensor.randn(K, N, dtype=dtypes.half)
#a = Tensor.zeros(M, K, dtype=dtypes.half).contiguous()
#a[0,16] = 1
#b = Tensor.ones(K, N, dtype=dtypes.half).contiguous()
c = Tensor.empty(M, N, dtype=dtypes.float)
with Context(DEBUG=0): Tensor.realize(a,b)
ref = a.dot(b, dtype=dtypes.float)
ref.realize()
GlobalCounters.reset()
with Context(DEBUG=max(2, DEBUG.value), DEVECTORIZE=2):
tst = Tensor.custom_kernel(c, a, b, fxn=custom_gemm)[0]
tst.realize()
print(f"{(N*M*K*2 / GlobalCounters.time_sum_s)*1e-12:.2f} REAL TFLOPS")
with Context(DEBUG=0):
#print(ref.numpy())
#print(tst.numpy())
assert Tensor.isclose(ref, tst, atol=1e-2).all().item(), "matrix not close"
+4 -8
View File
@@ -5,10 +5,8 @@ from tinygrad.dtype import _to_np_dtype
from tinygrad.codegen.opt import OptOps
from tinygrad.engine.realize import lower_schedule
dtype_in = (dtypes.half if getenv("HALF") else dtypes.bfloat16 if getenv("BFLOAT16") else
dtypes.fp8e4m3 if getenv("FP8E4M3") else dtypes.fp8e5m2 if getenv("FP8E5M2") else dtypes.float)
acc_dtype = (dtypes.half if getenv("ACC_HALF") else dtypes.bfloat16 if getenv("ACC_BFLOAT16") else
dtypes.fp8e4m3 if getenv("ACC_FP8E4M3") else dtypes.fp8e5m2 if getenv("ACC_FP8E5M2") else None)
dtype_in = dtypes.half if getenv("HALF") else dtypes.bfloat16 if getenv("BFLOAT16") else dtypes.float
acc_dtype = dtypes.half if getenv("ACC_HALF") else dtypes.bfloat16 if getenv("ACC_BFLOAT16") else None
if getenv("INT"): dtype_in, acc_dtype = dtypes.int8, dtypes.int32
if getenv("UINT"): dtype_in, acc_dtype = dtypes.uint8, dtypes.int32
@@ -16,10 +14,8 @@ N = getenv("N", 4096)
M = getenv("M", N)
K = getenv("K", N)
CNT = getenv("CNT", 10)
atol, rtol = {dtypes.half:{1e-3, 1e-2}, dtypes.bfloat16:(1e-3, 1e-2), dtypes.fp8e4m3:(1e-1, 1e-1), dtypes.fp8e5m2:(1.0, 5e-1)}.get(dtype_in, (1e-4, 3e-2))
ATOL, RTOL = getenv("ATOL", atol), getenv("RTOL", rtol)
ATOL = getenv("ATOL", 1e-4)
RTOL = getenv("RTOL", 3e-2)
INT_LOW = getenv("INT_LOW", 0)
INT_HIGH = getenv("INT_HIGH", 10)
+1 -5
View File
@@ -8,23 +8,19 @@ import torch
torch.set_num_threads(1)
from tinygrad.helpers import getenv
CUDA = getenv("CUDA", 1)
MPS = getenv("MPS", 0)
if getenv("FP16_ACC"): torch.backends.cuda.matmul.allow_fp16_accumulation = True
for dtype in [torch.float32, torch.float16, torch.bfloat16]:
for dtype in [torch.float32, torch.float16]:
for N in [256, 512, 1024, 2048, 4096]:
FLOPS = N*N*N*2
b = torch.rand((N,N), dtype=dtype)
c = torch.rand((N,N), dtype=dtype)
if CUDA: b,c = b.cuda(),c.cuda()
if MPS: b,c = b.to('mps'),c.to('mps')
def torch_prog(b, c):
st = time.perf_counter()
a = b@c
if CUDA: torch.cuda.synchronize()
if MPS: torch.mps.synchronize()
return time.perf_counter() - st
tm = min([torch_prog(b, c) for _ in range(20)])
print(f"{N*N:10d} {tm*1e6:9.2f} us, would be {FLOPS*1e-9/tm:9.2f} GFLOPS {N:4d}x{N:4d}x{N:4d} matmul in {dtype}")
+3 -2
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
import argparse, glob, os, time, subprocess, sys
from tinygrad.runtime.support.system import System
import argparse, glob, os, re, time, subprocess, sys
def scan_devs_based_on_lock(prefix:str, args) -> list[str]:
target_dev = args.pci_bus if 'pci_bus' in args.__dir__() else ""
@@ -11,7 +12,7 @@ def scan_devs_based_on_lock(prefix:str, args) -> list[str]:
if os.path.exists(f"/sys/bus/pci/devices/{dev_id}") and dev_id.startswith(target_dev): devs.append(dev_id)
return devs
def _do_reset_device(pci_bus): os.system(f"sudo sh -c 'echo 1 > /sys/bus/pci/devices/{pci_bus}/reset'")
def _do_reset_device(pci_bus): System.pci_reset(pci_bus)
def _is_module_loaded(name: str) -> bool: return os.path.isdir(f"/sys/module/{name}")
def cmd_remove_module(args):
-23
View File
@@ -1,23 +0,0 @@
#!/bin/sh
if [ "$#" -ne 1 ] || ! [ -d $1 ]; then
echo "usage: $0 MESA_PREFIX"
exit 1
fi
TMP=$(mktemp)
trap 'rm -f "$TMP"' EXIT
(
cat <<EOF
#define HAVE_ENDIAN_H
#define HAVE_STRUCT_TIMESPEC
#define HAVE_PTHREAD
#include <unistd.h>
#include "nir_shader_compiler_options.h"
#include "compiler/shader_enums.h"
EOF
sed -n '/struct nir_shader_compiler_options/,/^}/{p;/^}/q}' $1/src/gallium/drivers/llvmpipe/lp_screen.c
echo "int main(void) { write(1, &gallivm_nir_options, sizeof(gallivm_nir_options)); }"
) | cc -x c -o $TMP - -I$1/src/compiler/nir -I$1/src -I$1/include && $TMP | gzip | base64 -w0
+22 -40
View File
@@ -7,34 +7,31 @@ import os
NUM_WORKGROUPS = 96
WAVE_SIZE = 32
NUM_WAVES = 2
FLOPS_PER_MATMUL = 16*16*16*2
INTERNAL_LOOP = 1_000_00
INSTRUCTIONS_PER_LOOP = 200
DIRECTIVE = ".amdhsa_wavefront_size32 1"
FLOPS_PER_MATMUL = 16*16*16*2
INTERNAL_LOOP = 1_000_000
INSTRUCTIONS_PER_LOOP = 1_000
assemblyTemplate = (pathlib.Path(__file__).parent / "template.s").read_text()
def launchBenchmark(instruction, vgprIndices, dense=True, accum=False, extra=""):
if accum:
instructions = "{} a[0:{}], v[{}:{}], v[{}:{}], 1{}\n".format(instruction, vgprIndices[0],
vgprIndices[1], vgprIndices[2],
vgprIndices[1], vgprIndices[2], extra)
elif dense:
def launchBenchmark(instruction, vgprIndices, dense = True):
if dense:
instructions = "{} v[0:{}], v[{}:{}], v[{}:{}], 1\n".format(instruction, vgprIndices[0],
vgprIndices[1], vgprIndices[2],
vgprIndices[1], vgprIndices[2])
vgprIndices[1], vgprIndices[2]) * INSTRUCTIONS_PER_LOOP
else:
instructions = "{} v[0:{}], v[{}:{}], v[{}:{}], v{}\n".format(instruction, vgprIndices[0],
vgprIndices[1], vgprIndices[2],
vgprIndices[3], vgprIndices[4],
vgprIndices[5])
src = assemblyTemplate.replace("INTERNAL_LOOP", str(INTERNAL_LOOP)).replace("INSTRUCTION", instructions*INSTRUCTIONS_PER_LOOP)
src = src.replace("DIRECTIVE", DIRECTIVE)
vgprIndices[1], vgprIndices[2],
vgprIndices[3], vgprIndices[4],
vgprIndices[5]) * INSTRUCTIONS_PER_LOOP
src = assemblyTemplate.replace("INSTRUCTION", instructions)
lib = COMPILER.compile(src)
fxn = AMDProgram(DEV, "matmul", lib)
elapsed = fxn(global_size=(NUM_WORKGROUPS,1,1), local_size=(WAVE_SIZE*NUM_WAVES,1,1), wait=True)
start = time.perf_counter()
fxn(global_size=(NUM_WORKGROUPS,1,1), local_size=(WAVE_SIZE*NUM_WAVES,1,1), wait=True) #For some reason the returned time is very small after the first kernel execution
end = time.perf_counter()
elapsed = end-start
FLOPs = FLOPS_PER_MATMUL * NUM_WAVES * NUM_WORKGROUPS * INTERNAL_LOOP * INSTRUCTIONS_PER_LOOP
print(f"{instruction:<29} : {FLOPs/elapsed/10**12:.2f} T(FL)OPS")
print("{:<29} : {} T(FL)OPS".format(instruction, round(FLOPs/elapsed/10**12, 2)))
if __name__=="__main__":
DEVICENUM = os.getenv("DEVICENUM", "0")
@@ -43,17 +40,18 @@ if __name__=="__main__":
except:
raise RuntimeError("Error while initiating AMD device")
COMPILER = HIPCompiler(DEV.arch)
if DEV.arch in {'gfx1100', 'gfx1103'}:
if DEV.arch == 'gfx1103':
NUM_WORKGROUPS = 8
if (ARCH := DEV.arch) not in ['gfx1100', 'gfx1201']:
raise RuntimeError("only gfx1100 and gfx1201 supported")
COMPILER = HIPCompiler(ARCH)
if ARCH == 'gfx1100':
launchBenchmark("v_wmma_bf16_16x16x16_bf16", (7,8,15))
launchBenchmark("v_wmma_f16_16x16x16_f16", (7,8,15))
launchBenchmark("v_wmma_f32_16x16x16_bf16", (7,8,15))
launchBenchmark("v_wmma_f32_16x16x16_f16", (7,8,15))
launchBenchmark("v_wmma_i32_16x16x16_iu4", (7,8,9))
launchBenchmark("v_wmma_i32_16x16x16_iu8", (7,8,11))
elif DEV.arch == 'gfx1201':
if ARCH == 'gfx1201':
NUM_WORKGROUPS = 64
launchBenchmark("v_wmma_bf16_16x16x16_bf16", (3,4,7))
launchBenchmark("v_wmma_f16_16x16x16_f16", (3,4,7))
@@ -78,20 +76,4 @@ if __name__=="__main__":
launchBenchmark("v_swmmac_f32_16x16x32_bf8_fp8", (7,8,9,10,13,14), False)
launchBenchmark("v_swmmac_f32_16x16x32_bf8_bf8", (7,8,9,10,13,14), False)
FLOPS_PER_MATMUL = 16*16*64*2
launchBenchmark("v_swmmac_i32_16x16x64_iu4", (7,8,9,10,13,14), False)
elif DEV.arch == 'gfx950':
DIRECTIVE = ".amdhsa_accum_offset 4"
NUM_WORKGROUPS = 256
WAVE_SIZE = 64
NUM_WAVES = 4
launchBenchmark("v_mfma_f32_16x16x16_f16", (3,0,1), accum=True)
launchBenchmark("v_mfma_f32_16x16x16_bf16", (3,0,1), accum=True)
FLOPS_PER_MATMUL = 16*16*32*2
launchBenchmark("v_mfma_f32_16x16x32_f16", (3,0,3), accum=True)
launchBenchmark("v_mfma_f32_16x16x32_bf16", (3,0,3), accum=True)
FLOPS_PER_MATMUL = 16*16*128*2
launchBenchmark("v_mfma_f32_16x16x128_f8f6f4", (3,0,7), accum=True) # fp8
launchBenchmark("v_mfma_f32_16x16x128_f8f6f4", (3,0,5), accum=True, extra=", cbsz:2 blgp:2") # fp6
launchBenchmark("v_mfma_f32_16x16x128_f8f6f4", (3,0,3), accum=True, extra=", cbsz:4 blgp:4") # fp4
else:
raise RuntimeError(f"arch {DEV.arch} not supported.")
launchBenchmark("v_swmmac_i32_16x16x64_iu4", (7,8,9,10,13,14), False)
+5 -4
View File
@@ -1,9 +1,9 @@
.text
.globl matmul
.p2align 8
.p2align 8
.type matmul,@function
matmul:
s_mov_b32 s1, INTERNAL_LOOP
s_mov_b32 s1, 1000000
s_mov_b32 s2, 0
inner_loop:
INSTRUCTION
@@ -17,7 +17,7 @@ matmul:
.amdhsa_kernel matmul
.amdhsa_next_free_vgpr .amdgcn.next_free_vgpr
.amdhsa_next_free_sgpr .amdgcn.next_free_sgpr
DIRECTIVE
.amdhsa_wavefront_size32 1
.end_amdhsa_kernel
.amdgpu_metadata
@@ -28,7 +28,7 @@ amdhsa.version:
amdhsa.kernels:
- .name: matmul
.symbol: matmul.kd
.kernarg_segment_size: 0
.kernarg_segment_size: 0
.group_segment_fixed_size: 0
.private_segment_fixed_size: 0
.kernarg_segment_align: 4
@@ -36,5 +36,6 @@ amdhsa.kernels:
.sgpr_count: 8
.vgpr_count: 32
.max_flat_workgroup_size: 1024
.args:
...
.end_amdgpu_metadata
-14
View File
@@ -89,20 +89,6 @@ class Attention:
keys, values = repeat_kv(keys, self.n_rep), repeat_kv(values, self.n_rep)
xq, keys, values = xq.transpose(1, 2), keys.transpose(1, 2), values.transpose(1, 2)
attn = xq.scaled_dot_product_attention(keys, values, mask).transpose(1, 2)
if getenv("STUB_ATTENTION"):
# TODO: do we need mask?
from tinygrad.uop.ops import UOp, KernelInfo
def fa_custom_forward(attn:UOp, q:UOp, k:UOp, v:UOp) -> UOp:
return UOp.sink(arg=KernelInfo(name="fa_custom_forward"))
def fa_custom_backward(out_q:UOp, out_k:UOp, out_v:UOp, grad:UOp, q:UOp, k:UOp, v:UOp) -> UOp:
return UOp.sink(arg=KernelInfo(name="fa_custom_backward"))
def fa_backward(grad:UOp, kernel:UOp) -> tuple[None, UOp, UOp, UOp]:
grad_q = Tensor.empty_like(q:=Tensor(kernel.src[1]))
grad_k = Tensor.empty_like(k:=Tensor(kernel.src[2]))
grad_v = Tensor.empty_like(v:=Tensor(kernel.src[3]))
ck = Tensor.custom_kernel(grad_q, grad_k, grad_v, Tensor(grad), q, k, v, fxn=fa_custom_backward)[:3]
return (None, ck[0].uop, ck[1].uop, ck[2].uop)
attn = Tensor.empty_like(attn).custom_kernel(xq, keys, values, fxn=fa_custom_forward, grad_fxn=fa_backward)[0]
attn = attn.reshape(bsz, seqlen, -1)
return self.wo(attn)
-134
View File
@@ -1,134 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 1993-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef PCIEXPTBL_H
#define PCIEXPTBL_H
#define NV_BCRT_HASH_INFO_BASE_CODE_TYPE_VBIOS_BASE 0x00
#define NV_BCRT_HASH_INFO_BASE_CODE_TYPE_VBIOS_EXT 0xE0
//
// The VBIOS object comes from walking the PCI expansion code block
// The following structure holds the expansion code format.
//
#define PCI_EXP_ROM_SIGNATURE 0xaa55
#define PCI_EXP_ROM_SIGNATURE_NV 0x4e56 // "VN" in word format
#define PCI_EXP_ROM_SIGNATURE_NV2 0xbb77
#define IS_VALID_PCI_ROM_SIG(sig) ((sig == PCI_EXP_ROM_SIGNATURE) || \
(sig == PCI_EXP_ROM_SIGNATURE_NV) || \
(sig == PCI_EXP_ROM_SIGNATURE_NV2))
#define OFFSETOF_PCI_EXP_ROM_SIG 0x0
#define OFFSETOF_PCI_EXP_ROM_NBSI_DATA_OFFSET 0x16
#define OFFSETOF_PCI_EXP_ROM_PCI_DATA_STRUCT_PTR 0x18
#pragma pack(1)
typedef struct _PCI_EXP_ROM_STANDARD
{
NvU16 sig; // 00h: ROM Signature 0xaa55
NvU8 reserved [0x16]; // 02h: Reserved (processor architecture unique data)
NvU16 pciDataStrucPtr; // 18h: Pointer to PCI Data Structure
NvU32 sizeOfBlock; // 1Ah: <NBSI-specific appendage>
} PCI_EXP_ROM_STANDARD, *PPCI_EXP_ROM_STANDARD;
#pragma pack()
#pragma pack(1)
typedef struct _PCI_EXP_ROM_NBSI
{
NvU16 sig; // 00h: ROM Signature 0xaa55
NvU8 reserved [0x14]; // 02h: Reserved (processor architecture unique data)
NvU16 nbsiDataOffset; // 16h: Offset from header to NBSI image
NvU16 pciDataStrucPtr; // 18h: Pointer to PCI Data Structure
NvU32 sizeOfBlock; // 1Ah: <NBSI-specific appendage>
} PCI_EXP_ROM_NBSI, *PPCI_EXP_ROM_NBSI;
#pragma pack()
typedef union _PCI_EXP_ROM {
PCI_EXP_ROM_STANDARD standard;
PCI_EXP_ROM_NBSI nbsi;
} PCI_EXP_ROM, *PPCI_EXP_ROM;
#define PCI_DATA_STRUCT_SIGNATURE 0x52494350 // "PCIR" in dword format
#define PCI_DATA_STRUCT_SIGNATURE_NV 0x5344504E // "NPDS" in dword format
#define PCI_DATA_STRUCT_SIGNATURE_NV2 0x53494752 // "RGIS" in dword format
#define IS_VALID_PCI_DATA_SIG(sig) ((sig == PCI_DATA_STRUCT_SIGNATURE) || \
(sig == PCI_DATA_STRUCT_SIGNATURE_NV) || \
(sig == PCI_DATA_STRUCT_SIGNATURE_NV2))
#define PCI_LAST_IMAGE NVBIT(7)
#define PCI_ROM_IMAGE_BLOCK_SIZE 512U
#define OFFSETOF_PCI_DATA_STRUCT_SIG 0x0
#define OFFSETOF_PCI_DATA_STRUCT_VENDOR_ID 0x4
#define OFFSETOF_PCI_DATA_STRUCT_LEN 0xa
#define OFFSETOF_PCI_DATA_STRUCT_CLASS_CODE 0xd
#define OFFSETOF_PCI_DATA_STRUCT_CODE_TYPE 0x14
#define OFFSETOF_PCI_DATA_STRUCT_IMAGE_LEN 0x10
#define OFFSETOF_PCI_DATA_STRUCT_LAST_IMAGE 0x15
#pragma pack(1)
typedef struct _PCI_DATA_STRUCT
{
NvU32 sig; // 00h: Signature, the string "PCIR" or NVIDIA's alternate "NPDS"
NvU16 vendorID; // 04h: Vendor Identification
NvU16 deviceID; // 06h: Device Identification
NvU16 deviceListPtr; // 08h: Device List Pointer
NvU16 pciDataStructLen; // 0Ah: PCI Data Structure Length
NvU8 pciDataStructRev; // 0Ch: PCI Data Structure Revision
NvU8 classCode[3]; // 0Dh: Class Code
NvU16 imageLen; // 10h: Image Length (units of 512 bytes)
NvU16 vendorRomRev; // 12h: Revision Level of the Vendor's ROM
NvU8 codeType; // 14h: holds NBSI_OBJ_CODE_TYPE (0x70) and others
NvU8 lastImage; // 15h: Last Image Indicator: bit7=1 is lastImage
NvU16 maxRunTimeImageLen; // 16h: Maximum Run-time Image Length (units of 512 bytes)
} PCI_DATA_STRUCT, *PPCI_DATA_STRUCT;
#pragma pack()
#define NV_PCI_DATA_EXT_SIG 0x4544504E // "NPDE" in dword format
#define NV_PCI_DATA_EXT_REV_10 0x100 // 1.0
#define NV_PCI_DATA_EXT_REV_11 0x101 // 1.1
#define OFFSETOF_PCI_DATA_EXT_STRUCT_SIG 0x0
#define OFFSETOF_PCI_DATA_EXT_STRUCT_LEN 0x6
#define OFFSETOF_PCI_DATA_EXT_STRUCT_REV 0x4
#define OFFSETOF_PCI_DATA_EXT_STRUCT_SUBIMAGE_LEN 0x8
#define OFFSETOF_PCI_DATA_EXT_STRUCT_LAST_IMAGE 0xa
#define OFFSETOF_PCI_DATA_EXT_STRUCT_FLAGS 0xb
#define PCI_DATA_EXT_STRUCT_FLAGS_CHECKSUM_DISABLED 0x04
#pragma pack(1)
typedef struct _NV_PCI_DATA_EXT_STRUCT
{
NvU32 signature; // 00h: Signature, the string "NPDE"
NvU16 nvPciDataExtRev; // 04h: NVIDIA PCI Data Extension Revision
NvU16 nvPciDataExtLen; // 06h: NVIDIA PCI Data Extension Length
NvU16 subimageLen; // 08h: Sub-image Length
NvU8 privLastImage; // 0Ah: Private Last Image Indicator
NvU8 flags; // 0Bh: Private images enabled if bit0=1
} NV_PCI_DATA_EXT_STRUCT, *PNV_PCI_DATA_EXT_STRUCT;
#pragma pack()
#endif // PCIEXPTBL_H
+4 -107
View File
@@ -3,21 +3,8 @@ from tinygrad.tensor import _to_np_dtype
from tinygrad.nn.onnx import OnnxRunner, OnnxValue
import numpy as np
import onnxruntime as ort
ort_options = ort.SessionOptions()
ort_options.log_severity_level = 3
def get_example_inputs(graph_inputs:dict[str, OnnxValue], config={}):
"""
Generate example input tensors based on the provided ONNX graph input specifications.
NOTE: This is not guaranteed to be reliable. It's a best-effort helper
that uses heuristics to guess input shapes and values.
Example:
from tinygrad.nn.onnx import OnnxRunner
from extra.onnx_helpers import get_example_inputs
inputs = get_example_inputs(OnnxRunner(model_path).graph_inputs)
"""
def _get_shape(onnx_shape: tuple[str|int]):
shape = []
for onnx_dim in onnx_shape:
@@ -57,9 +44,11 @@ def get_example_inputs(graph_inputs:dict[str, OnnxValue], config={}):
ret.update({name:value})
return ret
def _get_tinygrad_and_ort_np_outputs(onnx_file, inputs):
def validate(onnx_file, inputs, rtol=1e-5, atol=1e-5):
run_onnx = OnnxRunner(onnx_file)
ort_options = ort.SessionOptions()
ort_options.log_severity_level = 3
ort_sess = ort.InferenceSession(onnx_file, ort_options, ["CPUExecutionProvider"])
np_inputs = {k:v.numpy() if isinstance(v, Tensor) else v for k,v in inputs.items()}
out_names = list(run_onnx.graph_outputs)
@@ -67,101 +56,9 @@ def _get_tinygrad_and_ort_np_outputs(onnx_file, inputs):
ort_out = dict(zip(out_names, out_values))
tinygrad_out = run_onnx(inputs)
Tensor.realize(*(x for x in tinygrad_out.values() if x is not None))
tinygrad_out = {k:v.numpy() if v is not None else None for k,v in tinygrad_out.items()}
return tinygrad_out, ort_out
def validate(onnx_file, inputs, rtol=1e-5, atol=1e-5):
"""
Compares the final output tensors of an onnx model run in tinygrad and onnxruntime.
"""
tinygrad_out, ort_out = _get_tinygrad_and_ort_np_outputs(onnx_file, inputs)
assert tinygrad_out.keys() == ort_out.keys()
for k in tinygrad_out.keys():
tiny_v, onnx_v = tinygrad_out[k], ort_out[k]
if tiny_v is None: assert onnx_v is None, f"{k}: {tiny_v=}, {onnx_v=}"
else: np.testing.assert_allclose(tiny_v, onnx_v, rtol=rtol, atol=atol, err_msg=f"For tensor '{k}' in {tinygrad_out.keys()}")
def validate_all_intermediates(onnx_file, inputs, rtol=1e-5, atol=1e-5):
"""
Compares all intermediate node output of an onnx model run in tinygrad and onnxruntime.
"""
report = generate_node_output_report(onnx_file, inputs)
for i, node in enumerate(report):
node_name = node["node"]
op = node["op"]
outputs = node["outputs"]
for output in outputs:
output_name = output["name"]
tinygrad_out = output["tinygrad"]
ort_out = output["onnxruntime"]
try:
if tinygrad_out is None: assert ort_out is None, f"None outputs are not equal {tinygrad_out=} {ort_out=}"
else: np.testing.assert_allclose(tinygrad_out, ort_out, rtol=rtol, atol=atol)
print(f"Validated {i}: {op=} {node_name=} {output_name=}")
except AssertionError as e:
print(f"FAILED {i}: {op=} {node_name=} {output_name=}")
print(str(e).strip() + "\n")
def generate_node_output_report(onnx_file, inputs):
"""
Build a report of all ONNX node outputs from tinygrad and onnxruntime
Returns:
A list of dictionaries, where each entry corresponds to one
node in the ONNX graph. The structure is as follows:
[
{
"node": str, # The name of the ONNX node.
"op": str, # The operation type of the ONNX node.
"outputs": [
{
"name": str, # The name of the output tensor.
"tinygrad": np.ndarray | None, # The output value from tinygrad.
"onnxruntime": np.ndarray | None, # The output value from onnxruntime.
},
...
]
},
...
]
"""
import onnx_graphsurgeon as gs
import onnx
import tempfile
# rewrite the model to output all the node outputs
# `infer_shapes` here tries to fill the shapes and dtypes of intermediate values which graphsurgeon requires when assigning them as outputs
inferred_model = onnx.shape_inference.infer_shapes(onnx.load(onnx_file))
model = gs.import_onnx(inferred_model)
model_nodes = model.nodes
node_outputs = [n.outputs for n in model.nodes]
model.outputs = [
each_output for outputs in node_outputs for each_output in outputs
if not (each_output.dtype is None and each_output.shape is None) # output with None dtype and None shape is likely a `None` value
]
rewritten_model = gs.export_onnx(model)
# TODO: remove this once ORT supports 1.18.0
if getattr(rewritten_model, "ir_version", 0) > 10:
rewritten_model.ir_version = 10
with tempfile.NamedTemporaryFile(suffix=".onnx") as f:
onnx.save(rewritten_model, f.name)
rewritten_model_path = f.name
tinygrad_out, ort_out = _get_tinygrad_and_ort_np_outputs(rewritten_model_path, inputs)
report = []
for node in model_nodes:
outputs = []
for each_output in node.outputs:
if each_output.dtype is None and each_output.shape is None:
continue
name = each_output.name
tinygrad_output = tinygrad_out[name]
ort_output = ort_out[name]
outputs.append({"name": name, "tinygrad": tinygrad_output, "onnxruntime": ort_output})
report.append({"node": node.name, "op": node.op, "outputs": outputs})
return report
else: np.testing.assert_allclose(tiny_v.numpy(), onnx_v, rtol=rtol, atol=atol, err_msg=f"For tensor '{k}' in {tinygrad_out.keys()}")
+224 -293
View File
@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<database xmlns="http://nouveau.freedesktop.org/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<import file="freedreno_copyright.xml"/>
xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.xsd">
<import file="adreno/adreno_common.xml"/>
<enum name="vgt_event_type" varset="chip">
@@ -21,9 +20,9 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<value name="HLSQ_FLUSH" value="7" variants="A3XX-A4XX"/>
<value name="VIZQUERY_END" value="8" variants="A2XX"/>
<value name="SC_WAIT_WC" value="9" variants="A2XX"/>
<value name="WRITE_PRIMITIVE_COUNTS" value="9" variants="A6XX-"/>
<value name="START_PRIMITIVE_CTRS" value="11" variants="A6XX-"/>
<value name="STOP_PRIMITIVE_CTRS" value="12" variants="A6XX-"/>
<value name="WRITE_PRIMITIVE_COUNTS" value="9" variants="A6XX"/>
<value name="START_PRIMITIVE_CTRS" value="11" variants="A6XX"/>
<value name="STOP_PRIMITIVE_CTRS" value="12" variants="A6XX"/>
<!-- Not sure that these 4 events don't have the same meaning as on A5XX+ -->
<value name="RST_PIX_CNT" value="13" variants="A2XX-A4XX"/>
<value name="RST_VTX_CNT" value="14" variants="A2XX-A4XX"/>
@@ -31,8 +30,8 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<value name="STAT_EVENT" value="16" variants="A2XX-A4XX"/>
<value name="CACHE_FLUSH_AND_INV_TS_EVENT" value="20" variants="A2XX-A4XX"/>
<doc>
If A6XX_RB_SAMPLE_COUNTER_CNTL.copy is true, writes OQ Z passed
sample counts to RB_SAMPLE_COUNTER_BASE. This writes to main
If A6XX_RB_SAMPLE_COUNT_CONTROL.copy is true, writes OQ Z passed
sample counts to RB_SAMPLE_COUNT_ADDR. This writes to main
memory, skipping UCHE.
</doc>
<value name="ZPASS_DONE" value="21"/>
@@ -97,13 +96,6 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
</doc>
<value name="BLIT" value="30" variants="A5XX-"/>
<doc>
Flip between the primary and secondary LRZ buffers. This is used
for concurrent binning, so that BV can write to one buffer while
BR reads from the other.
</doc>
<value name="LRZ_FLIP_BUFFER" value="36" variants="A7XX-"/>
<doc>
Clears based on GRAS_LRZ_CNTL configuration, could clear
fast-clear buffer or LRZ direction.
@@ -120,12 +112,11 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<value name="LRZ_FLUSH" value="38" variants="A5XX-"/>
<value name="BLIT_OP_FILL_2D" value="39" variants="A5XX-"/>
<value name="BLIT_OP_COPY_2D" value="40" variants="A5XX-A6XX"/>
<value name="LRZ_CACHE_INVALIDATE" value="40" variants="A7XX-"/>
<value name="LRZ_Q_CACHE_INVALIDATE" value="41" variants="A7XX-"/>
<value name="UNK_40" value="40" variants="A7XX"/>
<value name="BLIT_OP_SCALE_2D" value="42" variants="A5XX-"/>
<value name="CONTEXT_DONE_2D" value="43" variants="A5XX-"/>
<value name="VSC_BINNING_START" value="44" variants="A5XX-"/>
<value name="VSC_BINNING_END" value="45" variants="A5XX-"/>
<value name="UNK_2C" value="44" variants="A5XX-"/>
<value name="UNK_2D" value="45" variants="A5XX-"/>
<!-- a6xx events -->
<doc>
@@ -138,22 +129,21 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<!-- note, some of these are the same as a6xx, just named differently -->
<doc> Doesn't seem to do anything </doc>
<value name="DUMMY_EVENT" value="1" variants="A7XX-"/>
<value name="CCU_INVALIDATE_DEPTH" value="24" variants="A7XX-"/>
<value name="CCU_INVALIDATE_COLOR" value="25" variants="A7XX-"/>
<value name="CCU_RESOLVE_CLEAN" value="26" variants="A7XX-"/>
<value name="CCU_FLUSH_DEPTH" value="28" variants="A7XX-"/>
<value name="CCU_FLUSH_COLOR" value="29" variants="A7XX-"/>
<value name="CCU_RESOLVE" value="30" variants="A7XX-"/>
<value name="CCU_END_RESOLVE_GROUP" value="31" variants="A7XX-"/>
<value name="CCU_CLEAN_DEPTH" value="32" variants="A7XX-"/>
<value name="CCU_CLEAN_COLOR" value="33" variants="A7XX-"/>
<value name="CACHE_RESET" value="48" variants="A7XX-"/>
<value name="CACHE_CLEAN" value="49" variants="A7XX-"/>
<value name="DUMMY_EVENT" value="1" variants="A7XX"/>
<value name="CCU_INVALIDATE_DEPTH" value="24" variants="A7XX"/>
<value name="CCU_INVALIDATE_COLOR" value="25" variants="A7XX"/>
<value name="CCU_RESOLVE_CLEAN" value="26" variants="A7XX"/>
<value name="CCU_FLUSH_DEPTH" value="28" variants="A7XX"/>
<value name="CCU_FLUSH_COLOR" value="29" variants="A7XX"/>
<value name="CCU_RESOLVE" value="30" variants="A7XX"/>
<value name="CCU_END_RESOLVE_GROUP" value="31" variants="A7XX"/>
<value name="CCU_CLEAN_DEPTH" value="32" variants="A7XX"/>
<value name="CCU_CLEAN_COLOR" value="33" variants="A7XX"/>
<value name="CACHE_RESET" value="48" variants="A7XX"/>
<value name="CACHE_CLEAN" value="49" variants="A7XX"/>
<!-- TODO: deal with name conflicts with other gens -->
<value name="CACHE_FLUSH7" value="50" variants="A7XX-"/>
<value name="CACHE_INVALIDATE7" value="51" variants="A7XX-"/>
<value name="DEPTH_BUFFER_FLIP" value="0x3d" variants="A8XX-"/>
<value name="CACHE_FLUSH7" value="50" variants="A7XX"/>
<value name="CACHE_INVALIDATE7" value="51" variants="A7XX"/>
</enum>
<enum name="pc_di_primtype">
@@ -334,7 +324,7 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<doc>fetch state sub-blocks and initiate shader code DMAs</doc>
<value name="CP_SET_STATE" value="0x25"/>
<doc>load constant into chip and to memory</doc>
<value name="CP_SET_CONSTANT" value="0x2d" variants="A2XX"/>
<value name="CP_SET_CONSTANT" value="0x2d"/>
<doc>load sequencer instruction memory (pointer-based)</doc>
<value name="CP_IM_LOAD" value="0x27"/>
<doc>load sequencer instruction memory (code embedded in packet)</doc>
@@ -381,7 +371,7 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<value name="CP_LOAD_STATE" value="0x30" variants="A3XX"/>
<value name="CP_LOAD_STATE4" value="0x30" variants="A4XX-A5XX"/>
<doc>Conditionally load a IB based on a flag, prefetch enabled</doc>
<value name="CP_COND_INDIRECT_BUFFER_PFE" value="0x3a" variants="A3XX-A5XX"/>
<value name="CP_COND_INDIRECT_BUFFER_PFE" value="0x3a"/>
<doc>Conditionally load a IB based on a flag, prefetch disabled</doc>
<value name="CP_COND_INDIRECT_BUFFER_PFD" value="0x32" variants="A3XX"/>
<doc>Load a buffer with pre-fetch enabled</doc>
@@ -524,7 +514,7 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<!--
Seems to set the mode flags which control which CP_SET_DRAW_STATE
packets are executed, based on their ENABLE_MASK values
CP_SET_MODE w/ payload of 0x1 seems to cause CP_SET_DRAW_STATE
packets w/ ENABLE_MASK & 0x6 to execute immediately
-->
@@ -547,7 +537,7 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<value name="CP_LOAD_STATE6_GEOM" value="0x32" variants="A6XX-"/>
<value name="CP_LOAD_STATE6_FRAG" value="0x34" variants="A6XX-"/>
<!--
Note: For UAV state (Image/SSBOs) which have shared state across
Note: For IBO state (Image/SSBOs) which have shared state across
shader stages, for 3d pipeline CP_LOAD_STATE6 is used. But for
compute shaders, CP_LOAD_STATE6_FRAG is used. Possibly they are
interchangable.
@@ -576,21 +566,20 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<value name="IN_PREEMPT" value="0x0f" variants="A6XX-"/>
<!-- TODO do these exist on A5xx? -->
<value name="CP_SCRATCH_WRITE" value="0x4c" variants="A6XX-"/>
<value name="CP_SCRATCH_WRITE" value="0x4c" variants="A6XX"/>
<value name="CP_REG_TO_MEM_OFFSET_MEM" value="0x74" variants="A6XX-"/>
<value name="CP_REG_TO_MEM_OFFSET_REG" value="0x72" variants="A6XX-"/>
<value name="CP_WAIT_MEM_GTE" value="0x14" variants="A6XX"/>
<value name="CP_WAIT_TWO_REGS" value="0x70" variants="A6XX"/>
<value name="CP_MEMCPY" value="0x75" variants="A6XX-"/>
<value name="CP_SET_BIN_DATA5_OFFSET" value="0x2e" variants="A6XX-"/>
<!-- A750+, set in place of CP_SET_BIN_DATA5_OFFSET but has different values -->
<value name="CP_SET_UNK_BIN_DATA" value="0x2d" variants="A7XX-"/>
<doc>
Write CP_CONTEXT_SWITCH_*_INFO from CP to the following dwords,
and forcibly switch to the indicated context.
</doc>
<value name="CP_CONTEXT_SWITCH" value="0x54" variants="A6XX"/>
<value name="CP_SET_AMBLE" value="0x55" variants="A6XX-"/>
<!-- Note, kgsl calls this CP_SET_AMBLE: -->
<value name="CP_SET_CTXSWITCH_IB" value="0x55" variants="A6XX-"/>
<!--
Seems to always have the payload:
@@ -641,7 +630,8 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<value name="CP_BV_BR_COUNT_OPS" value="0x1b" variants="A7XX-"/>
<doc> Clears, adds to local, or adds to global timestamp </doc>
<value name="CP_MODIFY_TIMESTAMP" value="0x1c" variants="A7XX-"/>
<value name="CP_NON_CONTEXT_REG_BUNCH" value="0x5d" variants="A7XX-"/>
<!-- similar to CP_CONTEXT_REG_BUNCH, but discards first two dwords?? -->
<value name="CP_CONTEXT_REG_BUNCH2" value="0x5d" variants="A7XX-"/>
<doc>
Write to a scratch memory that is read by CP_REG_TEST with
SOURCE_SCRATCH_MEM set. It's not the same scratch as scratch registers.
@@ -658,11 +648,6 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<doc>Reset various on-chip state used for synchronization</doc>
<value name="CP_RESET_CONTEXT_STATE" value="0x1f" variants="A7XX-"/>
<doc>Invalidates the "CCHE" introduced on a740</doc>
<value name="CP_CCHE_INVALIDATE" value="0x3a" variants="A7XX-"/>
<value name="CP_SCOPE_CNTL" value="0x6c" variants="A7XX-"/>
</enum>
@@ -805,14 +790,14 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<value name="SB6_GS_SHADER" value="0xb"/>
<value name="SB6_FS_SHADER" value="0xc"/>
<value name="SB6_CS_SHADER" value="0xd"/>
<value name="SB6_UAV" value="0xe"/>
<value name="SB6_CS_UAV" value="0xf"/>
<value name="SB6_IBO" value="0xe"/>
<value name="SB6_CS_IBO" value="0xf"/>
</enum>
<enum name="a6xx_state_type">
<value name="ST6_SHADER" value="0"/>
<value name="ST6_CONSTANTS" value="1"/>
<value name="ST6_UBO" value="2"/>
<value name="ST6_UAV" value="3"/>
<value name="ST6_IBO" value="3"/>
</enum>
<enum name="a6xx_state_src">
<value name="SS6_DIRECT" value="0"/>
@@ -918,6 +903,12 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
</reg32>
<stripe varset="chip" variants="A5XX-">
<reg32 offset="4" name="4">
<bitfield name="INDX_BASE_LO" low="0" high="31"/>
</reg32>
<reg32 offset="5" name="5">
<bitfield name="INDX_BASE_HI" low="0" high="31"/>
</reg32>
<reg64 offset="4" name="INDX_BASE" type="address"/>
<reg32 offset="6" name="6">
<!-- max # of elements in index buffer -->
@@ -1093,10 +1084,8 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<bitfield name="BINNING" pos="20" varset="chip" variants="A6XX-" type="boolean"/>
<bitfield name="GMEM" pos="21" varset="chip" variants="A6XX-" type="boolean"/>
<bitfield name="SYSMEM" pos="22" varset="chip" variants="A6XX-" type="boolean"/>
<!-- high bit is 28 until a750: -->
<bitfield name="GROUP_ID" low="24" high="29" type="uint"/>
<bitfield name="GROUP_ID" low="24" high="28" type="uint"/>
</reg32>
<reg64 offset="1" name="ADDR" type="address"/>
<reg32 offset="1" name="1">
<bitfield name="ADDR_LO" low="0" high="31" type="hex"/>
</reg32>
@@ -1130,63 +1119,39 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
</reg32>
</domain>
<enum name="a7xx_abs_mask_mode">
<value name="ABS_MASK" value="0x1"/>
<value name="NO_ABS_MASK" value="0x0"/>
</enum>
<domain name="CP_SET_BIN_DATA5" width="32">
<reg32 offset="0" name="0">
<bitfield name="VSC_MASK" low="0" high="15" type="hex">
<doc>
A mask of bins, starting at VSC_N, whose
visibility is OR'd together. A value of 0 is
interpreted as 1 (i.e. just use VSC_N for
visbility) for backwards compatibility. Only
exists on a7xx.
</doc>
</bitfield>
<!-- equiv to PC_VSTREAM_CONTROL.SIZE on a3xx/a4xx: -->
<bitfield name="VSC_SIZE" low="16" high="21" type="uint"/>
<!-- equiv to PC_VSTREAM_CONTROL.N on a3xx/a4xx: -->
<bitfield name="VSC_N" low="22" high="26" type="uint"/>
<bitfield name="ABS_MASK" pos="28" type="a7xx_abs_mask_mode" addvariant="yes">
<doc>
If this field is 1, VSC_MASK and VSC_N are
ignored and instead a new ordinal immediately
after specifies the full 32-bit mask of bins
to use. The mask is "absolute" instead of
relative to VSC_N.
</doc>
</bitfield>
</reg32>
<stripe varset="a7xx_abs_mask_mode" variants="NO_ABS_MASK">
<!-- BIN_DATA_ADDR -> VSC_PIPE[p].DATA_ADDRESS -->
<reg64 offset="1" name="BIN_DATA_ADDR" type="address"/>
<!-- BIN_SIZE_ADDRESS -> VSC_SIZE_ADDRESS + (p * 4)-->
<reg64 offset="3" name="BIN_SIZE_ADDR" type="address"/>
<!-- new on a6xx, where BIN_DATA_ADDR is the DRAW_STRM: -->
<reg64 offset="5" name="BIN_PRIM_STRM" type="address"/>
<!--
a7xx adds a few more addresses to the end of the pkt
-->
<reg64 offset="7" name="7"/>
<reg64 offset="9" name="9"/>
</stripe>
<stripe varset="a7xx_abs_mask_mode" variants="ABS_MASK">
<reg32 offset="1" name="ABS_MASK"/>
<!-- BIN_DATA_ADDR -> VSC_PIPE[p].DATA_ADDRESS -->
<reg64 offset="2" name="BIN_DATA_ADDR" type="address"/>
<!-- BIN_SIZE_ADDRESS -> VSC_SIZE_ADDRESS + (p * 4)-->
<reg64 offset="4" name="BIN_SIZE_ADDR" type="address"/>
<!-- new on a6xx, where BIN_DATA_ADDR is the DRAW_STRM: -->
<reg64 offset="6" name="BIN_PRIM_STRM" type="address"/>
<!--
a7xx adds a few more addresses to the end of the pkt
-->
<reg64 offset="8" name="8"/>
<reg64 offset="10" name="10"/>
</stripe>
<!-- BIN_DATA_ADDR -> VSC_PIPE[p].DATA_ADDRESS -->
<reg32 offset="1" name="1">
<bitfield name="BIN_DATA_ADDR_LO" low="0" high="31" type="hex"/>
</reg32>
<reg32 offset="2" name="2">
<bitfield name="BIN_DATA_ADDR_HI" low="0" high="31" type="hex"/>
</reg32>
<!-- BIN_SIZE_ADDRESS -> VSC_SIZE_ADDRESS + (p * 4)-->
<reg32 offset="3" name="3">
<bitfield name="BIN_SIZE_ADDRESS_LO" low="0" high="31"/>
</reg32>
<reg32 offset="4" name="4">
<bitfield name="BIN_SIZE_ADDRESS_HI" low="0" high="31"/>
</reg32>
<!-- new on a6xx, where BIN_DATA_ADDR is the DRAW_STRM: -->
<reg32 offset="5" name="5">
<bitfield name="BIN_PRIM_STRM_LO" low="0" high="31"/>
</reg32>
<reg32 offset="6" name="6">
<bitfield name="BIN_PRIM_STRM_HI" low="0" high="31"/>
</reg32>
<!--
a7xx adds a few more addresses to the end of the pkt
-->
<reg64 offset="7" name="7"/>
<reg64 offset="9" name="9"/>
</domain>
<domain name="CP_SET_BIN_DATA5_OFFSET" width="32">
@@ -1197,42 +1162,23 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
stream is recorded.
</doc>
<reg32 offset="0" name="0">
<bitfield name="VSC_MASK" low="0" high="15" type="hex"/>
<!-- equiv to PC_VSTREAM_CONTROL.SIZE on a3xx/a4xx: -->
<bitfield name="VSC_SIZE" low="16" high="21" type="uint"/>
<!-- equiv to PC_VSTREAM_CONTROL.N on a3xx/a4xx: -->
<bitfield name="VSC_N" low="22" high="26" type="uint"/>
<bitfield name="ABS_MASK" pos="28" type="a7xx_abs_mask_mode" addvariant="yes"/>
</reg32>
<stripe varset="a7xx_abs_mask_mode" variants="NO_ABS_MASK">
<!-- BIN_DATA_ADDR -> VSC_PIPE[p].DATA_ADDRESS -->
<reg32 offset="1" name="1">
<bitfield name="BIN_DATA_OFFSET" low="0" high="31" type="uint"/>
</reg32>
<!-- BIN_SIZE_ADDRESS -> VSC_SIZE_ADDRESS + (p * 4)-->
<reg32 offset="2" name="2">
<bitfield name="BIN_SIZE_OFFSET" low="0" high="31" type="uint"/>
</reg32>
<!-- BIN_DATA2_ADDR -> VSC_PIPE[p].DATA2_ADDRESS -->
<reg32 offset="3" name="3">
<bitfield name="BIN_DATA2_OFFSET" low="0" high="31" type="uint"/>
</reg32>
</stripe>
<stripe varset="a7xx_abs_mask_mode" variants="ABS_MASK">
<reg32 offset="1" name="ABS_MASK"/>
<!-- BIN_DATA_ADDR -> VSC_PIPE[p].DATA_ADDRESS -->
<reg32 offset="2" name="2">
<bitfield name="BIN_DATA_OFFSET" low="0" high="31" type="uint"/>
</reg32>
<!-- BIN_SIZE_ADDRESS -> VSC_SIZE_ADDRESS + (p * 4)-->
<reg32 offset="3" name="3">
<bitfield name="BIN_SIZE_OFFSET" low="0" high="31" type="uint"/>
</reg32>
<!-- BIN_DATA2_ADDR -> VSC_PIPE[p].DATA2_ADDRESS -->
<reg32 offset="4" name="4">
<bitfield name="BIN_DATA2_OFFSET" low="0" high="31" type="uint"/>
</reg32>
</stripe>
<!-- BIN_DATA_ADDR -> VSC_PIPE[p].DATA_ADDRESS -->
<reg32 offset="1" name="1">
<bitfield name="BIN_DATA_OFFSET" low="0" high="31" type="uint"/>
</reg32>
<!-- BIN_SIZE_ADDRESS -> VSC_SIZE_ADDRESS + (p * 4)-->
<reg32 offset="2" name="2">
<bitfield name="BIN_SIZE_OFFSET" low="0" high="31" type="uint"/>
</reg32>
<!-- BIN_DATA2_ADDR -> VSC_PIPE[p].DATA2_ADDRESS -->
<reg32 offset="3" name="3">
<bitfield name="BIN_DATA2_OFFSET" low="0" high="31" type="uint"/>
</reg32>
</domain>
<domain name="CP_REG_RMW" width="32">
@@ -1250,9 +1196,6 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
</doc>
<reg32 offset="0" name="0">
<bitfield name="DST_REG" low="0" high="17" type="hex"/>
<bitfield name="DST_SCRATCH" pos="19" type="boolean" varset="chip" variants="A7XX-"/>
<!-- skip implied CP_WAIT_FOR_IDLE + CP_WAIT_FOR_ME -->
<bitfield name="SKIP_WAIT_FOR_ME" pos="23" type="boolean" varset="chip" variants="A7XX-"/>
<bitfield name="ROTATE" low="24" high="28" type="uint"/>
<bitfield name="SRC1_ADD" pos="29" type="boolean"/>
<bitfield name="SRC1_IS_REG" pos="30" type="boolean"/>
@@ -1266,7 +1209,7 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
</reg32>
</domain>
<domain name="CP_REG_TO_MEM" width="32" prefix="chip">
<domain name="CP_REG_TO_MEM" width="32">
<reg32 offset="0" name="0">
<bitfield name="REG" low="0" high="17" type="hex"/>
<!-- number of registers/dwords copied is max(CNT, 1). -->
@@ -1274,12 +1217,12 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<bitfield name="64B" pos="30" type="boolean"/>
<bitfield name="ACCUMULATE" pos="31" type="boolean"/>
</reg32>
<stripe varset="chip" variants="A2XX-A4XX">
<reg32 offset="1" name="DEST" type="address"/>
</stripe>
<stripe varset="chip" variants="A5XX-">
<reg64 offset="1" name="DEST" type="address"/>
</stripe>
<reg32 offset="1" name="1">
<bitfield name="DEST" low="0" high="31"/>
</reg32>
<reg32 offset="2" name="2" varset="chip" variants="A5XX-">
<bitfield name="DEST_HI" low="0" high="31"/>
</reg32>
</domain>
<domain name="CP_REG_TO_MEM_OFFSET_REG" width="32">
@@ -1295,7 +1238,12 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<bitfield name="64B" pos="30" type="boolean"/>
<bitfield name="ACCUMULATE" pos="31" type="boolean"/>
</reg32>
<reg64 offset="1" name="DEST" type="waddress"/>
<reg32 offset="1" name="1">
<bitfield name="DEST" low="0" high="31"/>
</reg32>
<reg32 offset="2" name="2" varset="chip" variants="A5XX-">
<bitfield name="DEST_HI" low="0" high="31"/>
</reg32>
<reg32 offset="3" name="3">
<bitfield name="OFFSET0" low="0" high="17" type="hex"/>
<bitfield name="OFFSET0_SCRATCH" pos="19" type="boolean"/>
@@ -1315,8 +1263,18 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<bitfield name="64B" pos="30" type="boolean"/>
<bitfield name="ACCUMULATE" pos="31" type="boolean"/>
</reg32>
<reg64 offset="1" name="DEST" type="waddress"/>
<reg64 offset="3" name="OFFSET" type="waddress"/>
<reg32 offset="1" name="1">
<bitfield name="DEST" low="0" high="31"/>
</reg32>
<reg32 offset="2" name="2" varset="chip" variants="A5XX-">
<bitfield name="DEST_HI" low="0" high="31"/>
</reg32>
<reg32 offset="3" name="3">
<bitfield name="OFFSET_LO" low="0" high="31" type="hex"/>
</reg32>
<reg32 offset="4" name="4">
<bitfield name="OFFSET_HI" low="0" high="31" type="hex"/>
</reg32>
</domain>
<domain name="CP_MEM_TO_REG" width="32">
@@ -1329,12 +1287,12 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<!-- does the same thing as CP_MEM_TO_MEM::UNK31 -->
<bitfield name="UNK31" pos="31" type="boolean"/>
</reg32>
<stripe varset="chip" variants="A2XX-A4XX">
<reg32 offset="1" name="SRC" type="address"/>
</stripe>
<stripe varset="chip" variants="A5XX-">
<reg64 offset="1" name="SRC" type="address"/>
</stripe>
<reg32 offset="1" name="1">
<bitfield name="SRC" low="0" high="31"/>
</reg32>
<reg32 offset="2" name="2" varset="chip" variants="A5XX-">
<bitfield name="SRC_HI" low="0" high="31"/>
</reg32>
</domain>
<domain name="CP_MEM_TO_MEM" width="32">
@@ -1354,10 +1312,6 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<!-- some other kind of wait -->
<bitfield name="UNK31" pos="31" type="boolean"/>
</reg32>
<reg64 offset="1" name="DST" type="waddress"/>
<reg64 offset="3" name="SRC_A" type="address"/>
<reg64 offset="5" name="SRC_B" type="address"/>
<reg64 offset="7" name="SRC_C" type="address"/>
<!--
followed by sequence of addresses.. the first is the
destination and the rest are N src addresses which are
@@ -1392,8 +1346,6 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<bitfield name="SCRATCH" low="20" high="22" type="uint"/>
<!-- number of registers/dwords copied is CNT + 1. -->
<bitfield name="CNT" low="24" high="26" type="uint"/>
<!-- skip implied CP_WAIT_FOR_IDLE + CP_WAIT_FOR_ME -->
<bitfield name="SKIP_WAIT_FOR_ME" pos="27" type="boolean" varset="chip" variants="A7XX-"/>
</reg32>
</domain>
@@ -1416,12 +1368,12 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
</domain>
<domain name="CP_MEM_WRITE" width="32">
<stripe varset="chip" variants="A2XX-A4XX">
<reg32 offset="0" name="ADDR" type="address"/>
</stripe>
<stripe varset="chip" variants="A5XX-">
<reg64 offset="0" name="ADDR" type="address"/>
</stripe>
<reg32 offset="0" name="0">
<bitfield name="ADDR_LO" low="0" high="31"/>
</reg32>
<reg32 offset="1" name="1">
<bitfield name="ADDR_HI" low="0" high="31"/>
</reg32>
<!-- followed by the DWORDs to write -->
</domain>
@@ -1473,14 +1425,24 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<bitfield name="POLL" low="4" high="5" type="poll_memory_type"/>
<bitfield name="WRITE_MEMORY" pos="8" type="boolean"/>
</reg32>
<reg64 offset="1" name="POLL_ADDR" type="address"/>
<reg32 offset="1" name="1">
<bitfield name="POLL_ADDR_LO" low="0" high="31" type="hex"/>
</reg32>
<reg32 offset="2" name="2">
<bitfield name="POLL_ADDR_HI" low="0" high="31" type="hex"/>
</reg32>
<reg32 offset="3" name="3">
<bitfield name="REF" low="0" high="31"/>
</reg32>
<reg32 offset="4" name="4">
<bitfield name="MASK" low="0" high="31"/>
</reg32>
<reg64 offset="5" name="WRITE_ADDR" type="waddress"/>
<reg32 offset="5" name="5">
<bitfield name="WRITE_ADDR_LO" low="0" high="31" type="hex"/>
</reg32>
<reg32 offset="6" name="6">
<bitfield name="WRITE_ADDR_HI" low="0" high="31" type="hex"/>
</reg32>
<reg32 offset="7" name="7">
<bitfield name="WRITE_DATA" low="0" high="31"/>
</reg32>
@@ -1495,7 +1457,12 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<!-- Reserved for flags, presumably? Unused in FW -->
<bitfield name="RESERVED" low="0" high="31" type="hex"/>
</reg32>
<reg64 offset="1" name="POLL_ADDR" type="address"/>
<reg32 offset="1" name="1">
<bitfield name="POLL_ADDR_LO" low="0" high="31" type="hex"/>
</reg32>
<reg32 offset="2" name="2">
<bitfield name="POLL_ADDR_HI" low="0" high="31" type="hex"/>
</reg32>
<reg32 offset="3" name="3">
<bitfield name="REF" low="0" high="31"/>
</reg32>
@@ -1513,7 +1480,12 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<bitfield name="POLL" low="4" high="5" type="poll_memory_type"/>
<bitfield name="WRITE_MEMORY" pos="8" type="boolean"/>
</reg32>
<reg64 offset="1" name="POLL_ADDR" type="address"/>
<reg32 offset="1" name="1">
<bitfield name="POLL_ADDR_LO" low="0" high="31" type="hex"/>
</reg32>
<reg32 offset="2" name="2">
<bitfield name="POLL_ADDR_HI" low="0" high="31" type="hex"/>
</reg32>
<reg32 offset="3" name="3">
<bitfield name="REF" low="0" high="31"/>
</reg32>
@@ -1647,7 +1619,12 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
TODO what is gpuaddr for, seems to be all 0's.. maybe needed for
context switch?
-->
<reg64 offset="1" name="ADDR" type="waddress"/>
<reg32 offset="1" name="1">
<bitfield name="ADDR_0_LO" low="0" high="31"/>
</reg32>
<reg32 offset="2" name="2">
<bitfield name="ADDR_0_HI" low="0" high="31"/>
</reg32>
<reg32 offset="3" name="3">
<!-- ??? -->
</reg32>
@@ -1676,8 +1653,8 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<bitfield name="WRITE_SAMPLE_COUNT" pos="12" type="boolean"/>
<!-- Write sample count at (iova + 16) -->
<bitfield name="SAMPLE_COUNT_END_OFFSET" pos="13" type="boolean"/>
<!-- *(iova + 8) += *(iova + 16) - *iova -->
<bitfield name="WRITE_ACCUM_SAMPLE_COUNT_DIFF" pos="14" type="boolean"/>
<!-- *(iova + 8) = *(iova + 16) - *iova -->
<bitfield name="WRITE_SAMPLE_COUNT_DIFF" pos="14" type="boolean"/>
<!-- Next 4 flags are valid to set only when concurrent binning is enabled -->
<!-- Increment 16b BV counter. Valid only in BV pipe -->
@@ -1691,11 +1668,15 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<bitfield name="WRITE_DST" pos="24" type="event_write_dst" addvariant="yes"/>
<!-- Writes into WRITE_DST from WRITE_SRC. RB_DONE_TS requires WRITE_ENABLED. -->
<bitfield name="WRITE_ENABLED" pos="27" type="boolean"/>
<bitfield name="IRQ" pos="31" type="boolean"/>
</reg32>
<stripe varset="event_write_dst" variants="EV_DST_RAM">
<reg64 offset="1" name="1" type="waddress"/>
<reg32 offset="1" name="1">
<bitfield name="ADDR_0_LO" low="0" high="31"/>
</reg32>
<reg32 offset="2" name="2">
<bitfield name="ADDR_0_HI" low="0" high="31"/>
</reg32>
<reg32 offset="3" name="3">
<bitfield name="PAYLOAD_0" low="0" high="31"/>
</reg32>
@@ -1762,7 +1743,9 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<reg32 offset="0" name="0">
</reg32>
<stripe varset="chip" variants="A4XX">
<reg32 offset="1" name="ADDR" type="address"/>
<reg32 offset="1" name="1">
<bitfield name="ADDR" low="0" high="31"/>
</reg32>
<reg32 offset="2" name="2">
<!-- localsize is value minus one: -->
<bitfield name="LOCALSIZEX" low="2" high="11" type="uint"/>
@@ -1771,7 +1754,12 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
</reg32>
</stripe>
<stripe varset="chip" variants="A5XX-">
<reg64 offset="1" name="ADDR" type="address"/>
<reg32 offset="1" name="1">
<bitfield name="ADDR_LO" low="0" high="31"/>
</reg32>
<reg32 offset="2" name="2">
<bitfield name="ADDR_HI" low="0" high="31"/>
</reg32>
<reg32 offset="3" name="3">
<!-- localsize is value minus one: -->
<bitfield name="LOCALSIZEX" low="2" high="11" type="uint"/>
@@ -1783,88 +1771,40 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<domain name="CP_SET_MARKER" width="32" varset="chip" prefix="chip" variants="A6XX-">
<doc>Tell CP the current operation mode, indicates save and restore procedure</doc>
<enum name="set_marker_mode">
<value value="0" name="SET_RENDER_MODE"/>
<!-- IFPC - inter-frame power collapse -->
<value value="1" name="SET_IFPC_MODE"/>
</enum>
<enum name="a6xx_ifpc_mode">
<value value="0" name="IFPC_ENABLE"/>
<value value="1" name="IFPC_DISABLE"/>
</enum>
<enum name="a6xx_marker">
<value value="1" name="RM6_DIRECT_RENDER"/>
<value value="2" name="RM6_BIN_VISIBILITY"/>
<value value="3" name="RM6_BIN_DIRECT"/>
<value value="4" name="RM6_BIN_RENDER_START"/>
<value value="5" name="RM6_BIN_END_OF_DRAWS"/>
<value value="6" name="RM6_BIN_RESOLVE"/>
<value value="7" name="RM6_BIN_RENDER_END"/>
<value value="1" name="RM6_BYPASS"/>
<value value="2" name="RM6_BINNING"/>
<value value="4" name="RM6_GMEM"/>
<value value="5" name="RM6_ENDVIS"/>
<value value="6" name="RM6_RESOLVE"/>
<value value="7" name="RM6_YIELD"/>
<value value="8" name="RM6_COMPUTE"/>
<value value="12" name="RM6_BLIT2DSCALE"/> <!-- no-op (at least on current sqe fw) -->
<value value="0xc" name="RM6_BLIT2DSCALE"/> <!-- no-op (at least on current sqe fw) -->
<!--
These values come from a6xx_set_marker() in the
downstream kernel, and they can only be set by the kernel
-->
<value value="13" name="RM6_IB1LIST_START"/>
<value value="14" name="RM6_IB1LIST_END"/>
<value value="15" name="RM7_BIN_VISIBILITY_END"/>
<!-- new in a8xx: -->
<value value="32" name="RM8_DEPTH_PASS_START"/>
<value value="33" name="RM8_DEPTH_PASS_END"/>
<value value="0xd" name="RM6_IB1LIST_START"/>
<value value="0xe" name="RM6_IB1LIST_END"/>
<!-- IFPC - inter-frame power collapse -->
<value value="0x100" name="RM6_IFPC_ENABLE"/>
<value value="0x101" name="RM6_IFPC_DISABLE"/>
</enum>
<stripe varset="chip" variants="A6XX-A7XX">
<reg32 offset="0" name="0">
<!-- if b8 is set, the low bits are interpreted differently (and b4 ignored) -->
<bitfield name="MARKER_MODE" pos="8" type="set_marker_mode" addvariant="yes"/>
<bitfield name="MODE" low="0" high="3" type="a6xx_marker" varset="set_marker_mode" variants="SET_RENDER_MODE"/>
<!-- used by preemption to determine if GMEM needs to be saved or not -->
<bitfield name="USES_GMEM" pos="4" type="boolean" varset="set_marker_mode" variants="SET_RENDER_MODE"/>
<bitfield name="IFPC_MODE" pos="0" type="a6xx_ifpc_mode" varset="set_marker_mode" variants="SET_IFPC_MODE"/>
<!--
CP_SET_MARKER is used with these bits to create a
critical section around a workaround for ray tracing.
The workaround happens after BVH building, and appears
to invalidate the RTU's BVH node cache. It makes sure
that only one of BR/BV/LPAC is executing the
workaround at a time, and no draws using RT on BV/LPAC
are executing while the workaround is executed on BR (or
vice versa, that no draws on BV/BR using RT are executed
while the workaround executes on LPAC), by
hooking subsequent CP_EVENT_WRITE/CP_DRAW_*/CP_EXEC_CS.
The blob usage is:
CP_SET_MARKER(RT_WA_START)
... workaround here ...
CP_SET_MARKER(RT_WA_END)
...
CP_SET_MARKER(SHADER_USES_RT)
CP_DRAW_INDX(...) or CP_EXEC_CS(...)
-->
<bitfield name="SHADER_USES_RT" pos="9" type="boolean" variants="A7XX-"/>
<bitfield name="RT_WA_START" pos="10" type="boolean" variants="A7XX-"/>
<bitfield name="RT_WA_END" pos="11" type="boolean" variants="A7XX-"/>
</reg32>
</stripe>
<stripe varset="chip" variants="A8XX-">
<reg32 offset="0" name="0">
<!-- if b8 is set, the low bits are interpreted differently (and b4 ignored) -->
<bitfield name="MARKER_MODE" pos="8" type="set_marker_mode" addvariant="yes"/>
<bitfield name="USES_GMEM" pos="7" type="boolean" varset="set_marker_mode" variants="SET_RENDER_MODE"/>
<bitfield name="MODE" low="0" high="6" type="a6xx_marker" varset="set_marker_mode" variants="SET_RENDER_MODE"/>
<bitfield name="IFPC_MODE" pos="0" type="a6xx_ifpc_mode" varset="set_marker_mode" variants="SET_IFPC_MODE"/>
<!-- idk if the RT w/a fields apply to a8xx as well -->
</reg32>
</stripe>
<reg32 offset="0" name="0">
<!--
NOTE: blob driver and some versions of freedreno/turnip set
b4, which is unused (at least by current sqe fw), but interferes
with parsing if we extend the size of the bitfield to include
b8 (only sent by kernel mode driver). Really, the way the
parsing works in the firmware, only b0-b3 are considered, but
if b8 is set, the low bits are interpreted differently. To
model this, without getting confused by spurious b4, this is
described as two overlapping bitfields:
-->
<bitfield name="MODE" low="0" high="8" type="a6xx_marker"/>
<bitfield name="MARKER" low="0" high="3" type="a6xx_marker"/>
</reg32>
</domain>
<domain name="CP_SET_PSEUDO_REG" width="32" varset="chip" prefix="chip" variants="A6XX-">
@@ -1890,9 +1830,9 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
If concurrent binning is disabled then BR also does binning so it will also
write the "real" registers in BR.
-->
<value value="8" name="VSC_PIPE_DATA_DRAW_BASE"/>
<value value="9" name="VSC_SIZE_BASE"/>
<value value="10" name="VSC_PIPE_DATA_PRIM_BASE"/>
<value value="8" name="DRAW_STRM_ADDRESS"/>
<value value="9" name="DRAW_STRM_SIZE_ADDRESS"/>
<value value="10" name="PRIM_STRM_ADDRESS"/>
<value value="11" name="UNK_STRM_ADDRESS"/>
<value value="12" name="UNK_STRM_SIZE_ADDRESS"/>
@@ -1993,11 +1933,11 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
a bitmask of which modes pass the test.
-->
<!-- RM6_BIN_VISIBILITY -->
<!-- RM6_BINNING -->
<bitfield name="BINNING" pos="25" variants="RENDER_MODE" type="boolean"/>
<!-- all others -->
<bitfield name="GMEM" pos="26" variants="RENDER_MODE" type="boolean"/>
<!-- RM6_DIRECT_RENDER -->
<!-- RM6_BYPASS -->
<bitfield name="SYSMEM" pos="27" variants="RENDER_MODE" type="boolean"/>
<bitfield name="BV" pos="25" variants="THREAD_MODE" type="boolean"/>
@@ -2070,45 +2010,54 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
</reg32>
</domain>
<domain name="CP_SET_AMBLE" width="32">
<domain name="CP_SET_CTXSWITCH_IB" width="32">
<doc>
Used by the userspace and kernel drivers to set various IB's
which are executed during context save/restore for handling
state that isn't restored by the context switch routine itself.
Used by the userspace driver to set various IB's which are
executed during context save/restore for handling
state that isn't restored by the
context switch routine itself.
</doc>
<enum name="amble_type">
<value name="PREAMBLE_AMBLE_TYPE" value="0">
<enum name="ctxswitch_ib">
<value name="RESTORE_IB" value="0">
<doc>Executed unconditionally when switching back to the context.</doc>
</value>
<value name="BIN_PREAMBLE_AMBLE_TYPE" value="1">
<value name="YIELD_RESTORE_IB" value="1">
<doc>
Executed when switching back after switching
away during execution of
a CP_SET_MARKER packet with RM6_BIN_RENDER_END as the
payload *and* skipsaverestore is set. This is
expected to restore static register values not
saved when skipsaverestore is set.
a CP_SET_MARKER packet with RM6_YIELD as the
payload *and* the normal save routine was
bypassed for a shorter one. I think this is
connected to the "skipsaverestore" bit set by
the kernel when preempting.
</doc>
</value>
<value name="POSTAMBLE_AMBLE_TYPE" value="2">
<value name="SAVE_IB" value="2">
<doc>
Executed when switching away from the context,
except for context switches initiated via
CP_YIELD.
</doc>
</value>
<value name="KMD_AMBLE_TYPE" value="3">
<value name="RB_SAVE_IB" value="3">
<doc>
This can only be set by the RB (i.e. the kernel)
and executes with protected mode off, but
is otherwise similar to POSTAMBLE_AMBLE_TYPE.
is otherwise similar to SAVE_IB.
Note, kgsl calls this CP_KMD_AMBLE_TYPE
</doc>
</value>
</enum>
<reg64 offset="0" name="ADDR" type="address"/>
<reg32 offset="0" name="0">
<bitfield name="ADDR_LO" low="0" high="31"/>
</reg32>
<reg32 offset="1" name="1">
<bitfield name="ADDR_HI" low="0" high="31"/>
</reg32>
<reg32 offset="2" name="2">
<bitfield name="DWORDS" low="0" high="19" type="uint"/>
<bitfield name="TYPE" low="20" high="21" type="amble_type"/>
<bitfield name="TYPE" low="20" high="21" type="ctxswitch_ib"/>
</reg32>
</domain>
@@ -2140,12 +2089,12 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<value name="UNK_EVENT_WRITE" value="0x4"/>
<doc>
Tracks GRAS_LRZ_CNTL::GREATER, GRAS_LRZ_CNTL::DIR, and
GRAS_LRZ_VIEW_INFO with previous values, and if one of
GRAS_LRZ_DEPTH_VIEW with previous values, and if one of
the following is true:
- GRAS_LRZ_CNTL::GREATER has changed
- GRAS_LRZ_CNTL::DIR has changed, the old value is not
CUR_DIR_GE, and the new value is not CUR_DIR_DISABLED
- GRAS_LRZ_VIEW_INFO has changed
- GRAS_LRZ_DEPTH_VIEW has changed
then it does a LRZ_FLUSH with GRAS_LRZ_CNTL::ENABLE
forced to 1.
Only exists in a650_sqe.fw.
@@ -2260,7 +2209,7 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<domain name="CP_MEM_TO_SCRATCH_MEM" width="32">
<doc>
Best guess is that it is a faster way to fetch all the VSC_CHANNEL_VISIBILITY registers
Best guess is that it is a faster way to fetch all the VSC_STATE registers
and keep them in a local scratch memory instead of fetching every time
when skipping IBs.
</doc>
@@ -2308,25 +2257,7 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<reg32 offset="0" name="0">
<bitfield name="CLEAR_ON_CHIP_TS" pos="0" type="boolean"/>
<bitfield name="CLEAR_RESOURCE_TABLE" pos="1" type="boolean"/>
<bitfield name="CLEAR_BV_BR_COUNTER" pos="2" type="boolean"/>
<bitfield name="RESET_GLOBAL_LOCAL_TS" pos="3" type="boolean"/>
</reg32>
</domain>
<domain name="CP_SCOPE_CNTL" width="32">
<enum name="cp_scope">
<value value="0" name="INTERRUPTS"/>
</enum>
<reg32 offset="0" name="0">
<bitfield name="DISABLE_PREEMPTION" pos="0" type="boolean"/>
<bitfield low="28" high="31" name="SCOPE" type="cp_scope"/>
</reg32>
</domain>
<domain name="CP_INDIRECT_BUFFER" width="32" varset="chip" prefix="chip" variants="A5XX-">
<reg64 offset="0" name="IB_BASE" type="address"/>
<reg32 offset="2" name="2">
<bitfield name="IB_SIZE" low="0" high="19"/>
<bitfield name="CLEAR_GLOBAL_LOCAL_TS" pos="2" type="boolean"/>
</reg32>
</domain>
-2
View File
@@ -10,8 +10,6 @@ import ctypes
class AsDictMixin:
import sys
if sys.version_info >= (3, 14): _layout_ = 'ms'
@classmethod
def as_dict(cls, self):
result = {}
+7 -16
View File
@@ -97,7 +97,7 @@ def parse_cmd_buf(dat):
if state_block == SB6_CS_SHADER:
from extra.disassemblers.adreno import disasm_raw
if state_type == ST6_SHADER and IOCTL > 3:
if state_type == ST6_SHADER and IOCTL > 2:
disasm_raw(get_mem(((vals[2] << 32) | vals[1]), num_unit * 128))
if state_type == ST6_CONSTANTS:
x = get_mem(((vals[2] << 32) | vals[1]), num_unit*4)
@@ -106,30 +106,25 @@ def parse_cmd_buf(dat):
print('constants')
hexdump(x)
if state_type == ST6_IBO:
if state_src == 0x1:
ibos_bytes = get_mem(CAPTURED_STATE['bindless_base'] + ((vals[2] << 32) | vals[1]) * 4, num_unit * 64)
else: ibos_bytes = get_mem((vals[2] << 32) | vals[1], num_unit * 16 * 4)
ibos_bytes = get_mem((vals[2] << 32) | vals[1], num_unit * 16 * 4)
CAPTURED_STATE['ibos'] = ibos_bytes[:]
if IOCTL > 1:
print('texture ibos')
hexdump(ibos_bytes)
elif state_block == SB6_CS_TEX:
if state_type == ST6_SHADER:
if state_src == 0x1:
samplers_bytes = get_mem(CAPTURED_STATE['bindless_base'] + ((vals[2] << 32) | vals[1]) * 4, num_unit * 64)
else: samplers_bytes = get_mem((vals[2] << 32) | vals[1], num_unit * 4 * 4)
samplers_bytes = get_mem((vals[2] << 32) | vals[1], num_unit * 4 * 4)
CAPTURED_STATE['samplers'] = samplers_bytes[:]
if IOCTL > 1:
print('texture samplers')
hexdump(samplers_bytes)
if state_type == ST6_CONSTANTS:
if state_src == 0x1:
descriptors_bytes = get_mem(CAPTURED_STATE['bindless_base'] + ((vals[2] << 32) | vals[1]) * 4, num_unit * 64)
else: descriptors_bytes = get_mem((vals[2] << 32) | vals[1], 1600)
descriptors_bytes = get_mem((vals[2] << 32) | vals[1], 1600)
CAPTURED_STATE['descriptors'] = descriptors_bytes[:]
if IOCTL > 1:
print('texture descriptors')
hexdump(descriptors_bytes)
elif ops[opcode] == "CP_REG_TO_MEM":
reg, cnt, b64, accum = vals[0] & 0x3FFFF, (vals[0] >> 18) & 0xFFF, (vals[0] >> 30) & 0x1, (vals[0] >> 31) & 0x1
dest = vals[1] | (vals[2] << 32)
@@ -157,10 +152,6 @@ def parse_cmd_buf(dat):
if IOCTL > 0:
print(f'THREADSIZE-{(vals[0] >> 20)&0x1}\nEARLYPREAMBLE-{(vals[0] >> 23) & 0x1}\nMERGEDREGS-{(vals[0] >> 3) & 0x1}\nTHREADMODE-{vals[0] & 0x1}\nHALFREGFOOTPRINT-{(vals[0] >> 1) & 0x3f}\nFULLREGFOOTPRINT-{(vals[0] >> 7) & 0x3f}\nBRANCHSTACK-{(vals[0] >> 14) & 0x3f}\n')
print(f'SP_CS_UNKNOWN_A9B1-{vals[1]}\nSP_CS_BRANCH_COND-{vals[2]}\nSP_CS_OBJ_FIRST_EXEC_OFFSET-{vals[3]}\nSP_CS_OBJ_START-{vals[4] | (vals[5] << 32)}\nSP_CS_PVT_MEM_PARAM-{vals[6]}\nSP_CS_PVT_MEM_ADDR-{vals[7] | (vals[8] << 32)}\nSP_CS_PVT_MEM_SIZE-{vals[9]}')
if offset == 0xa9e8:
CAPTURED_STATE['bindless_base'] = (vals[0] | (vals[1] << 32)) & ~0b11
# print(hex(CAPTURED_STATE['bindless_base']))
# hexdump(get_mem(CAPTURED_STATE['bindless_base'], 0x200))
if offset == 0xb180:
if IOCTL > 0:
print('border color offset', hex(vals[1] << 32 | vals[0]))
@@ -180,8 +171,8 @@ def ioctl(fd, request, argp):
name, stype = nrs[nr]
s = get_struct(argp, stype)
if IOCTL > 0: print(f"{ret:2d} = {name:40s}", ' '.join(format_struct(s)))
if name == "IOCTL_KGSL_GPUOBJ_INFO":
mmaped[s.gpuaddr] = mmap.mmap(fd, s.size, offset=s.id*0x1000)
if name == "IOCTL_KGSL_GPUOBJ_INFO": pass
# mmaped[s.gpuaddr] = mmap.mmap(fd, s.size, offset=s.id*0x1000)
if name == "IOCTL_KGSL_GPU_COMMAND":
for i in range(s.numcmds):
cmd = get_struct(s.cmdlist+ctypes.sizeof(msm_kgsl.struct_kgsl_command_object)*i, msm_kgsl.struct_kgsl_command_object)
+1 -10
View File
@@ -882,11 +882,6 @@ impl<'a> Thread<'a> {
let s1 = sign_ext((s1 & 0xffffff) as u64, 24) as i32;
(s0 * s1) as u32
}
10 => {
let s0 = sign_ext((s0 & 0xffffff) as u64, 24) as i64;
let s1 = sign_ext((s1 & 0xffffff) as u64, 24) as i64;
((s0 * s1) >> 32) as u32
}
17 | 18 | 26 => {
let (s0, s1) = (s0 as i32, s1 as i32);
(match op {
@@ -935,7 +930,7 @@ impl<'a> Thread<'a> {
let op = ((instr >> 16) & 0x3ff) as u32;
match op {
764 | 765 | 288 | 289 | 290 | 766 | 767 | 768 | 769 | 770 => {
764 | 765 | 288 | 289 | 290 | 766 | 767 | 768 | 769 => {
let vdst = (instr & 0xff) as usize;
let sdst = ((instr >> 8) & 0x7f) as usize;
let f = |i: u32| -> usize { ((instr >> i) & 0x1ff) as usize };
@@ -1001,10 +996,6 @@ impl<'a> Thread<'a> {
let ret = s0.wrapping_sub(s1);
(ret as u32, s1 > s0)
}
770 => {
let ret = s1.wrapping_sub(s0);
(ret as u32, s0 > s1)
}
_ => todo_instr!(instruction)?,
};
if self.exec.read() {
+119 -64
View File
@@ -1,32 +1,98 @@
import numpy as np
import unittest
import subprocess, struct, math
from tinygrad import Tensor, dtypes, Device, UOp
from tinygrad.helpers import getenv
from tinygrad.runtime.support.compiler_amd import amdgpu_disassemble
from tinygrad.renderer import ProgramSpec
from tinygrad.engine.realize import CompiledRunner
from typing import cast
from tinygrad.runtime.ops_amd import AMDProgram, AMDDevice
from tinygrad import Tensor, dtypes, Device
from tinygrad.helpers import diskcache, OSX, getenv
def get_output(asm:str, n_threads:int=1):
input_asm = "\n".join([ln if ln.strip().startswith('asm volatile') else f'asm volatile("{ln.strip().lstrip()}" : "+v"(a), "+v"(b));'
for ln in asm.strip().splitlines() if ln.strip()])
src = f"""
typedef long unsigned int size_t;
extern "C" __attribute__((device, const)) size_t __ockl_get_local_id(unsigned int);
extern "C" __attribute__((global)) void __attribute__((amdgpu_flat_work_group_size(1, {n_threads}))) test(unsigned int* data0_1) {{
int l = __ockl_get_local_id(0);
unsigned a = 0, b = 0, c = 0;
{input_asm}
unsigned res;
asm volatile("v_mov_b32 %0, %1" : "=v"(res) : "v"(a));
*(data0_1+l) = res;
}}"""
t = Tensor.zeros(n_threads, dtype=dtypes.uint32).contiguous().realize()
prg = ProgramSpec("test", src, Device.DEFAULT, UOp.sink(t), global_size=[1, 1, 1], local_size=[n_threads, 1, 1])
car = CompiledRunner(prg)
if getenv("PRINT_ASM"): amdgpu_disassemble(car.lib)
car([t.uop.buffer], {}, wait=True)
return t.numpy()
@diskcache
def assemble(code:str) -> bytes:
try:
LLVM_MC = "llvm-mc" if OSX else "/opt/rocm/llvm/bin/llvm-mc"
return subprocess.run([LLVM_MC, "--arch=amdgcn", "--mcpu=gfx1100", "--triple=amdgcn-amd-amdhsa", "-filetype=obj", "-o", "-"],
input=code.encode("utf-8"), stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True).stdout
except subprocess.CalledProcessError as e:
print("stderr:")
print(e.stderr.decode())
raise
# copied from extra/rdna
def get_prg(code:str, v_cnt:int, s_cnt:int):
function_name = "test"
metadata = f"""
amdhsa.kernels:
- .args:
- .address_space: global
.name: buf_0
.offset: 0
.size: 8
.type_name: unsigned int*
.value_kind: global_buffer
.group_segment_fixed_size: 0
.kernarg_segment_align: 8
.kernarg_segment_size: 8
.language: OpenCL C
.language_version:
- 1
- 2
.max_flat_workgroup_size: 256
.name: test
.private_segment_fixed_size: 0
.sgpr_count: {s_cnt}
.sgpr_spill_count: 0
.symbol: test.kd
.uses_dynamic_stack: false
.vgpr_count: {v_cnt}
.vgpr_spill_count: 0
.wavefront_size: 32
amdhsa.target: amdgcn-amd-amdhsa--gfx1100
amdhsa.version:
- 1
- 2
"""
boilerplate_start = f"""
.rodata
.global {function_name}.kd
.type {function_name}.kd,STT_OBJECT
.align 0x10
.amdhsa_kernel {function_name}"""
kernel_desc = {
'.amdhsa_group_segment_fixed_size': 0, '.amdhsa_private_segment_fixed_size': 0, '.amdhsa_kernarg_size': 0,
'.amdhsa_next_free_vgpr': v_cnt, # this matters!
'.amdhsa_reserve_vcc': 0, '.amdhsa_reserve_xnack_mask': 0,
'.amdhsa_next_free_sgpr': s_cnt,
'.amdhsa_float_round_mode_32': 0, '.amdhsa_float_round_mode_16_64': 0, '.amdhsa_float_denorm_mode_32': 3, '.amdhsa_float_denorm_mode_16_64': 3,
'.amdhsa_dx10_clamp': 1, '.amdhsa_ieee_mode': 1, '.amdhsa_fp16_overflow': 0,
'.amdhsa_workgroup_processor_mode': 1, '.amdhsa_memory_ordered': 1, '.amdhsa_forward_progress': 0, '.amdhsa_enable_private_segment': 0,
'.amdhsa_system_sgpr_workgroup_id_x': 1, '.amdhsa_system_sgpr_workgroup_id_y': 1, '.amdhsa_system_sgpr_workgroup_id_z': 1,
'.amdhsa_system_sgpr_workgroup_info': 0, '.amdhsa_system_vgpr_workitem_id': 2, # is amdhsa_system_vgpr_workitem_id real?
'.amdhsa_exception_fp_ieee_invalid_op': 0, '.amdhsa_exception_fp_denorm_src': 0,
'.amdhsa_exception_fp_ieee_div_zero': 0, '.amdhsa_exception_fp_ieee_overflow': 0, '.amdhsa_exception_fp_ieee_underflow': 0,
'.amdhsa_exception_fp_ieee_inexact': 0, '.amdhsa_exception_int_div_zero': 0,
'.amdhsa_user_sgpr_dispatch_ptr': 0, '.amdhsa_user_sgpr_queue_ptr': 0, '.amdhsa_user_sgpr_kernarg_segment_ptr': 1,
'.amdhsa_user_sgpr_dispatch_id': 0, '.amdhsa_user_sgpr_private_segment_size': 0, '.amdhsa_wavefront_size32': 1, '.amdhsa_uses_dynamic_stack': 0}
code_start = f""".end_amdhsa_kernel
.text
.global {function_name}
.type {function_name},@function
.p2align 8
{function_name}:
"""
ret = ".amdgpu_metadata\n" + metadata + ".end_amdgpu_metadata" + boilerplate_start + "\n" + '\n'.join("%s %d" % x for x in kernel_desc.items()) \
+ "\n" + code_start + code + f"\n.size {function_name}, .-{function_name}"
return AMDProgram(cast(AMDDevice, Device["AMD"]), function_name, assemble(ret))
def get_output(s:str, n_threads:int=1):
assert n_threads <= 32
code = "\n".join(["s_load_b64 s[0:1], s[0:1], null", "v_lshlrev_b32_e32 v0, 2, v0", s,
"s_waitcnt 0",
"global_store_b32 v0, v1, s[0:1]",
"s_nop 0", "s_sendmsg sendmsg(MSG_DEALLOC_VGPRS)", "s_endpgm"])
test = Tensor.zeros((n_threads,), dtype=dtypes.uint32).contiguous().realize().uop.buffer
prg = get_prg(code, 32, 32)
prg(test._buf, global_size=(1, 1, 1), local_size=(n_threads, 1, 1), wait=True)
return test.numpy()
def f16_to_bits(x:float) -> int: return struct.unpack('<H', struct.pack('<e', x))[0]
def f32_from_bits(x:int) -> float: return struct.unpack('<f', struct.pack('<I', x))[0]
@@ -39,57 +105,54 @@ class TestHW(unittest.TestCase):
def test_simple(self):
out = get_output("""
v_mov_b32_e32 %1 42
v_mov_b32_e32 %2 %1
""")[0]
v_mov_b32_e32 v10 42
v_mov_b32_e32 v1 v10
""", n_threads=2)
np.testing.assert_equal(out, 42)
def test_exec_mov(self):
out = get_output("""
v_mov_b32_e32 %1 42
v_mov_b32_e32 v10 42
s_mov_b32_e32 exec_lo 0b10
v_mov_b32_e32 %1 10
v_mov_b32_e32 v10 10
s_mov_b32_e32 exec_lo 0b11
v_mov_b32_e32 %2 %1
v_mov_b32_e32 v1 v10
""", n_threads=2)
np.testing.assert_equal(out, [42, 10])
def test_exec_cmp_vopc(self):
out = get_output("""
s_mov_b32 vcc_lo 0 // reset vcc
v_mov_b32_e32 %1 42
v_mov_b32_e32 %2 10
v_mov_b32_e32 v10 42
v_mov_b32_e32 v11 10
s_mov_b32_e32 exec_lo 0b01
v_cmp_ne_u32 %1 %2
v_cmp_ne_u32 v10 v11
s_mov_b32_e32 exec_lo 0b11
v_mov_b32_e32 %2 vcc_lo
v_mov_b32_e32 v1 vcc_lo
""", n_threads=2)
np.testing.assert_equal(out, 0b01)
def test_exec_cmpx_vop3(self):
out = get_output("""
s_mov_b32_e32 exec_lo 0b11
v_mov_b32_e32 %1 42
v_mov_b32_e32 %2 10
v_mov_b32_e32 v10 42
v_mov_b32_e32 v11 10
s_mov_b32_e32 exec_lo 0b01
v_cmpx_ne_u32 %1 %2
v_cmpx_ne_u32 v10 v11
s_mov_b32_e32 s10 exec_lo
s_mov_b32_e32 exec_lo 0b11
v_mov_b32_e32 %2 s10
""", n_threads=2)[0]
np.testing.assert_equal(out & 0b11, 0b01)
v_mov_b32_e32 v1 s10
""", n_threads=2)
np.testing.assert_equal(out, 0b01)
def test_fmac_vop3_modifier(self):
init_state = f"""
asm volatile("v_mov_b32_e32 %1, {f16_to_bits(4.0)}" : "+v"(a));
asm volatile("v_mov_b32_e32 %1, {f16_to_bits(3.0)}" : "+v"(b));
asm volatile("v_mov_b32_e32 %1, {f16_to_bits(2.0)}" : "+v"(c));
v_mov_b32_e32 v10 {f16_to_bits(4.0)}
v_mov_b32_e32 v11 {f16_to_bits(3.0)}
v_mov_b32_e32 v1 {f16_to_bits(2.0)}
"""
mov = """asm volatile("v_mov_b32_e32 %1, %2" : "+v"(c), "+v"(a));"""
def fmac(a, b, c): return f"""asm volatile("v_fmac_f16_e64 {c}, {a}, {b}" : "+v"(c) : "v"(a), "v"(b));"""+"\n"+mov
self.assertEqual(get_output(init_state+"\n"+fmac("%1", "%2", "%3")), f16_to_bits(14.))
self.assertEqual(get_output(init_state+"\n"+fmac("%1", "-%2", "%3")), f16_to_bits(-10.))
self.assertEqual(get_output(init_state+"\n"+fmac("-%1", "-%2", "%3")), f16_to_bits(14.))
self.assertEqual(get_output(init_state+"\n"+"v_fmac_f16_e64 v1 v11 v10"), f16_to_bits(14.))
self.assertEqual(get_output(init_state+"\n"+"v_fmac_f16_e64 v1 -v11 v10"), f16_to_bits(-10.))
self.assertEqual(get_output(init_state+"\n"+"v_fmac_f16_e64 v1 -v11 -v10"), f16_to_bits(14.))
def test_s_abs_i32(self):
def s_abs_i32(x, y, dst="s10", scc=0):
@@ -97,7 +160,7 @@ class TestHW(unittest.TestCase):
self.assertEqual(get_output(f"""
s_mov_b32_e32 {dst} {x}
s_abs_i32 {dst} {dst}
v_mov_b32_e32 %2 {reg}
v_mov_b32_e32 v1 {reg}
""")[0], val)
s_abs_i32(0x00000001, 0x00000001, scc=1)
s_abs_i32(0x7fffffff, 0x7fffffff, scc=1)
@@ -110,8 +173,8 @@ class TestHW(unittest.TestCase):
def test_v_rcp_f32_neg_vop3(self):
def v_neg_rcp_f32(x:float, y:float):
out = get_output(f"""
v_mov_b32_e32 %2 {f32_to_bits(x)}
v_rcp_f32_e64 %2, -%2
v_mov_b32_e32 v1 {f32_to_bits(x)}
v_rcp_f32_e64 v1, -v1
""")[0]
assert out == f32_to_bits(y), f"{f32_from_bits(out)} != {y} / {out} != {f32_to_bits(y)}"
v_neg_rcp_f32(math.inf, -0.0)
@@ -123,11 +186,10 @@ class TestHW(unittest.TestCase):
def test_v_cndmask_b32_neg(self):
def v_neg(x:int|float, y:float):
# always pick -v1
out = get_output(f"""
v_mov_b32_e32 %2 {f32_to_bits(x)}
s_mov_b32_e32 s10 1
v_cndmask_b32 %2, %2, -%2 s10
v_mov_b32_e32 v1 {f32_to_bits(x)}
s_mov_b32_e32 s10 1 // always pick -v1
v_cndmask_b32 v1, v1, -v1 s10
""")[0]
assert out == f32_to_bits(y), f"{f32_from_bits(out)} != {y} / {out} != {f32_to_bits(y)}"
v_neg(-0.0, 0.0)
@@ -136,12 +198,5 @@ class TestHW(unittest.TestCase):
v_neg(math.inf, -math.inf)
v_neg(-math.inf, math.inf)
def test_v_subrev_wrap(self):
out = get_output("""
v_dual_mov_b32 %1, 0xffffffff :: v_dual_mov_b32 %2, 0x0
v_subrev_co_u32 %2, vcc_lo, %2, %1
""")[0]
self.assertEqual(out, 0xffff_ffff)
if __name__ == "__main__":
unittest.main()
+2
View File
@@ -2,6 +2,8 @@
## Getting SQ Thread Trace
Only supported on 7900XTX, requires either AM (`rmmod amdgpu`) or disabling power gating on AMD (`ppfeaturemask=0xffff3fff`, don't forget to rebuild initramfs)
SQTT is implemented on top of normal tinygrad profiling, `VIZ=1 SQTT=1` to get profile pickle with sqtt data embedded in it.
`SQTT_BUFFER_SIZE=X` to change size of SQTT buffer (per shader engine, 6 SEs on 7900xtx) in megabytes, default 256.
+68
View File
@@ -0,0 +1,68 @@
import ctypes
from dataclasses import dataclass
import tinygrad.runtime.autogen.comgr as comgr
from tinygrad.runtime.support.compiler_amd import check
@dataclass
class InstrCtx:
pc:int=0
inst:str=""
@comgr.amd_comgr_create_disassembly_info.argtypes[2]
def instr_cb(text, user_data):
c = ctypes.cast(user_data, ctypes.POINTER(ctypes.py_object)).contents.value
c.inst = ctypes.string_at(text).decode("utf-8","replace").strip()
return comgr.AMD_COMGR_STATUS_SUCCESS
# nop callback
@comgr.amd_comgr_create_disassembly_info.argtypes[3]
def addr_cb(*args): return comgr.AMD_COMGR_STATUS_SUCCESS
def comgr_get_address_table(lib:bytes) -> dict[int, tuple[str, int]]:
check(comgr.amd_comgr_create_data(comgr.AMD_COMGR_DATA_KIND_EXECUTABLE, ctypes.byref(data_src:=comgr.amd_comgr_data_t())))
lib_buf = ctypes.create_string_buffer(lib, len(lib))
check(comgr.amd_comgr_set_data(data_src, len(lib), lib_buf))
check(comgr.amd_comgr_get_data_isa_name(data_src, isa_sz:=ctypes.c_size_t(128), isa:=(ctypes.c_char*isa_sz.value)()))
@comgr.amd_comgr_create_disassembly_info.argtypes[1]
def memory_cb(from_addr, to, size, _):
base, buf_len = ctypes.addressof(lib_buf), len(lib_buf)
start = int(from_addr) - base
if start < 0 or start >= buf_len: return 0
ctypes.memmove(to, base + start, n:=min(int(size), buf_len - start))
return n
info_src = comgr.amd_comgr_disassembly_info_t()
check(comgr.amd_comgr_create_disassembly_info(ctypes.cast(isa, ctypes.POINTER(ctypes.c_char)), memory_cb, instr_cb, addr_cb, info_src))
@comgr.amd_comgr_iterate_symbols.argtypes[1]
def sym_callback(sym, udata):
check(comgr.amd_comgr_symbol_get_info(sym, comgr.AMD_COMGR_SYMBOL_INFO_TYPE, ctypes.byref(sym_type:=ctypes.c_int())))
if sym_type.value != comgr.AMD_COMGR_SYMBOL_TYPE_FUNC: return comgr.AMD_COMGR_STATUS_SUCCESS
check(comgr.amd_comgr_symbol_get_info(sym, comgr.AMD_COMGR_SYMBOL_INFO_VALUE, ctypes.byref(vaddr:=ctypes.c_uint64())))
check(comgr.amd_comgr_symbol_get_info(sym, comgr.AMD_COMGR_SYMBOL_INFO_SIZE, ctypes.byref(size:=ctypes.c_uint64())))
check(comgr.amd_comgr_map_elf_virtual_address_to_code_object_offset(data_src, vaddr.value, ctypes.byref(offset:=ctypes.c_uint64()),
ctypes.byref(ctypes.c_uint64()), ctypes.byref(nobits:=ctypes.c_bool())))
check(nobits.value)
base = ctypes.addressof(lib_buf)
pc = base + offset.value
end = pc + size.value
addr_table = ctypes.cast(udata, ctypes.POINTER(ctypes.py_object)).contents.value
instr_ref = ctypes.py_object(ctx:=InstrCtx())
instr_ptr = ctypes.cast(ctypes.pointer(instr_ref), ctypes.c_void_p)
while pc < end:
size_read = ctypes.c_uint64(0)
ctx.pc = pc
st = comgr.amd_comgr_disassemble_instruction(info_src, ctypes.c_uint64(pc), instr_ptr, ctypes.byref(size_read))
if st == comgr.AMD_COMGR_STATUS_SUCCESS and size_read.value:
rel = (pc - base) - offset.value
addr_table[vaddr.value + rel] = (ctx.inst, int(size_read.value))
pc += size_read.value
else: # don't inf loop if comgr fails
b = ctypes.c_ubyte.from_buffer(lib_buf, pc - base).value
addr_table[vaddr.value + (pc - base - offset.value)] = (f"DISASSEMBLER ISSUE 0x{b:02x}", 1)
pc += 1
return comgr.AMD_COMGR_STATUS_SUCCESS
addr_table:dict[int, tuple[str, int]] = {}
check(comgr.amd_comgr_iterate_symbols(data_src, sym_callback, ctypes.cast(ctypes.pointer(ctypes.py_object(addr_table)), ctypes.c_void_p)))
return addr_table
+4 -19
View File
@@ -4,7 +4,7 @@ import argparse, ctypes, struct, hashlib, pickle, code, typing, functools
import tinygrad.runtime.autogen.sqtt as sqtt
from tinygrad.device import ProfileEvent, ProfileDeviceEvent, ProfileProgramEvent
from tinygrad.runtime.ops_amd import ProfileSQTTEvent
from tinygrad.helpers import round_up, flatten, all_same, temp
from tinygrad.helpers import round_up, flatten, all_same
from dataclasses import dataclass
CHUNK_CLASSES = {
@@ -154,22 +154,8 @@ class RGP:
if device not in device_events: raise RuntimeError(f"Device {device} not found in profile, devices in profile: {', '.join(device_events.keys())} ")
device_event = device_events[device]
sqtt_events = [x for x in profile if isinstance(x, ProfileSQTTEvent) and x.device == device_event.device]
device_props = device_event.props
# merge events per SE
merged_sqtt_events:dict[int, ProfileSQTTEvent] = {}
for ev in sqtt_events:
if ev.se not in merged_sqtt_events: merged_sqtt_events[ev.se] = ev
else:
merged_sqtt_events[ev.se] = ProfileSQTTEvent(
device=ev.device,
kern=ev.kern,
se=ev.se,
itrace=merged_sqtt_events[ev.se].itrace or ev.itrace,
blob=merged_sqtt_events[ev.se].blob + ev.blob,
)
sqtt_events = list(merged_sqtt_events.values())
if len(sqtt_events) == 0: raise RuntimeError(f"Device {device_event.device} doesn't contain SQTT data")
device_props = sqtt_events[0].props
gfx_ver = device_props['gfx_target_version'] // 10000
gfx_iplvl = getattr(sqtt, f"SQTT_GFXIP_LEVEL_GFXIP_{device_props['gfx_target_version']//10000}_{(device_props['gfx_target_version']//100)%100}",
getattr(sqtt, f"SQTT_GFXIP_LEVEL_GFXIP_{device_props['gfx_target_version']//10000}", None))
@@ -210,7 +196,7 @@ class RGP:
flags=0,
trace_shader_core_clock=0x93f05080,
trace_memory_clock=0x4a723a40,
device_id={110000: 0x744c, 110003: 0x7480, 120001: 0x7550, 120000: 0x7550}[device_props['gfx_target_version']],
device_id={110000: 0x744c, 110003: 0x7480, 120001: 0x7550}[device_props['gfx_target_version']],
device_revision_id=0xc8,
vgprs_per_simd=1536,
sgprs_per_simd=128*16,
@@ -324,7 +310,7 @@ class RGP:
if __name__ == '__main__':
parser = argparse.ArgumentParser(prog='rgptool', description='A tool to create (from pickled tinygrad profile), inspect and modify Radeon GPU Profiler files')
parser.add_argument('command')
parser.add_argument('input', nargs='?', default=temp("profile.pkl", append_user=True))
parser.add_argument('input')
parser.add_argument('-d', '--device')
parser.add_argument('-o', '--output')
args = parser.parse_args()
@@ -346,4 +332,3 @@ if __name__ == '__main__':
if args.output is not None:
with open(args.output, 'wb+') as fd: fd.write(rgp.to_bytes())
print(f"Saved to {args.output}")
+30 -105
View File
@@ -1,32 +1,9 @@
import ctypes, pathlib, argparse, pickle, re, functools, dataclasses, itertools
from tinygrad.helpers import temp, unwrap, DEBUG
from tinygrad.device import ProfileEvent, ProfileDeviceEvent, ProfileProgramEvent
from tinygrad.runtime.ops_amd import ProfileSQTTEvent, ProfilePMCEvent
from tinygrad.runtime.autogen import llvm, rocprof
from tinygrad.runtime.support.elf import elf_loader
# to pass NULL to callbacks
llvm.LLVMCreateDisasmCPUFeatures.argtypes = tuple(llvm.LLVMCreateDisasmCPUFeatures.argtypes[:5]) + (ctypes.c_void_p, ctypes.c_void_p)
def llvm_disasm(arch:str, lib:bytes) -> dict[int, tuple[str, int]]:
llvm.LLVMInitializeAMDGPUTargetInfo()
llvm.LLVMInitializeAMDGPUTargetMC()
llvm.LLVMInitializeAMDGPUAsmParser()
llvm.LLVMInitializeAMDGPUDisassembler()
ctx = llvm.LLVMCreateDisasmCPUFeatures("amdgcn-amd-amdhsa".encode(), arch.encode(), "".encode(), None, 0, None, None)
image, sections, relocs = elf_loader(lib)
text = next((sh.header for sh in sections if sh.name == ".text"), None)
off, sz = unwrap(text).sh_addr, unwrap(text).sh_size
addr_table:dict[int, tuple[str, int]] = {}
out = ctypes.create_string_buffer(128)
cur_off = off
while cur_off < sz + off:
view = (ctypes.c_ubyte * ((sz + off) - cur_off)).from_buffer_copy(memoryview(image)[cur_off:])
instr_sz = llvm.LLVMDisasmInstruction(ctx, view, ctypes.c_uint64(len(view)), ctypes.c_uint64(0), out, ctypes.c_size_t(128))
addr_table[cur_off] = (out.value.decode("utf-8", "replace").strip(), instr_sz)
cur_off += instr_sz
return addr_table
import ctypes, pathlib, argparse, pickle, re, functools, dataclasses
from extra.sqtt.rocprof import rocprof
from extra.sqtt.disasm import comgr_get_address_table
from tinygrad.helpers import temp, DEBUG
from tinygrad.device import ProfileEvent, ProfileProgramEvent
from tinygrad.runtime.ops_amd import ProfileSQTTEvent
@dataclasses.dataclass
class InstInfo:
@@ -40,77 +17,47 @@ class InstInfo:
def on_ev(self, ev):
self.hit, self.lat, self.stall = self.hit + 1, self.lat + ev.duration, self.stall + ev.stall
@dataclasses.dataclass(frozen=True)
class InstExec:
typ:str
inst:str
stall:int
dur:int
time:int
@dataclasses.dataclass(frozen=True)
class PrgExec:
name:str
wave:int
cu:int
simd:int
def __str__(self): return f"{self.name},{self.wave},{self.cu},{self.simd}"
@dataclasses.dataclass(frozen=True)
class WaveExec:
wave_id:int
cu:int
simd:int
insts:list[InstExec]
class _ROCParseCtx:
def __init__(self, dev_evs:dict[str, ProfileDeviceEvent], sqtt_evs:list[ProfileSQTTEvent], prog_evs:list[ProfileProgramEvent]):
self.dev_evs, self.sqtt_evs, self.prog_evs = dev_evs, iter(sqtt_evs), prog_evs
self.wave_events:dict[PrgExec, dict[int, InstInfo]] = {}
self.disasms:dict[tuple[str, int], tuple[str, int]] = {}
self.inst_execs:dict[str, list[WaveExec]] = {}
def __init__(self, sqtt_evs:list[ProfileSQTTEvent], prog_evs:list[ProfileProgramEvent]):
self.sqtt_evs, self.prog_evs = iter(sqtt_evs), prog_evs
self.wave_events, self.disasms, self.addr2prg = {}, {}, {}
for prog in prog_evs:
arch = "gfx%d%x%x" % ((trgt:=unwrap(dev_evs[prog.device].props)['gfx_target_version']) // 10000, (trgt // 100) % 100, trgt % 100)
for addr, info in llvm_disasm(arch, unwrap(prog.lib)).items():
self.disasms[(prog.name, unwrap(prog.base) + addr)] = info
for addr, info in comgr_get_address_table(prog.lib).items():
self.disasms[prog.base + addr] = info
self.addr2prg[prog.base + addr] = prog
def next_sqtt(self):
x = next(self.sqtt_evs, None)
self.active_kern = x.kern if x is not None else None
self.active_se = x.se if x is not None else None
return x
def next_sqtt(self): return next(self.sqtt_evs, None)
def find_program(self, addr): return self.addr2prg[addr]
def on_occupancy_ev(self, ev):
if DEBUG >= 5: print("OCC", ev.time, self.active_se, ev.cu, ev.simd, ev.wave_id, ev.start)
if DEBUG >= 4: print("OCC", ev.time, ev.cu, ev.simd, ev.wave_id, ev.start)
def on_wave_ev(self, ev):
if DEBUG >= 5: print("WAVE", ev.wave_id, self.active_se, ev.cu, ev.simd, ev.contexts, ev.begin_time, ev.end_time)
if DEBUG >= 4: print("WAVE", ev.wave_id, ev.cu, ev.simd, ev.contexts, ev.begin_time, ev.end_time)
asm:dict[int, InstInfo] = {}
inst_execs:list[InstExec] = []
asm = {}
for j in range(ev.instructions_size):
inst_ev = ev.instructions_array[j]
inst_typ = rocprof.rocprofiler_thread_trace_decoder_inst_category_t__enumvalues[inst_ev.category]
inst_disasm = self.disasms[(unwrap(self.active_kern), unwrap(inst_ev.pc.address))][0]
asm.setdefault(inst_ev.pc.address, InstInfo(typ=inst_typ, inst=inst_disasm))
asm.setdefault(inst_ev.pc.address, InstInfo(typ=inst_typ, inst=self.disasms[inst_ev.pc.address][0]))
asm[inst_ev.pc.address].on_ev(inst_ev)
inst_execs.append(InstExec(inst_typ, inst_disasm, inst_ev.stall, inst_ev.duration, inst_ev.time))
if ev.instructions_size > 0:
self.wave_events[key:=PrgExec(unwrap(self.active_kern), ev.wave_id, ev.cu, ev.simd)] = asm
self.inst_execs.setdefault(key.name, []).append(WaveExec(ev.wave_id, ev.cu, ev.simd, inst_execs))
self.wave_events[(self.find_program(ev.instructions_array[0].pc.address).name, ev.wave_id, ev.cu, ev.simd)] = asm
def decode(profile:list[ProfileEvent]) -> _ROCParseCtx:
dev_events:dict[str, ProfileDeviceEvent] = {}
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('--profile', type=pathlib.Path, help='Path to profile', default=pathlib.Path(temp("profile.pkl", append_user=True)))
args = parser.parse_args()
with args.profile.open("rb") as f: profile = pickle.load(f)
sqtt_events:list[ProfileSQTTEvent] = []
prog_events:list[ProfileProgramEvent] = []
for e in profile:
if isinstance(e, ProfileDeviceEvent): dev_events[e.device] = e
if isinstance(e, ProfileSQTTEvent): sqtt_events.append(e)
if isinstance(e, ProfileProgramEvent) and e.device.startswith("AMD"): prog_events.append(e)
ROCParseCtx = _ROCParseCtx(dev_events, sqtt_events, prog_events)
ROCParseCtx = _ROCParseCtx(sqtt_events, prog_events)
@rocprof.rocprof_trace_decoder_se_data_callback_t
def copy_cb(buf, buf_size, data_ptr):
@@ -127,12 +74,12 @@ def decode(profile:list[ProfileEvent]) -> _ROCParseCtx:
case rocprof.ROCPROFILER_THREAD_TRACE_DECODER_RECORD_WAVE:
for ev in (rocprof.rocprofiler_thread_trace_decoder_wave_t * n).from_address(events_ptr): ROCParseCtx.on_wave_ev(ev)
case _:
if DEBUG >= 5: print(rocprof.rocprofiler_thread_trace_decoder_record_type_t__enumvalues[record_type], events_ptr, n)
if DEBUG >= 2: print(rocprof.rocprofiler_thread_trace_decoder_record_type_t__enumvalues[record_type], events_ptr, n)
return rocprof.ROCPROFILER_THREAD_TRACE_DECODER_STATUS_SUCCESS
@rocprof.rocprof_trace_decoder_isa_callback_t
def isa_cb(instr_ptr, mem_size_ptr, size_ptr, pc, data_ptr):
instr, mem_size_ptr[0] = ROCParseCtx.disasms[(unwrap(ROCParseCtx.active_kern), pc.address)]
instr, mem_size_ptr[0] = ROCParseCtx.disasms[pc.address]
# this is the number of bytes to next instruction, set to 0 for end_pgm
if instr == "s_endpgm": mem_size_ptr[0] = 0
@@ -145,27 +92,5 @@ def decode(profile:list[ProfileEvent]) -> _ROCParseCtx:
return rocprof.ROCPROFILER_THREAD_TRACE_DECODER_STATUS_SUCCESS
try:
rocprof.rocprof_trace_decoder_parse_data(copy_cb, trace_cb, isa_cb, None)
except AttributeError as e: raise RuntimeError("Failed to find rocprof-trace-decoder. Run ./extra/sqtt/install_sqtt_decoder.py to install") from e
return ROCParseCtx
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('--profile', type=pathlib.Path, help='Path to profile', default=pathlib.Path(temp("profile.pkl", append_user=True)))
args = parser.parse_args()
with args.profile.open("rb") as f: profile = pickle.load(f)
rctx = decode(profile)
print('SQTT:', rctx.wave_events.keys())
for ev in profile:
if not isinstance(ev, ProfilePMCEvent): continue
print(f"PMC Event: dev={ev.device} kern={ev.kern}")
ptr = 0
for s in ev.sched:
view = memoryview(ev.blob).cast('Q')
print(f"\t{s.name}")
for xcc, inst, se_idx, sa_idx, wgp_idx in itertools.product(range(s.xcc), range(s.inst), range(s.se), range(s.sa), range(s.wgp)):
print(f"\t\tXCC {xcc} Inst {inst} SE {se_idx} SA {sa_idx} WGP {wgp_idx}: {view[ptr]:#x}")
ptr += 1
rocprof.rocprof_trace_decoder_parse_data(copy_cb, trace_cb, isa_cb, None)
print(ROCParseCtx.wave_events.keys())
@@ -13,6 +13,6 @@ if __name__ == "__main__":
os.chmod(fp, 0o755)
os.system(f"sudo {fp} --prefix={fp.parent} --include-subdir")
else:
lib = fetch("https://github.com/ROCm/rocprof-trace-decoder/raw/43bf0fef74a83c3c25badfc5a09c0bd39ed8c6f9/releases/linux_glibc_2_28_x86_64/librocprof-trace-decoder.so", name="librocprof-trace-decoder.so")
lib = fetch("https://github.com/ROCm/rocprof-trace-decoder/raw/5420409ad0963b2d76450add067b9058493ccbd0/releases/linux_glibc_2_28_x86_64/librocprof-trace-decoder.so", name="librocprof-trace-decoder.so")
shutil.copy2(lib, DEST)
print(f"Installed {lib.name} to", DEST)
@@ -8,19 +8,6 @@
# LONGDOUBLE_SIZE is: 16
#
import ctypes, ctypes.util
PATHS_TO_TRY = [
'/usr/local/lib/librocprof-trace-decoder.so',
'/usr/local/lib/librocprof-trace-decoder.dylib',
]
def _try_dlopen_rocprof_trace_decoder():
library = ctypes.util.find_library("rocprof-trace-decoder")
if library:
try: return ctypes.CDLL(library)
except OSError: pass
for candidate in PATHS_TO_TRY:
try: return ctypes.CDLL(candidate)
except OSError: pass
return None
class AsDictMixin:
@@ -168,7 +155,7 @@ class FunctionFactoryStub:
# You can either re-run clan2py with -l /path/to/library.so
# Or manually fix this by comment the ctypes.CDLL loading
_libraries = {}
_libraries['FIXME_STUB'] = _try_dlopen_rocprof_trace_decoder() # ctypes.CDLL('FIXME_STUB')
_libraries['FIXME_STUB'] = ctypes.CDLL(ctypes.util.find_library('rocprof-trace-decoder')) # ctypes.CDLL('FIXME_STUB')
-105
View File
@@ -1,105 +0,0 @@
import os
os.environ["PYTHONPATH"] = "."
os.environ["SQTT"] = "1"
os.environ["AMD"] = "1"
os.environ["VIZ"] = "1"
os.environ["AMD_LLVM"] = "0"
import unittest
import sys, contextlib
from tinygrad import Tensor
from tinygrad.dtype import dtypes
from tinygrad.renderer import ProgramSpec
from tinygrad.uop.ops import UOp, Ops, KernelInfo
from tinygrad.engine.realize import CompiledRunner
from tinygrad.device import Device, ProfileDeviceEvent
from extra.sqtt.roc import decode, InstExec, PrgExec
dev = Device["AMD"]
def custom(arg:str, s:UOp|None=None) -> UOp: return UOp(Ops.CUSTOM, src=(s,) if s is not None else (), arg=arg)
def asm_kernel(instrs:list[str], l:int=1, g:int=1) -> Tensor:
name = sys._getframe(1).f_code.co_name
def fxn(_):
L = UOp.special(l, "lidx0")
G = UOp.special(g, "gidx0")
op = custom("asm volatile (")
for inst in instrs: op = custom(f' "{inst}\\n\\t"', op)
op = custom(");", op)
return UOp.sink(op, L, G, arg=KernelInfo(name=name))
k = Tensor.custom_kernel(Tensor.empty(1), fxn=fxn)[0]
return k
@contextlib.contextmanager
def save_sqtt():
# clear the old traces
dev.profile_events.clear()
sqtt:dict[PrgExec, list[InstExec]] = {}
yield sqtt
# decode sqtt
rctx = decode(dev.profile_events+[ProfileDeviceEvent("AMD", props=dev.device_props())])
assert len(rctx.inst_execs) > 0, "empty sqtt output"
sqtt.update(rctx.inst_execs)
class TestTiming(unittest.TestCase):
def test_v_add(self):
with save_sqtt() as sqtt:
asm_kernel([f"v_add_f32 v{10+i} v{10+i+1} {10+i}" for i in range(3)]).realize()
wave = list(sqtt.values())[0][:-1]
assert all(s.dur == 1 for s in wave)
assert all(s.stall == 0 for s in wave)
def test_chain_v_add_1l(self):
with save_sqtt() as sqtt:
asm_kernel([
"v_add_f32_e32 v1 v0 v0",
"v_add_f32_e32 v2 v1 v1",
]).realize()
wave = list(sqtt.values())[0][:-1]
assert all(s.dur == 1 for s in wave)
assert all(s.stall == 0 for s in wave)
def test_multi_cycle_inst(self):
with save_sqtt() as sqtt:
asm_kernel([
"v_mov_b32_e32 v4 0x3f800000",
"v_rcp_f32_e32 v5 v4",
"v_mul_f32_e32 v6 v5 v4",
]).realize()
w = list(sqtt.values())[0]
rcp, mul = w[1], w[2]
self.assertGreater(rcp.dur, 1) # 4 cycles on gfx11
self.assertEqual(mul.dur, 1)
# mul depends on v5, how can it run before rcp is done?
self.assertGreaterEqual(mul.time, rcp.time+rcp.dur)
def test_wmma(self):
with save_sqtt() as sqtt:
asm_kernel([
"v_wmma_f32_16x16x16_f16 v[16:23], v[0:7], v[8:15], v[16:23]",
"v_add_f32_e32 v0 v16 v0",
], l=32*4).realize()
assert len(sqtt) == 2, f"expected two waves, got {len(sqtt)} {list(sqtt.keys())}"
wmma = list(sqtt.values())[0][0]
self.assertGreater(wmma.dur, 1) # rgp says 32 clocks
def test_sleep(self):
n = 1
def sleep_kernel(data0):
assert data0.dtype.base == dtypes.ulong
op = custom("unsigned long long t0 = __builtin_readcyclecounter();")
op = custom(f"__builtin_amdgcn_s_sleep({n});", op)
op = custom(f"unsigned long long t1 = __builtin_readcyclecounter();", op)
op = custom(f"data0_{data0.size}[0] = t1 - t0;", op)
return UOp.sink(data0, op, arg=KernelInfo(name=f"sleep_{n}"))
diff_hw_reg = Tensor.empty(1, dtype=dtypes.ulong)
diff_hw_reg = Tensor.custom_kernel(diff_hw_reg, fxn=sleep_kernel)[0]
with save_sqtt() as sqtt:
diff_hw_reg.realize()
diff_sqtt = list(sqtt.values())[0][2]
self.assertEqual(diff_sqtt.dur, diff_hw_reg.item()-1) # 1 cycle for reading the counter register
if __name__ == "__main__":
unittest.main()
-106
View File
@@ -1,106 +0,0 @@
#include "kittens.cuh"
using namespace kittens;
constexpr int NUM_WORKERS = 4;
constexpr int PIPE_STAGES = 3;
constexpr int ATTN_B = 16;
constexpr int ATTN_N = 1024;
constexpr int ATTN_H = 16;
constexpr int ATTN_D = 64;
template<int D> constexpr size_t ROWS = 16*(64/D); // height of each worker tile (rows)
template<int D, typename T=bf16, typename L=row_l> using qkvo_tile = rt<T, ROWS<D>, D, L>;
template<int D, typename T=float> using attn_tile = rt<T, ROWS<D>, ROWS<D>>;
template<int D> using shared_tile = st_bf<ROWS<D>, D>;
template<int D> using global_layout = gl<bf16, -1, -1, -1, D>; // B, N, H, specified at runtime, D known at compile time for this kernel
template<int D> struct globals { global_layout<D> Qg, Kg, Vg, Og; };
__launch_bounds__(NUM_WORKERS*WARP_THREADS, 1)
__global__ void attend_ker(bf16 *O_ptr, bf16 *Q_ptr, bf16 *K_ptr, bf16 *V_ptr) {
constexpr int D = ATTN_D;
global_layout<D> Qg{Q_ptr, ATTN_B, ATTN_N, ATTN_H, nullptr};
global_layout<D> Kg{K_ptr, ATTN_B, ATTN_N, ATTN_H, nullptr};
global_layout<D> Vg{V_ptr, ATTN_B, ATTN_N, ATTN_H, nullptr};
global_layout<D> Og{O_ptr, ATTN_B, ATTN_N, ATTN_H, nullptr};
globals<D> g(Qg, Kg, Vg, Og);
using load_group = kittens::group<2>; // pairs of workers collaboratively load k, v tiles
int loadid = load_group::groupid(), workerid = kittens::warpid(); // which worker am I?
constexpr int LOAD_BLOCKS = NUM_WORKERS / load_group::GROUP_WARPS;
const int batch = blockIdx.z, head = blockIdx.y, q_seq = blockIdx.x * NUM_WORKERS + workerid;
extern __shared__ alignment_dummy __shm[];
shared_allocator al((int*)&__shm[0]);
shared_tile<D> (&k_smem)[LOAD_BLOCKS][PIPE_STAGES] = al.allocate<shared_tile<D>, LOAD_BLOCKS, PIPE_STAGES>();
shared_tile<D> (&v_smem)[LOAD_BLOCKS][PIPE_STAGES] = al.allocate<shared_tile<D>, LOAD_BLOCKS, PIPE_STAGES>();
shared_tile<D> (&qo_smem)[NUM_WORKERS] = reinterpret_cast<shared_tile<D>(&)[NUM_WORKERS]>(k_smem);
// Initialize all of the register tiles.
qkvo_tile<D, bf16> q_reg, k_reg; // Q and K are both row layout, as we use mma_ABt.
qkvo_tile<D, bf16, col_l> v_reg; // V is column layout, as we use mma_AB.
qkvo_tile<D, float> o_reg; // Output tile.
attn_tile<D, float> att_block; // attention tile, in float. (We want to use float wherever possible.)
attn_tile<D, bf16> att_block_mma; // bf16 attention tile for the second mma_AB. We cast right before that op.
typename attn_tile<D, float>::col_vec max_vec_last, max_vec, norm_vec; // these are column vectors for the in-place softmax.
// each warp loads its own Q tile of 16x64
if (q_seq*ROWS<D> < g.Qg.depth()) {
warp::load<1, false>(qo_smem[workerid], g.Qg, {batch, q_seq, head, 0}); // going through shared memory improves coalescing of dram reads.
__syncwarp();
warp::load(q_reg, qo_smem[workerid]);
}
__syncthreads();
if constexpr(D == 64) q_reg *= __float2bfloat16(0.125f * 1.44269504089f);
else if constexpr(D == 128) q_reg *= __float2bfloat16(0.08838834764f * 1.44269504089f);
max_vec = base_types::constants<float>::neg_infty();
norm_vec = 0.f;
o_reg = 0.f;
// launch the load of the first k, v tiles
int kv_blocks = (g.Kg.depth() + LOAD_BLOCKS*ROWS<D>-1) / (LOAD_BLOCKS*ROWS<D>), tic = 0;
load_group::load_async<1, false>(k_smem[loadid][0], g.Kg, {batch, loadid, head, 0});
load_group::load_async<1, false>(v_smem[loadid][0], g.Vg, {batch, loadid, head, 0});
// iterate over k, v for these q's that have been loaded
for(auto kv_idx = 0; kv_idx < kv_blocks; kv_idx++, tic=(tic+1)%3) {
int next_load_idx = (kv_idx+1)*LOAD_BLOCKS + loadid;
if(next_load_idx*ROWS<D> < g.Kg.depth()) {
int next_tic = (tic+1)%3;
load_group::load_async<1, false>(k_smem[loadid][next_tic], g.Kg, {batch, next_load_idx, head, 0});
load_group::load_async<1, false>(v_smem[loadid][next_tic], g.Vg, {batch, next_load_idx, head, 0});
load_async_wait<1>(); // next k, v can stay in flight.
}
else load_async_wait();
__syncthreads();
#pragma unroll LOAD_BLOCKS
for(int subtile = 0; subtile < LOAD_BLOCKS && (kv_idx*LOAD_BLOCKS + subtile)*ROWS<D> < g.Kg.depth(); subtile++) {
warp::load(k_reg, k_smem[subtile][tic]); // load k from shared into registers
att_block = 0.f; // zero 16x16 attention tile
warp::mma<transpose::N, transpose::T>(att_block, q_reg, k_reg, att_block); // [email protected]
// int first_index = (kv_idx*LOAD_BLOCKS + subtile)*ROWS<D>; // one past the last KV index of this tile
// int start_fill = g.Kg.depth()-first_index < ROWS<D> ? g.Kg.depth()-first_index : ROWS<D>;
// right_fill(att_block, att_block, start_fill, base_types::constants<float>::neg_infty());
max_vec_last = max_vec;
max_vec = warp::max<axis::COL>(att_block, max_vec);
att_block = warp::exp2(att_block - max_vec);
max_vec_last = warp::exp2(max_vec_last - max_vec);
norm_vec *= max_vec_last;
norm_vec = warp::sum<axis::COL>(att_block, norm_vec);
att_block_mma = att_block; // copy to bf16 tile
warp::load(v_reg, v_smem[subtile][tic]);
o_reg *= max_vec_last;
warp::mma<transpose::N, transpose::N>(o_reg, att_block_mma, v_reg, o_reg);
}
}
o_reg /= norm_vec;
__syncthreads();
if (q_seq*ROWS<D> < g.Og.depth()) { // write out o.
warp::store(qo_smem[workerid], o_reg); // going through shared memory improves coalescing of dram writes.
__syncwarp();
warp::store<1, false>(g.Og, qo_smem[workerid], {batch, q_seq, head, 0});
}
}
-43
View File
@@ -1,43 +0,0 @@
import pathlib
from tinygrad import Device, Tensor
from tinygrad.helpers import Context
from tinygrad.runtime.support.compiler_cuda import pretty_ptx, NVCCCompiler
if __name__ == "__main__":
code = (pathlib.Path(__file__).parent / "fa.cu").read_text()
device = Device["CUDA"]
kitten_args = [f"-I{(pathlib.Path(__file__).parent / 'include').as_posix()}", "-std=c++20", "--expt-relaxed-constexpr", "-DKITTENS_4090"]
lib = NVCCCompiler(device.compiler.arch, kitten_args).compile(code)
kernel_name = lib.decode().split(".globl\t")[1].split("\n")[0]
print("kernel name", kernel_name)
print(pretty_ptx(lib.decode()))
prg = device.runtime(kernel_name, lib)
prg.smem = 16384 * 3
B, N, H, D = 16, 1024, 16, 64
q = Tensor.randn(B, N, H, D, device='CUDA', dtype="bfloat16")
k = Tensor.randn(B, N, H, D, device='CUDA', dtype="bfloat16")
v = Tensor.randn(B, N, H, D, device='CUDA', dtype="bfloat16")
out = Tensor.empty(B, N, H, D, device='CUDA', dtype="bfloat16")
Tensor.realize(q, k, v, out)
NUM_WORKERS = 4
ROWS = 16 * (64 // D)
gsz = (N // (ROWS*NUM_WORKERS), H, B)
for _ in range(5):
et = prg(out.uop.buffer.ensure_allocated()._buf, q.uop.buffer._buf, k.uop.buffer._buf, v.uop.buffer._buf,
global_size=gsz, local_size=(ROWS*NUM_WORKERS,1,1), wait=True)
attn_flops = 2 * B * H * N * N * D + \
4 * B * H * N * N + \
2 * B * H * N * N * D
print(f"{attn_flops/(et*1e9):2f} GFLOPS")
for _ in range(5):
with Context(DEBUG=2):
ref = q.scaled_dot_product_attention(k, v)
ref, out = ref.float(), out.float()
print((ref-out).mean().item(), (ref-out).max().item())
@@ -46,9 +46,9 @@ __device__ static inline void arrive(int id) {
#include "memory/memory.cuh"
#include "shared/shared.cuh"
#include "register/register.cuh"
#include "mma/mma.cuh"
#ifdef KITTENS_HOPPER
#include "mma/mma.cuh"
template<int n_reg> __device__ static inline void increase_registers() {
static_assert(n_reg % 8 == 0, "n_reg must be a multiple of 8");
@@ -93,4 +93,4 @@ __device__ static inline void sync() {
using warp = group<1>; // scope used by most pre-Hopper GPUs, and also for most register operations.
using warpgroup = group<4>; // special scope commonly used by Hopper and later.
}
}
@@ -65,8 +65,8 @@ template<typename _T, int _axis=-9999, bool _swizzle_flag=true> struct descripto
namespace detail {
template<typename... Args>
struct descriptor_dict {
__host__ __device__ descriptor_dict() {}
template<typename T> __host__ __device__ descriptor_dict(T _, int b, int d, int r, int c) {}
__host__ descriptor_dict() {}
template<typename T> __host__ descriptor_dict(T _, int b, int d, int r, int c) {}
__host__ __device__ descriptor_dict(const descriptor_dict &other) {}
#ifdef KITTENS_HOPPER
template<typename T, int U> __device__ const CUtensorMap* get() const {
@@ -85,8 +85,8 @@ struct descriptor_dict<_T, Args...> {
using DESC = kittens::tma::descriptor<_T>; // copy or initialize with a default value
CUtensorMap tma_desc;
descriptor_dict<Args...> other_descs;
__host__ __device__ descriptor_dict() {}
__host__ __device__ descriptor_dict(typename DESC::T::dtype *data, int b, int d, int r, int c): other_descs(data, b, d, r, c) {
__host__ descriptor_dict() {}
__host__ descriptor_dict(typename DESC::T::dtype *data, int b, int d, int r, int c): other_descs(data, b, d, r, c) {
kittens::detail::tma::create_tensor_map<typename DESC::T, DESC::axis, DESC::swizzle_flag>(&tma_desc, data, b, d, r, c);
}
__host__ __device__ inline descriptor_dict(const descriptor_dict &other) :
@@ -135,7 +135,7 @@ struct gl {
detail::descriptor_dict<TMA_Types...> tma_descs;
__host__ __device__ inline gl(T *_data,
__host__ inline gl(T *_data,
ducks::gl::make_arg_t<b> _batch,
ducks::gl::make_arg_t<d> _depth,
ducks::gl::make_arg_t<r> _rows,
@@ -425,4 +425,4 @@ __host__ static inline CUtensorMap* allocate_and_create_tensor_map(const typenam
} // namespace tma
} // namespace detail
} // namespace kittens
} // namespace kittens
-45
View File
@@ -1,45 +0,0 @@
// https://github.com/HazyResearch/ThunderKittens/blob/main/kernels/matmul/educational/level_04.cu
#include "kittens.cuh"
using namespace kittens;
constexpr int g_N = 8192;
constexpr int BLOCK_SIZE = 32;
#define NUM_WORKERS (1)
using sub_tile = st_bf<BLOCK_SIZE,BLOCK_SIZE>;
using tile_gl = gl<bf16, 1, 1, g_N, g_N>;
__launch_bounds__(NUM_WORKERS*WARP_THREADS, 1)
__global__ void kernel(bf16 *c_ptr, bf16 *a_ptr, bf16 *b_ptr) {
tile_gl g_C{c_ptr, nullptr, nullptr, nullptr, nullptr};
tile_gl g_A{a_ptr, nullptr, nullptr, nullptr, nullptr};
tile_gl g_B{b_ptr, nullptr, nullptr, nullptr, nullptr};
extern __shared__ alignment_dummy __shm[];
shared_allocator al((int*)&__shm[0]);
st_bf<BLOCK_SIZE,BLOCK_SIZE> &As = al.allocate<st_bf<BLOCK_SIZE,BLOCK_SIZE>>();
st_bf<BLOCK_SIZE,BLOCK_SIZE> &Bs = al.allocate<st_bf<BLOCK_SIZE,BLOCK_SIZE>>();
rt_bf<BLOCK_SIZE,BLOCK_SIZE> A_reg;
rt_bf<BLOCK_SIZE,BLOCK_SIZE> B_reg;
rt_bf<BLOCK_SIZE,BLOCK_SIZE, ducks::rt_layout::col> B_reg_col;
rt_fl<BLOCK_SIZE,BLOCK_SIZE> C_accum;
int col = blockIdx.x;
int row = blockIdx.y;
warp::zero(C_accum);
int num_tiles = (g_N + BLOCK_SIZE - 1) / BLOCK_SIZE;
for (int tile = 0; tile < num_tiles; ++tile) {
warp::load(As, g_A, {0, 0, row, tile});
warp::load(Bs, g_B, {0, 0, tile, col});
__syncthreads();
warp::load(A_reg, As);
warp::load(B_reg, Bs);
warp::swap_layout(B_reg_col, B_reg);
__syncthreads();
warp::mma_AB(C_accum, A_reg, B_reg_col, C_accum);
__syncthreads();
}
warp::store(g_C, C_accum, {0, 0, row, col});
}
-55
View File
@@ -1,55 +0,0 @@
import pathlib
from tinygrad import Device, Tensor
from tinygrad.helpers import Context, getenv
from tinygrad.runtime.support.compiler_cuda import pretty_ptx, NVCCCompiler
if __name__ == "__main__":
if getenv("MATMUL2"):
code = (pathlib.Path(__file__).parent / "matmul2.cu").read_text()
else:
code = (pathlib.Path(__file__).parent / "matmul.cu").read_text()
device = Device["CUDA"]
kitten_args = [f"-I{(pathlib.Path(__file__).parent / 'include').as_posix()}", "-std=c++20", "--expt-relaxed-constexpr"]
lib = NVCCCompiler(device.compiler.arch, kitten_args).compile(code)
kernel_name = lib.decode().split(".globl\t")[1].split("\n")[0]
print("kernel name", kernel_name)
print(pretty_ptx(lib.decode()))
prg = device.runtime(kernel_name, lib)
if getenv("MATMUL2"):
prg.smem = 16384 * 2
else:
prg.smem = 10000
N = 8192
a = Tensor.randn(N, N, device='CUDA', dtype="bfloat16")
b = Tensor.randn(N, N, device='CUDA', dtype="bfloat16")
c = Tensor.empty(N, N, device='CUDA', dtype="bfloat16")
Tensor.realize(a, b, c)
WARP_THREADS = 32
if getenv("MATMUL2"):
SUPER_N = 2
SUPER_M = 2
NUM_WORKERS = SUPER_N * SUPER_M
BLOCK_SIZE = 32
gsz = (N // (BLOCK_SIZE * SUPER_N), N // (BLOCK_SIZE * SUPER_M), 1)
else:
NUM_WORKERS = 1
BLOCK_SIZE = 32
gsz = (N // (BLOCK_SIZE), N // (BLOCK_SIZE), 1)
for _ in range(5):
et = prg(c.uop.buffer.ensure_allocated()._buf, a.uop.buffer._buf, b.uop.buffer._buf,
global_size=gsz, local_size=(NUM_WORKERS*WARP_THREADS,1,1), wait=True)
print(f"{N*N*N*2/(et*1e9):2f} GFLOPS")
# print(c.tolist())
for _ in range(5):
with Context(DEBUG=2):
ref = (a@b).realize()
ref, c = ref.float(), c.float()
print((ref-c).mean().item(), (ref-c).max().item())
-105
View File
@@ -1,105 +0,0 @@
#include "kittens.cuh"
using namespace kittens;
constexpr int g_N = 8192;
constexpr int SUPER_N = 2;
constexpr int SUPER_M = 2;
constexpr int NUM_WORKERS = SUPER_N * SUPER_M;
constexpr int LOAD_TASKS = SUPER_N + SUPER_M;
constexpr int WORKER_M = 32;
constexpr int WORKER_N = 32;
constexpr int BLOCK_K = 32;
constexpr int BLOCK_M = WORKER_M * SUPER_M;
constexpr int BLOCK_N = WORKER_N * SUPER_N;
constexpr int PIPE_STAGES = 2;
using reg_tile_A = rt_bf<WORKER_M, BLOCK_K>;
using reg_tile_B_col = rt_bf<BLOCK_K, WORKER_N, ducks::rt_layout::col>;
using reg_tile_C = rt_fl<WORKER_M, WORKER_N>;
using shared_tile_A = st_bf<WORKER_M, BLOCK_K>;
using shared_tile_B = st_bf<BLOCK_K, WORKER_N>;
using shared_tile_C = st_bf<WORKER_M, WORKER_N>;
using gl_tile_A = gl<bf16, 1, 1, g_N, g_N, shared_tile_A>;
using gl_tile_B = gl<bf16, 1, 1, g_N, g_N, shared_tile_B>;
using gl_tile_C = gl<bf16, 1, 1, g_N, g_N, shared_tile_C>;
__launch_bounds__(NUM_WORKERS *WARP_THREADS, 1) __global__
void kernel(bf16 *c_ptr, bf16 *a_ptr, bf16 *b_ptr) {
gl_tile_C g_C{c_ptr, nullptr, nullptr, nullptr, nullptr};
gl_tile_A g_A{a_ptr, nullptr, nullptr, nullptr, nullptr};
gl_tile_B g_B{b_ptr, nullptr, nullptr, nullptr, nullptr};
extern __shared__ alignment_dummy __shm[];
shared_allocator al((int *)&__shm[0]);
shared_tile_A(&As)[SUPER_M][PIPE_STAGES] =
al.allocate<shared_tile_A, SUPER_M, PIPE_STAGES>();
shared_tile_B(&Bs)[SUPER_N][PIPE_STAGES] =
al.allocate<shared_tile_B, SUPER_N, PIPE_STAGES>();
reg_tile_A A_reg;
reg_tile_B_col B_reg_col;
reg_tile_C C_accum;
int warpid = kittens::warpid();
int warp_m = warpid % SUPER_M;
int warp_n = warpid / SUPER_M;
int load_group_id = warpgroup::groupid();
int block_row = blockIdx.y * SUPER_M;
int block_col = blockIdx.x * SUPER_N;
warp::zero(C_accum);
int num_tiles = (g_N + BLOCK_K - 1) / BLOCK_K;
for (int load_tile = 0; load_tile < (PIPE_STAGES - 1); load_tile++) {
if (load_tile < num_tiles) {
int load_smem_idx = load_tile % PIPE_STAGES;
for (int task_id = warpid; task_id < LOAD_TASKS; task_id += NUM_WORKERS) {
if (task_id < SUPER_M) {
warp::load_async(As[task_id][load_smem_idx], g_A, {0, 0, block_row + task_id, load_tile});
} else {
int n_index = task_id - SUPER_M;
warp::load_async(Bs[n_index][load_smem_idx], g_B, {0, 0, load_tile, block_col + n_index});
}
}
}
}
for (int tile = 0; tile < num_tiles; tile++) {
int compute_smem_idx = tile % PIPE_STAGES;
int load_tile = tile + PIPE_STAGES - 1;
int load_smem_idx = load_tile % PIPE_STAGES;
if (load_tile < num_tiles) {
for (int task_id = warpid; task_id < LOAD_TASKS; task_id += NUM_WORKERS) {
if (task_id < SUPER_M) {
warp::load_async(As[task_id][load_smem_idx], g_A,
{0, 0, block_row + task_id, load_tile});
} else {
int n_index = task_id - SUPER_M;
warp::load_async(Bs[n_index][load_smem_idx], g_B,
{0, 0, load_tile, block_col + n_index});
}
}
load_async_wait<1>();
} else
load_async_wait();
__syncthreads();
warp::load(A_reg, As[warp_m][compute_smem_idx]);
warp::load(B_reg_col, Bs[warp_n][compute_smem_idx]);
warp::mma_AB(C_accum, A_reg, B_reg_col, C_accum);
__syncthreads();
}
warp::store(g_C, C_accum, {0, 0, block_row + warp_m, block_col + warp_n});
}
-1
View File
@@ -1 +0,0 @@
WARP_THREADS = 32
-272
View File
@@ -1,272 +0,0 @@
import math, functools
from typing import cast, Callable
from tinygrad import Tensor, Device, Context, GlobalCounters, dtypes
from tinygrad.uop.ops import AxisType, UOp, KernelInfo, Ops
from tinygrad.engine.realize import ExecItem, get_runner
from tinygrad.dtype import AddrSpace, PtrDType
from tinygrad.helpers import getenv, prod
from extra.thunder.tiny.tk import WARP_THREADS
from extra.thunder.tiny.tk.tiles import TILE_ROW_DIM, TILE_COL_DIM, RT_BASE_TILE_NEPT, slots
class Group:
def __init__(self, warps:int, ker):
self.warps = warps
self.group_threads = warps * WARP_THREADS
self.threadIdx_x = ker.threadIdx_x
self.ker = ker
# helpers
@property
def laneid(self): return self.threadIdx_x % self.group_threads
@property
def warpid(self): return self.laneid // WARP_THREADS
@property
def groupid(self): return self.threadIdx_x // self.group_threads
# ops that only work on a single warp
clear_rid = 1000
def clear(self, reg:UOp, value:float=0):
assert self.warps == 1
i = UOp.range(reg.size, Group.clear_rid)
Group.clear_rid += 1
return reg.reshape((reg.size,))[i].set(value, end=i).after(reg).reshape(reg.shape)
def zero(self, reg:UOp): return self.clear(reg, 0)
def neg_inf(self, reg:UOp): return self.clear(reg, -math.inf)
copy_rid = 300
def copy(self, dst:UOp, src:UOp):
assert self.warps == 1
assert dst.shape == src.shape
assert cast(PtrDType, dst.dtype).addrspace == AddrSpace.REG
assert cast(PtrDType, src.dtype).addrspace == AddrSpace.REG
rngs_for_shape = tuple(UOp.range(dim, Group.copy_rid + i) for i, dim in enumerate(dst.shape))
Group.copy_rid += len(dst.shape)
dst_store = dst[*rngs_for_shape].store(src[*rngs_for_shape].cast(dst.dtype.base)).end(*rngs_for_shape)
self.ker.push_store(dst_store, dst)
return dst.after(dst_store).reshape(dst.shape)
mma_rid = 600
def mma_AB(self, c:UOp, a:UOp, b:UOp, after=True):
assert self.warps == 1
mma_i_height = UOp.range(c.shape[-3], Group.mma_rid)
mma_i_width = UOp.range(c.shape[-2], Group.mma_rid+1)
mma_i_inner = UOp.range(a.shape[-2], Group.mma_rid+2, AxisType.REDUCE)
Group.mma_rid += 3
wmma_arg = ("WMMA_8_16_16_bfloat16_float", (8, 16, 16), dtypes.bfloat16, dtypes.float, "CUDA", 32, (((4, 2), (3, 2), (8, 2)), ((4, 2), (3, 2)), ((4, 2), (3, 2))), ())
a_in = UOp.vectorize(*[a[mma_i_height, mma_i_inner, i] for i in range(8)])
b_in1 = UOp.vectorize(*([b[mma_i_inner, mma_i_width, i] for i in range(2)] + [b[mma_i_inner, mma_i_width, 4+i] for i in range(2)]))
c_out1 = UOp.vectorize(*[c[mma_i_height, mma_i_width, i] for i in range(4)])
b_in2 = UOp.vectorize(*([b[mma_i_inner, mma_i_width, 2+i] for i in range(2)] + [b[mma_i_inner, mma_i_width, 6+i] for i in range(2)]))
c_out2 = UOp.vectorize(*[c[mma_i_height, mma_i_width, 4+i] for i in range(4)])
out1 = UOp(Ops.WMMA, dtypes.float32.vec(4), (a_in, b_in1, c_out1), arg=wmma_arg)
out2 = UOp(Ops.WMMA, dtypes.float32.vec(4), (a_in, b_in2, c_out2), arg=wmma_arg)
c_i = [c[mma_i_height, mma_i_width, i].store(out1.gep(i)) for i in range(4)] + [c[mma_i_height, mma_i_width, 4+i].store(out2.gep(i)) for i in range(4)]
c_store = UOp.group(*c_i).end(mma_i_height, mma_i_width, mma_i_inner)
self.ker.push_store(c_store, c)
return c.after(c_store).reshape(c.shape) if after else c_store
def mma_ABt(self, c:UOp, a:UOp, b:UOp, after=True):
assert self.warps == 1
mma_i_height = UOp.range(c.shape[-3], Group.mma_rid)
mma_i_width = UOp.range(c.shape[-2], Group.mma_rid+1)
mma_i_inner = UOp.range(a.shape[-2], Group.mma_rid+2, AxisType.REDUCE)
Group.mma_rid += 3
wmma_arg = ("WMMA_8_16_16_bfloat16_float", (8, 16, 16), dtypes.bfloat16, dtypes.float, "CUDA", 32, (((4, 2), (3, 2), (8, 2)), ((4, 2), (3, 2)), ((4, 2), (3, 2))), ())
a_in = UOp.vectorize(*[a[mma_i_height, mma_i_inner, i] for i in range(8)])
b_in1 = UOp.vectorize(*([b[mma_i_width, mma_i_inner, i] for i in range(2)] + [b[mma_i_width, mma_i_inner, 4+i] for i in range(2)]))
c_out1 = UOp.vectorize(*[c[mma_i_height, mma_i_width, i] for i in range(4)])
b_in2 = UOp.vectorize(*([b[mma_i_width, mma_i_inner, 2+i] for i in range(2)] + [b[mma_i_width, mma_i_inner, 6+i] for i in range(2)]))
c_out2 = UOp.vectorize(*[c[mma_i_height, mma_i_width, 4+i] for i in range(4)])
out1 = UOp(Ops.WMMA, dtypes.float32.vec(4), (a_in, b_in1, c_out1), arg=wmma_arg)
out2 = UOp(Ops.WMMA, dtypes.float32.vec(4), (a_in, b_in2, c_out2), arg=wmma_arg)
c_i = [c[mma_i_height, mma_i_width, i].store(out1.gep(i)) for i in range(4)] + [c[mma_i_height, mma_i_width, 4+i].store(out2.gep(i)) for i in range(4)]
c_store = UOp.group(*c_i).end(mma_i_height, mma_i_width, mma_i_inner)
self.ker.push_store(c_store, c)
return c.after(c_store).reshape(c.shape) if after else c_store
map_rid = 400
def map(self, a:UOp, op:Callable[[UOp], UOp]|Callable[[UOp, tuple], UOp]):
assert self.warps == 1
rngs_for_shape = tuple(UOp.range(dim, Group.map_rid + i) for i, dim in enumerate(a.shape))
Group.map_rid += len(a.shape)
if op.__code__.co_argcount == 1:
to_store = op(a[*rngs_for_shape])
else:
to_store = op(a[*rngs_for_shape], rngs_for_shape)
a_store = a[*rngs_for_shape].store(to_store).end(*rngs_for_shape)
self.ker.push_store(a_store, a)
return a.after(a_store).reshape(a.shape)
def row_reduce(self, vec:UOp, src:UOp, op:Callable[[UOp, UOp], UOp]):
assert self.warps == 1
red_local = UOp.placeholder((self.group_threads, 2), src.dtype.base, addrspace=AddrSpace.LOCAL, slot=slots.shared_slot)
slots.shared_slot += 1
for height in self.ker.range(src.shape[-3], track=False):
for i_outer in self.ker.range(2, track=False):
for width in self.ker.range(src.shape[-2], AxisType.REDUCE, track=False):
for i_inner in self.ker.range(4, AxisType.REDUCE, track=False):
elem_index = i_inner + 2 * (i_inner // 2) + i_outer * 2
vec_store = vec[height, 0, i_outer].store(op(vec[height, 0, i_outer], src[height, width, elem_index])).end(width, i_inner, i_outer)
vec = vec.after(vec_store).reshape(vec.shape)
# store to shared memory
for i_outer in self.ker.range(2, track=False):
red_local_store = red_local[self.laneid, i_outer].store(vec[height, 0, i_outer]).end(i_outer)
red_local = red_local.after(red_local_store).reshape(red_local.shape)
# reduce from shared memory
for i_outer in self.ker.range(2, track=False):
for i_inner in self.ker.range(3, AxisType.REDUCE, track=False):
offset = (self.laneid // 4) * 4 + ((self.laneid + 1 + i_inner) % 4)
vec_store = vec[height, 0, i_outer].store(op(vec[height, 0, i_outer], red_local[offset, i_outer])).end(i_inner, i_outer)
self.ker.push_store(vec_store, vec)
return vec.after(vec_store).reshape(vec.shape)
# ops that can work across multiple warps
LOAD_INNER = 8
load_rid = 100
def load(self, dst:UOp, src:UOp, dst_idxs:tuple[UOp|int,...]=(), idxs:tuple[UOp|int,...]=(), axis:int=0, transpose:bool=False):
assert isinstance(dst.dtype, PtrDType) and isinstance(src.dtype, PtrDType)
dst_dtype, src_dtype = cast(PtrDType, dst.dtype), cast(PtrDType, src.dtype)
if dst_dtype.addrspace == AddrSpace.REG and src_dtype.addrspace == AddrSpace.LOCAL:
srcf = src.flatten(-2)
load_i_height = UOp.range(dst.shape[-3], Group.load_rid)
load_i_width = UOp.range(dst.shape[-2], Group.load_rid+1)
load_i_inner = UOp.range(RT_BASE_TILE_NEPT, Group.load_rid+2)
Group.load_rid += 3
if self.warps % 4 == 0: local_warpid = (self.warpid // 4) + (self.warpid % 4) * (self.warps // 4)
else: local_warpid = self.warpid
warp_laneid = self.threadIdx_x % WARP_THREADS
if not transpose:
row = (local_warpid * dst.shape[-3] + load_i_height) * TILE_ROW_DIM + (warp_laneid // 4)
col = load_i_width * TILE_COL_DIM + 2 * (warp_laneid % 4)
row_offset = ((load_i_inner % 4) // 2) * 8
col_offset = (load_i_inner % 2) + (load_i_inner // 4) * 8
else:
row = (local_warpid * dst.shape[-3] + load_i_height) * TILE_ROW_DIM + 2 * (warp_laneid % 4)
col = load_i_width * TILE_COL_DIM + (warp_laneid // 4)
row_offset = (load_i_inner % 2) + (load_i_inner // 4) * 8
col_offset = ((load_i_inner % 4) // 2) * 8
src_i_last = (row + row_offset) * src.shape[-1] + col + col_offset
dst_store = dst[*dst_idxs, load_i_height, load_i_width, load_i_inner].store(srcf[*idxs[:-2], src_i_last])
dst_store = dst_store.end(load_i_height, load_i_width, load_i_inner)
elif dst_dtype.addrspace == AddrSpace.LOCAL and src_dtype.addrspace == AddrSpace.GLOBAL:
dstf = dst.flatten(-2)
srcf = src.flatten()
row_stride = prod(src.shape[axis+1:])
idxs = tuple(idx * dst.shape[-2] if i == axis else idx for i, idx in enumerate(idxs))
idxs = tuple(idx * dst.shape[-1] if i == 3 else idx for i, idx in enumerate(idxs))
src_i = ((idxs[0] * src.shape[-3] + idxs[1]) * src.shape[-2] + idxs[2]) * src.shape[-1] + idxs[3]
memcpy_per_row = dst.shape[-1] // Group.LOAD_INNER
total_calls = prod(dst.shape[-2:]) // (self.group_threads * Group.LOAD_INNER)
load_i_outer = UOp.range(total_calls, Group.load_rid)
load_i_inner = UOp.range(Group.LOAD_INNER, Group.load_rid+1)
Group.load_rid += 2
load_idx = load_i_outer * self.group_threads + self.laneid
row = load_idx // memcpy_per_row
col = (load_idx * Group.LOAD_INNER) % dst.shape[-1]
dst_i = row * dst.shape[-1] + col + load_i_inner
src_i += row * row_stride + col + load_i_inner
dst_store = dstf[*dst_idxs, dst_i].store(srcf[src_i]).end(load_i_outer, load_i_inner)
else:
raise NotImplementedError(f"load from {src_dtype.addrspace} to {dst_dtype.addrspace} not implemented")
return dst.after(dst_store.barrier()).reshape(dst.shape)
STORE_INNER = 8
store_rid = 200
def store(self, dst:UOp, src:UOp, idxs:tuple[UOp|int,...]=(), src_idxs:tuple[UOp|int,...]=(), axis=0, after=True):
assert isinstance(dst.dtype, PtrDType) and isinstance(src.dtype, PtrDType)
dst_dtype, src_dtype = cast(PtrDType, dst.dtype), cast(PtrDType, src.dtype)
if src_dtype.addrspace == AddrSpace.REG and dst_dtype.addrspace == AddrSpace.LOCAL:
dstf = dst.flatten(-2)
store_i_height = UOp.range(src.shape[-3], Group.store_rid)
store_i_width = UOp.range(src.shape[-2], Group.store_rid+1)
store_i_inner = UOp.range(RT_BASE_TILE_NEPT, Group.store_rid+2)
Group.store_rid += 3
if self.warps % 4 == 0: local_warpid = (self.warpid // 4) + (self.warpid % 4) * (self.warps // 4)
else: local_warpid = self.warpid
warp_laneid = self.threadIdx_x % WARP_THREADS
row = (local_warpid * src.shape[-3] + store_i_height) * TILE_ROW_DIM + (warp_laneid // 4)
col = store_i_width * TILE_COL_DIM + 2 * (warp_laneid % 4)
row_offset = ((store_i_inner % 4) // 2) * 8
col_offset = (store_i_inner % 2) + (store_i_inner // 4) * 8
dst_i_last = (row + row_offset) * dst.shape[-1] + col + col_offset
dst_store = dstf[*idxs[:-2], dst_i_last].store(src[*src_idxs, store_i_height, store_i_width, store_i_inner])
dst_store = dst_store.end(store_i_height, store_i_width, store_i_inner)
elif src_dtype.addrspace == AddrSpace.LOCAL and dst_dtype.addrspace == AddrSpace.GLOBAL:
dstf = dst.flatten()
row_stride = prod(dst.shape[axis+1:])
idxs = tuple(idx * src.shape[-2] if i == axis else idx for i, idx in enumerate(idxs))
idxs = tuple(idx * src.shape[-1] if i == 3 else idx for i, idx in enumerate(idxs))
dst_i = ((idxs[0] * dst.shape[-3] + idxs[1]) * dst.shape[-2] + idxs[2]) * dst.shape[-1] + idxs[3]
srcf = src.flatten(-2)
memcpy_per_row = src.shape[-1] // Group.STORE_INNER
total_calls = prod(src.shape[-2:]) // (self.group_threads * Group.STORE_INNER)
store_i_outer = UOp.range(total_calls, Group.store_rid)
store_i_inner = UOp.range(Group.STORE_INNER, Group.store_rid+1)
Group.store_rid += 2
load_idx = store_i_outer * self.group_threads + self.laneid
row = load_idx // memcpy_per_row
col = (load_idx * Group.STORE_INNER) % src.shape[-1]
src_i = row * src.shape[-1] + col + store_i_inner
dst_i += row * row_stride + col + store_i_inner
dst_store = dstf[dst_i].store(srcf[*src_idxs, src_i]).end(store_i_outer, store_i_inner)
else:
raise NotImplementedError(f"store from {src_dtype.addrspace} to {dst_dtype.addrspace} not implemented")
self.ker.push_store(dst_store, dst)
return dst.after(dst_store.barrier()).reshape(dst.shape) if after else dst_store
-57
View File
@@ -1,57 +0,0 @@
from contextlib import AbstractContextManager
from tinygrad.uop.ops import UOp, KernelInfo, AxisType
from extra.thunder.tiny.tk import WARP_THREADS
from extra.thunder.tiny.tk.group import Group
class _tk_range:
user_rid = 0
def __init__(self, end:int, axis_type:AxisType): self.end, self.axis_type, self.done = end, axis_type, False
def __iter__(self): return self
def __next__(self):
if not self.done:
self.done = True
_tk_range.user_rid += 1
self._rng = UOp.range(self.end, _tk_range.user_rid-1, axis_type=self.axis_type)
return self._rng
raise StopIteration
class Kernel(AbstractContextManager):
def __init__(self, grid_size:tuple[int, int, int], block_size:int):
self.blockIdx_x = UOp.special(grid_size[0], "gidx0")
self.blockIdx_y = UOp.special(grid_size[1], "gidx1")
self.blockIdx_z = UOp.special(grid_size[2], "gidx2")
self.threadIdx_x = UOp.special(block_size, "lidx0")
self.range_stack = []
self.store_stack = []
@property
def warpid(self): return self.threadIdx_x // WARP_THREADS
def __enter__(self): return self
def __exit__(self, exc_type, exc_value, traceback): pass
def group(self, size:int): return Group(size, self)
@property
def warp(self): return self.group(1)
@property
def warpgroup(self): return self.group(4)
def range(self, end:int, axis_type:AxisType=AxisType.LOOP, track:bool=True):
rng = _tk_range(end, axis_type)
if track: self.range_stack.append(rng)
return rng
def push_store(self, store:UOp, uop:UOp): self.store_stack.append((store, uop))
def finish(self):
# end all ranges
rngs = []
while self.range_stack: rngs.append(self.range_stack.pop(0)._rng)
return self.store_stack.pop()[0].end(*rngs).sink(arg=KernelInfo(opts_to_apply=())).simplify()
def endrange(self):
last_store = self.store_stack.pop()
last_range = self.range_stack.pop()
return last_store[1].after(last_store[0].barrier().end(last_range._rng)).reshape(last_store[1].shape)
-52
View File
@@ -1,52 +0,0 @@
import math
from typing import cast, Callable
from tinygrad import Tensor, Device, Context, GlobalCounters, dtypes
from tinygrad.uop.ops import AxisType, UOp, KernelInfo, Ops
from tinygrad.engine.realize import ExecItem, get_runner
from tinygrad.dtype import AddrSpace, PtrDType
from tinygrad.helpers import getenv, prod
from extra.thunder.tiny.tk import WARP_THREADS
class _Slots:
def __init__(self):
self.global_slot = 0
self.shared_slot = 0
self.register_slot = 0
slots = _Slots()
def gl(shape, dtype):
slots.global_slot += 1
return UOp.placeholder(shape, dtype, slot=slots.global_slot-1)
shared_slot = 0
def st(shape, dtype):
slots.shared_slot += 1
return UOp.placeholder(shape, dtype, addrspace=AddrSpace.LOCAL, slot=slots.shared_slot-1)
TILE_ROW_DIM, TILE_COL_DIM = 16, 16
RT_BASE_TILE_NE = TILE_ROW_DIM * TILE_COL_DIM
RT_BASE_TILE_NEPT = RT_BASE_TILE_NE // WARP_THREADS
register_slot = 0
def rt(shape, dtype):
assert len(shape) == 2
height = shape[0] // TILE_ROW_DIM
width = shape[1] // TILE_COL_DIM
slots.register_slot += 1
return UOp.placeholder((height, width, RT_BASE_TILE_NEPT), dtype, addrspace=AddrSpace.REG, slot=slots.register_slot-1)
def rv(length, dtype, layout="naive"):
tiles = length // TILE_ROW_DIM
match layout:
case "naive":
inner_dim = 1
outer_dim = (tiles + 1) // 2
case "ortho":
inner_dim = 1
outer_dim = tiles
case _: raise NotImplementedError(f"rv layout {layout} not implemented")
slots.register_slot += 1
return UOp.placeholder((outer_dim, inner_dim, 2), dtype, addrspace=AddrSpace.REG, slot=slots.register_slot-1)
-11
View File
@@ -1,11 +0,0 @@
from tinygrad.tensor import Tensor
import argparse
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--hash", type=str, required=True, help="file hash to fetch")
parser.add_argument("--len", type=int, required=True, help="file length to fetch")
parser.add_argument("--dest", type=str, required=True, help="destination path to save the file")
args = parser.parse_args()
Tensor(bytes.fromhex(args.hash), device="CPU").load(args.len).to(f"disk:{args.dest}").realize()
-39
View File
@@ -1,39 +0,0 @@
import json, multiprocessing
from pathlib import Path
from tinygrad.tensor import Tensor
from tinygrad.helpers import tqdm, getenv
raid_root = Path(getenv("RAID_ROOT", "/raid"))
def fetch_file(item):
path, info = item
h, size = info["hash"], info["size"]
path = raid_root / Path(path)
path.parent.mkdir(parents=True, exist_ok=True)
try:
pt = Tensor(bytes.fromhex(h), device="CPU").load(size).to(f"disk:{path.as_posix()}").realize()
except Exception as e:
print(f"error fetching {path}, {h}, {size}: {e}")
raise
pt.uop.buffer.deallocate()
def fetch_mapping():
mapping_tensor = Tensor(bytes.fromhex("d734f5e3be9f1e9d863bfaa4fc6c1ef2")).load(175866113).realize()
mapping = mapping_tensor.data().tobytes().decode()
mapping = json.loads(mapping)
mapped_files = mapping.items()
return list(mapped_files)
if __name__ == "__main__":
with multiprocessing.Pool(processes=1) as pool:
mapped_files = pool.apply(fetch_mapping)
print(f"fetched mapping for {len(mapped_files)} files")
with multiprocessing.Pool(processes=multiprocessing.cpu_count()) as pool:
for _ in tqdm(pool.imap_unordered(fetch_file, mapped_files), total=len(mapped_files)):
pass
-31
View File
@@ -1,31 +0,0 @@
from pathlib import Path
import multiprocessing, json
from tinygrad.tensor import Tensor
from tinygrad.helpers import tqdm
raid_root = Path("/raid")
def upload_file(path: Path):
pt = Tensor(path).realize()
h = pt.store().realize()
pt.uop.realized.deallocate()
return h.data().hex(), path, pt.nbytes()
if __name__ == "__main__":
raid_files = sorted([p for p in raid_root.rglob("*") if p.is_file()])
print(f"found {len(raid_files)} files in /raid")
mapping = {}
with multiprocessing.Pool(processes=multiprocessing.cpu_count()) as pool:
for h, p, s in tqdm(pool.imap_unordered(upload_file, raid_files), total=len(raid_files)):
mapping[p.relative_to(raid_root).as_posix()] = {"hash": h, "size": s}
# sort the mapping by key
mapping = dict(sorted(mapping.items()))
mapping = json.dumps(mapping).encode()
mapping_tensor = Tensor(mapping, device="CPU")
h = mapping_tensor.store().realize()
print(f"final hash: {h.data().hex()}, size: {len(mapping)}")
+4 -5
View File
@@ -642,11 +642,10 @@ def get_real_tinygrad_buffers():
torch.nn.modules.module.register_module_buffer_registration_hook(register_torch_buffer)
from torch.nn.modules import Module
def param_hook(_grad):
if _grad is not None and _grad.is_tiny: Tensor.realize(unwrap(_grad))
def module_hook(module:Module, _name, _submodule):
for param in _submodule.parameters(recurse=False):
if param.requires_grad: param.register_hook(param_hook)
def backward_hook(model:Module, _grad_input, _grad_out):
grads_to_realize = [unwrap(p.grad) for p in model.parameters() if p.grad is not None]
if len(grads_to_realize): Tensor.realize(*grads_to_realize)
def module_hook(module:Module, _name, _submodule): module.register_backward_hook(backward_hook)
torch.nn.modules.module.register_module_module_registration_hook(module_hook)
def realize_optimizer_step(optimizer: torch.optim.Optimizer, *args, **kwargs):
+75
View File
@@ -0,0 +1,75 @@
import torch
#credit to KellerJordan at https://github.com/KellerJordan/Muon/tree/master
#some changes: classic momentum instead of weighting gradient
#added ns_steps, ns_params, nesterov as hyperparams
def zeropower_via_newtonschulz5(G:torch.tensor, steps:int, params:tuple[int, ...]):
"""
Newton-Schulz iteration to compute the zeroth power / orthogonalization of G. We opt to use a
quintic iteration whose coefficients are selected to maximize the slope at zero. For the purpose
of minimizing steps, it turns out to be empirically effective to keep increasing the slope at
zero even beyond the point where the iteration no longer converges all the way to one everywhere
on the interval. This iteration therefore does not produce UV^T but rather something like US'V^T
where S' is diagonal with S_{ii}' ~ Uniform(0.5, 1.5), which turns out not to hurt model
performance at all relative to UV^T, where USV^T = G is the SVD.
"""
assert G.ndim >= 2 # batched Muon implementation by @scottjmaddox, and put into practice in the record by @YouJiacheng
a, b, c = params
X = G
if G.size(-2) > G.size(-1):
X = X.mT
# Ensure spectral norm is at most 1
X = X / (X.norm(dim=(-2, -1), keepdim=True) + 1e-7)
# Perform the NS iterations
for _ in range(steps):
A = X @ X.mT
B = b * A + c * A @ A # quintic computation strategy adapted from suggestion by @jxbz, @leloykun, and @YouJiacheng
X = a * X + B @ X
if G.size(-2) > G.size(-1):
X = X.mT
return X
def muon_update(grad, momentum, beta=0.95, ns_steps=5, ns_params=(3.4445, -4.7750, 2.0315), nesterov=True):
if beta:
momentum.mul_(beta).add_(grad)
update = grad.add(momentum,alpha=beta) if nesterov else momentum
else: update = grad
if update.ndim == 4: # for the case of conv filters
update = update.view(len(update), -1)
update = zeropower_via_newtonschulz5(update, steps=ns_steps, params=ns_params)
return update
class SingleDeviceMuon(torch.optim.Optimizer):
"""
Muon variant for usage in non-distributed settings.
"""
def __init__(self, params, lr=0.02, weight_decay=0.0, momentum=0.95, ns_steps=5, ns_params=(3.4445, -4.7750, 2.0315), nesterov=True):
defaults = dict(lr=lr, weight_decay=weight_decay, momentum=momentum, ns_steps=ns_steps, ns_params=ns_params, nesterov=nesterov)
super().__init__(params, defaults)
@torch.no_grad()
def step(self, closure=None):
loss = None
if closure is not None:
with torch.enable_grad():
loss = closure()
for group in self.param_groups:
for p in group["params"]:
if p.grad is None:
p.grad = torch.zeros_like(p) # Force synchronization
state = self.state[p]
if len(state) == 0:
state["momentum_buffer"] = torch.zeros_like(p)
update = muon_update(p.grad, state["momentum_buffer"], beta=group["momentum"], ns_steps=group["ns_steps"],
ns_params=group["ns_params"], nesterov=group["nesterov"])
p.mul_(1.0 - group["lr"] * group["weight_decay"])
p.add_(update.reshape(p.shape), alpha=-group["lr"])
return loss
-13
View File
@@ -1,13 +0,0 @@
xcuserdata/
**/*.xcodeproj/project.xcworkspace/*
!**/*.xcodeproj/project.xcworkspace/xcshareddata
**/*.xcodeproj/project.xcworkspace/xcshareddata/*
!**/*.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
**/*.playground/playground.xcworkspace/*
!**/*.playground/playground.xcworkspace/xcshareddata
**/*.playground/playground.xcworkspace/xcshareddata/*
!**/*.playground/playground.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -1,11 +0,0 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -1,148 +0,0 @@
{
"images" : [
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -1,6 +0,0 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -1,19 +0,0 @@
import AppKit
import SwiftUI
final class AppDelegate: NSObject, NSApplicationDelegate {
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
true
}
}
@main
struct TinyGPUApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
TinyGPUView()
}
}
}
@@ -1,33 +0,0 @@
import SwiftUI
struct TinyGPUView: View {
@ObservedObject var viewModel = TinyGPUViewModel()
var body: some View {
#if os(macOS)
VStack(alignment: .center) {
Text("TinyGPU Intsaller")
.padding()
.font(.title)
Text(self.viewModel.dextLoadingState)
.multilineTextAlignment(.center)
HStack {
Button(
action: {
self.viewModel.activateMyDext()
}, label: {
Text("Install extension")
}
)
}
}
.frame(width: 500, height: 200, alignment: .center)
#endif
}
}
struct TinyGPUView_Previews: PreviewProvider {
static var previews: some View {
TinyGPUView()
}
}
@@ -1,142 +0,0 @@
import Foundation
import os.log
import SystemExtensions
class TinyGPUDriverLoadingStateMachine {
enum State { case unloaded, activating, needsApproval, activated, activationError }
}
class TinyGPUViewModel: NSObject {
@Published private var state: TinyGPUDriverLoadingStateMachine.State = .unloaded
override init() {
super.init()
refreshInitialDextState()
}
private func refreshInitialDextState() {
#if os(macOS)
Task.detached { [dextIdentifier] in
let newState = Self.queryDextState(bundleID: dextIdentifier)
await MainActor.run { self.state = newState }
}
#endif
}
#if os(macOS)
private static func queryDextState(bundleID: String) -> TinyGPUDriverLoadingStateMachine.State {
let tool = "/usr/bin/systemextensionsctl"
let p = Process()
p.executableURL = URL(fileURLWithPath: tool)
p.arguments = ["list"]
let pipe = Pipe()
p.standardOutput = pipe
p.standardError = Pipe()
do {
try p.run()
p.waitUntilExit()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
guard let output = String(data: data, encoding: .utf8) else { return .unloaded }
// Look for our bundle id line
if let line = output.split(separator: "\n").first(where: { $0.contains(bundleID) }) {
if line.contains("[activated enabled]") { return .activated }
if line.contains("[activated waiting for user]") { return .needsApproval }
if line.contains("terminated waiting to uninstall") { return .unloaded }
return .activating
} else {
return .unloaded
}
} catch {
return .unloaded
}
}
#endif
private let dextIdentifier: String = "org.tinygrad.tinygpu.edriver"
public var dextLoadingState: String {
switch state {
case .unloaded:
return "TinyGPUDriver isn't loaded."
case .activating:
return "Activating TinyGPUDriver, please wait."
case .needsApproval:
return "Please follow the prompt to approve TinyGPUDriver."
case .activated:
return "TinyGPUDriver has been activated and is ready to use. You can close the installer."
case .activationError:
return "TinyGPUDriver has experienced an error during activation.\nPlease check the logs to find the error."
}
}
}
extension TinyGPUViewModel: ObservableObject {
#if os(macOS)
func activateMyDext() {
activateExtension(dextIdentifier)
}
func deactivateMyDext() {
deactivateExtension(dextIdentifier)
}
func activateExtension(_ dextIdentifier: String) {
let request = OSSystemExtensionRequest
.activationRequest(forExtensionWithIdentifier: dextIdentifier,
queue: .main)
request.delegate = self
OSSystemExtensionManager.shared.submitRequest(request)
self.state = .activating
}
func deactivateExtension(_ dextIdentifier: String) {
let request = OSSystemExtensionRequest.deactivationRequest(forExtensionWithIdentifier: dextIdentifier, queue: .main)
request.delegate = self
OSSystemExtensionManager.shared.submitRequest(request)
self.state = .unloaded
}
#endif
}
#if os(macOS)
extension TinyGPUViewModel: OSSystemExtensionRequestDelegate {
func request(
_ request: OSSystemExtensionRequest,
actionForReplacingExtension existing: OSSystemExtensionProperties,
withExtension ext: OSSystemExtensionProperties) -> OSSystemExtensionRequest.ReplacementAction {
var replacementAction: OSSystemExtensionRequest.ReplacementAction
os_log("sysex actionForReplacingExtension: %@ %@", existing, ext)
replacementAction = .replace
self.state = .activating
return replacementAction
}
func requestNeedsUserApproval(_ request: OSSystemExtensionRequest) {
os_log("sysex requestNeedsUserApproval")
self.state = .needsApproval
}
func request(_ request: OSSystemExtensionRequest, didFinishWithResult result: OSSystemExtensionRequest.Result) {
os_log("sysex didFinishWithResult: %d", result.rawValue)
self.state = .activated
}
func request(_ request: OSSystemExtensionRequest, didFailWithError error: Error) {
os_log("sysex didFailWithError: %@", error.localizedDescription)
self.state = .activationError
}
}
#endif
@@ -1,589 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
0ACB55392E9CB880007029EF /* PCIDriverKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0ACB55382E9CB880007029EF /* PCIDriverKit.framework */; };
54798269286A3512009785F6 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 54798268286A3512009785F6 /* CoreAudio.framework */; };
549EB121286A1A37009D38AB /* TinyGPUViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 549EB11F286A1A37009D38AB /* TinyGPUViewModel.swift */; };
549EB123286A1D48009D38AB /* org.tinygrad.tinygpu.edriver.dext in Embed System Extensions */ = {isa = PBXBuildFile; fileRef = C5B7D9BC26128AC50089B4C3 /* org.tinygrad.tinygpu.edriver.dext */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
549EB131286A2B98009D38AB /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 549EB130286A2B98009D38AB /* IOKit.framework */; };
54E42BC8286A1697000E1E9A /* TinyGPUApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E42BB8286A1696000E1E9A /* TinyGPUApp.swift */; };
54E42BCA286A1697000E1E9A /* TinyGPUView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E42BB9286A1696000E1E9A /* TinyGPUView.swift */; };
54E42BCC286A1697000E1E9A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 54E42BBA286A1697000E1E9A /* Assets.xcassets */; };
C5B7D9C326128AC50089B4C3 /* TinyGPUDriver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C5B7D9C226128AC50089B4C3 /* TinyGPUDriver.cpp */; };
C5B7D9C526128AC50089B4C3 /* TinyGPUDriver.iig in Sources */ = {isa = PBXBuildFile; fileRef = C5B7D9C426128AC50089B4C3 /* TinyGPUDriver.iig */; };
C5C3BBB32612ACDC003C7BFE /* AudioDriverKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C5C3BBB12612ACD3003C7BFE /* AudioDriverKit.framework */; };
C5C3BBB52612ACEF003C7BFE /* DriverKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C5C3BBB42612ACEF003C7BFE /* DriverKit.framework */; };
C5D787AC261667FC006047E5 /* TinyGPUDriverUserClient.iig in Sources */ = {isa = PBXBuildFile; fileRef = C5D787AB261667FC006047E5 /* TinyGPUDriverUserClient.iig */; };
C5D787AE26168E59006047E5 /* TinyGPUDriverUserClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C5D787AD26168D1E006047E5 /* TinyGPUDriverUserClient.cpp */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
549EB126286A1D66009D38AB /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = C5B7D9B326128AC50089B4C3 /* Project object */;
proxyType = 1;
remoteGlobalIDString = C5B7D9BB26128AC50089B4C3;
remoteInfo = SimpleAudioDriver;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
549EB122286A1D3A009D38AB /* Embed System Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "$(SYSTEM_EXTENSIONS_FOLDER_PATH)";
dstSubfolderSpec = 16;
files = (
549EB123286A1D48009D38AB /* org.tinygrad.tinygpu.edriver.dext in Embed System Extensions */,
);
name = "Embed System Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
0ACB55382E9CB880007029EF /* PCIDriverKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PCIDriverKit.framework; path = System/DriverKit/System/Library/Frameworks/PCIDriverKit.framework; sourceTree = SDKROOT; };
54798268286A3512009785F6 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.0.sdk/System/Library/Frameworks/CoreAudio.framework; sourceTree = DEVELOPER_DIR; };
549EB11F286A1A37009D38AB /* TinyGPUViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TinyGPUViewModel.swift; sourceTree = "<group>"; usesTabs = 1; };
549EB130286A2B98009D38AB /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.0.sdk/System/Library/Frameworks/IOKit.framework; sourceTree = DEVELOPER_DIR; };
549EB132286A2B9D009D38AB /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.0.sdk/System/Library/Frameworks/IOKit.framework; sourceTree = DEVELOPER_DIR; };
54E42BB8286A1696000E1E9A /* TinyGPUApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TinyGPUApp.swift; sourceTree = "<group>"; };
54E42BB9286A1696000E1E9A /* TinyGPUView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TinyGPUView.swift; sourceTree = "<group>"; };
54E42BBA286A1697000E1E9A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
54E42BC4286A1697000E1E9A /* TinyGPU.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TinyGPU.app; sourceTree = BUILT_PRODUCTS_DIR; };
54E42BC6286A1697000E1E9A /* macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = macOS.entitlements; sourceTree = "<group>"; };
C5B7D9BC26128AC50089B4C3 /* org.tinygrad.tinygpu.edriver.dext */ = {isa = PBXFileReference; explicitFileType = "wrapper.driver-extension"; includeInIndex = 0; path = org.tinygrad.tinygpu.edriver.dext; sourceTree = BUILT_PRODUCTS_DIR; };
C5B7D9BF26128AC50089B4C3 /* DriverKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DriverKit.framework; path = Library/Frameworks/DriverKit.framework; sourceTree = DEVELOPER_DIR; };
C5B7D9C226128AC50089B4C3 /* TinyGPUDriver.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TinyGPUDriver.cpp; sourceTree = "<group>"; usesTabs = 1; };
C5B7D9C426128AC50089B4C3 /* TinyGPUDriver.iig */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.iig; path = TinyGPUDriver.iig; sourceTree = "<group>"; };
C5B7D9C626128AC50089B4C3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C5B7D9CC26128ADA0089B4C3 /* AudioDriverKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioDriverKit.framework; path = System/DriverKit/System/Library/Frameworks/AudioDriverKit.framework; sourceTree = SDKROOT; };
C5B7D9CE26128B150089B4C3 /* TinyGPUDriver.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = TinyGPUDriver.entitlements; sourceTree = "<group>"; };
C5C0063326178F98003345D8 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/AppKit.framework; sourceTree = DEVELOPER_DIR; };
C5C006352617ACB8003345D8 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/CoreAudio.framework; sourceTree = DEVELOPER_DIR; };
C5C3BBB12612ACD3003C7BFE /* AudioDriverKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioDriverKit.framework; path = Platforms/DriverKit.platform/Developer/SDKs/DriverKit.MacOSX21.0.Internal.sdk/System/DriverKit/System/Library/Frameworks/AudioDriverKit.framework; sourceTree = DEVELOPER_DIR; };
C5C3BBB42612ACEF003C7BFE /* DriverKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DriverKit.framework; path = Platforms/DriverKit.platform/Developer/SDKs/DriverKit.MacOSX21.0.Internal.sdk/System/DriverKit/System/Library/Frameworks/DriverKit.framework; sourceTree = DEVELOPER_DIR; };
C5D787AB261667FC006047E5 /* TinyGPUDriverUserClient.iig */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.iig; path = TinyGPUDriverUserClient.iig; sourceTree = "<group>"; };
C5D787AD26168D1E006047E5 /* TinyGPUDriverUserClient.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TinyGPUDriverUserClient.cpp; sourceTree = "<group>"; };
C5D787B026169723006047E5 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/IOKit.framework; sourceTree = DEVELOPER_DIR; };
C5D787B22616973F006047E5 /* SystemExtensions.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemExtensions.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/SystemExtensions.framework; sourceTree = DEVELOPER_DIR; };
C5D787B426169747006047E5 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
54E42BC1286A1697000E1E9A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
54798269286A3512009785F6 /* CoreAudio.framework in Frameworks */,
549EB131286A2B98009D38AB /* IOKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
C5B7D9B926128AC50089B4C3 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
C5C3BBB32612ACDC003C7BFE /* AudioDriverKit.framework in Frameworks */,
C5C3BBB52612ACEF003C7BFE /* DriverKit.framework in Frameworks */,
0ACB55392E9CB880007029EF /* PCIDriverKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
54E42BB7286A1696000E1E9A /* Shared */ = {
isa = PBXGroup;
children = (
54E42BB8286A1696000E1E9A /* TinyGPUApp.swift */,
54E42BB9286A1696000E1E9A /* TinyGPUView.swift */,
549EB11F286A1A37009D38AB /* TinyGPUViewModel.swift */,
54E42BBA286A1697000E1E9A /* Assets.xcassets */,
);
path = Shared;
sourceTree = "<group>";
};
54E42BC5286A1697000E1E9A /* macOS */ = {
isa = PBXGroup;
children = (
54E42BC6286A1697000E1E9A /* macOS.entitlements */,
);
path = macOS;
sourceTree = "<group>";
};
C5B7D9B226128AC50089B4C3 = {
isa = PBXGroup;
children = (
C5B7D9C126128AC50089B4C3 /* TinyGPUDriverExtension */,
54E42BB7286A1696000E1E9A /* Shared */,
54E42BC5286A1697000E1E9A /* macOS */,
C5B7D9BE26128AC50089B4C3 /* Frameworks */,
C5B7D9BD26128AC50089B4C3 /* Products */,
);
sourceTree = "<group>";
usesTabs = 1;
};
C5B7D9BD26128AC50089B4C3 /* Products */ = {
isa = PBXGroup;
children = (
C5B7D9BC26128AC50089B4C3 /* org.tinygrad.tinygpu.edriver.dext */,
54E42BC4286A1697000E1E9A /* TinyGPU.app */,
);
name = Products;
sourceTree = "<group>";
};
C5B7D9BE26128AC50089B4C3 /* Frameworks */ = {
isa = PBXGroup;
children = (
0ACB55382E9CB880007029EF /* PCIDriverKit.framework */,
54798268286A3512009785F6 /* CoreAudio.framework */,
549EB130286A2B98009D38AB /* IOKit.framework */,
549EB132286A2B9D009D38AB /* IOKit.framework */,
C5C006352617ACB8003345D8 /* CoreAudio.framework */,
C5C0063326178F98003345D8 /* AppKit.framework */,
C5D787B426169747006047E5 /* Foundation.framework */,
C5D787B22616973F006047E5 /* SystemExtensions.framework */,
C5D787B026169723006047E5 /* IOKit.framework */,
C5C3BBB42612ACEF003C7BFE /* DriverKit.framework */,
C5B7D9CC26128ADA0089B4C3 /* AudioDriverKit.framework */,
C5C3BBB12612ACD3003C7BFE /* AudioDriverKit.framework */,
C5B7D9BF26128AC50089B4C3 /* DriverKit.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
C5B7D9C126128AC50089B4C3 /* TinyGPUDriverExtension */ = {
isa = PBXGroup;
children = (
C5B7D9C226128AC50089B4C3 /* TinyGPUDriver.cpp */,
C5B7D9C426128AC50089B4C3 /* TinyGPUDriver.iig */,
C5D787AD26168D1E006047E5 /* TinyGPUDriverUserClient.cpp */,
C5D787AB261667FC006047E5 /* TinyGPUDriverUserClient.iig */,
C5B7D9C626128AC50089B4C3 /* Info.plist */,
C5B7D9CE26128B150089B4C3 /* TinyGPUDriver.entitlements */,
);
path = TinyGPUDriverExtension;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
C5B7D9B726128AC50089B4C3 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
54E42BC3286A1697000E1E9A /* TinyGPU Installer (macOS) */ = {
isa = PBXNativeTarget;
buildConfigurationList = 54E42BD2286A1697000E1E9A /* Build configuration list for PBXNativeTarget "TinyGPU Installer (macOS)" */;
buildPhases = (
54E42BC0286A1697000E1E9A /* Sources */,
54E42BC1286A1697000E1E9A /* Frameworks */,
54E42BC2286A1697000E1E9A /* Resources */,
549EB122286A1D3A009D38AB /* Embed System Extensions */,
);
buildRules = (
);
dependencies = (
549EB127286A1D66009D38AB /* PBXTargetDependency */,
);
name = "TinyGPU Installer (macOS)";
productName = "SimpleAudioDriverExtension2 (macOS)";
productReference = 54E42BC4286A1697000E1E9A /* TinyGPU.app */;
productType = "com.apple.product-type.application";
};
C5B7D9BB26128AC50089B4C3 /* TinyGPUDriver */ = {
isa = PBXNativeTarget;
buildConfigurationList = C5B7D9C926128AC50089B4C3 /* Build configuration list for PBXNativeTarget "TinyGPUDriver" */;
buildPhases = (
C5B7D9B726128AC50089B4C3 /* Headers */,
C5B7D9B826128AC50089B4C3 /* Sources */,
C5B7D9B926128AC50089B4C3 /* Frameworks */,
C5B7D9BA26128AC50089B4C3 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = TinyGPUDriver;
productName = SimpleAudioDriverExtension;
productReference = C5B7D9BC26128AC50089B4C3 /* org.tinygrad.tinygpu.edriver.dext */;
productType = "com.apple.product-type.driver-extension";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
C5B7D9B326128AC50089B4C3 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
DefaultBuildSystemTypeForWorkspace = Latest;
LastSwiftUpdateCheck = 1400;
LastUpgradeCheck = 1600;
ORGANIZATIONNAME = Apple;
TargetAttributes = {
54E42BC3286A1697000E1E9A = {
CreatedOnToolsVersion = 14.0;
LastSwiftMigration = 1400;
};
C5B7D9BB26128AC50089B4C3 = {
CreatedOnToolsVersion = 13.0;
};
};
};
buildConfigurationList = C5B7D9B626128AC50089B4C3 /* Build configuration list for PBXProject "TinyGPUDriverExtension" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = C5B7D9B226128AC50089B4C3;
productRefGroup = C5B7D9BD26128AC50089B4C3 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
C5B7D9BB26128AC50089B4C3 /* TinyGPUDriver */,
54E42BC3286A1697000E1E9A /* TinyGPU Installer (macOS) */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
54E42BC2286A1697000E1E9A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
54E42BCC286A1697000E1E9A /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
C5B7D9BA26128AC50089B4C3 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
54E42BC0286A1697000E1E9A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
549EB121286A1A37009D38AB /* TinyGPUViewModel.swift in Sources */,
54E42BCA286A1697000E1E9A /* TinyGPUView.swift in Sources */,
54E42BC8286A1697000E1E9A /* TinyGPUApp.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
C5B7D9B826128AC50089B4C3 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C5B7D9C526128AC50089B4C3 /* TinyGPUDriver.iig in Sources */,
C5D787AE26168E59006047E5 /* TinyGPUDriverUserClient.cpp in Sources */,
C5D787AC261667FC006047E5 /* TinyGPUDriverUserClient.iig in Sources */,
C5B7D9C326128AC50089B4C3 /* TinyGPUDriver.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
549EB127286A1D66009D38AB /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = C5B7D9BB26128AC50089B4C3 /* TinyGPUDriver */;
targetProxy = 549EB126286A1D66009D38AB /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
54E42BCF286A1697000E1E9A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements;
CODE_SIGN_IDENTITY = "-";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 9YG3G8543N;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.1;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = org.tinygrad.tinygpu.installer;
PRODUCT_NAME = TinyGPU;
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
54E42BD0286A1697000E1E9A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements;
CODE_SIGN_IDENTITY = "-";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 9YG3G8543N;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.1;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = org.tinygrad.tinygpu.installer;
PRODUCT_NAME = TinyGPU;
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
};
name = Release;
};
C5B7D9C726128AC50089B4C3 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
DRIVERKIT_DEPLOYMENT_TARGET = 21.0;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = driverkit;
};
name = Debug;
};
C5B7D9C826128AC50089B4C3 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DRIVERKIT_DEPLOYMENT_TARGET = 21.0;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = driverkit;
SWIFT_COMPILATION_MODE = wholemodule;
};
name = Release;
};
C5B7D9CA26128AC50089B4C3 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
AD_HOC_CODE_SIGNING_ALLOWED = YES;
CODE_SIGN_ENTITLEMENTS = TinyGPUDriverExtension/TinyGPUDriver.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 9YG3G8543N;
DRIVERKIT_DEPLOYMENT_TARGET = 21.0;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(SDKROOT)/System/DriverKit/System/Library/Frameworks",
);
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = TinyGPUDriverExtension/Info.plist;
INFOPLIST_KEY_OSBundleUsageDescription = "Sample Code Audio Driver Kit Extension";
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = org.tinygrad.tinygpu.edriver;
PRODUCT_NAME = "$(inherited)";
PROVISIONING_PROFILE_SPECIFIER = "";
RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = driverkit;
SKIP_INSTALL = YES;
};
name = Debug;
};
C5B7D9CB26128AC50089B4C3 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
AD_HOC_CODE_SIGNING_ALLOWED = YES;
CODE_SIGN_ENTITLEMENTS = TinyGPUDriverExtension/TinyGPUDriver.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 9YG3G8543N;
DRIVERKIT_DEPLOYMENT_TARGET = 21.0;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(SDKROOT)/System/DriverKit/System/Library/Frameworks",
);
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = TinyGPUDriverExtension/Info.plist;
INFOPLIST_KEY_OSBundleUsageDescription = "Sample Code Audio Driver Kit Extension";
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = org.tinygrad.tinygpu.edriver;
PRODUCT_NAME = "$(inherited)";
PROVISIONING_PROFILE_SPECIFIER = "";
RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = driverkit;
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
54E42BD2286A1697000E1E9A /* Build configuration list for PBXNativeTarget "TinyGPU Installer (macOS)" */ = {
isa = XCConfigurationList;
buildConfigurations = (
54E42BCF286A1697000E1E9A /* Debug */,
54E42BD0286A1697000E1E9A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C5B7D9B626128AC50089B4C3 /* Build configuration list for PBXProject "TinyGPUDriverExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C5B7D9C726128AC50089B4C3 /* Debug */,
C5B7D9C826128AC50089B4C3 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C5B7D9C926128AC50089B4C3 /* Build configuration list for PBXNativeTarget "TinyGPUDriver" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C5B7D9CA26128AC50089B4C3 /* Debug */,
C5B7D9CB26128AC50089B4C3 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = C5B7D9B326128AC50089B4C3 /* Project object */;
}
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildSystemType</key>
<string>Latest</string>
<key>DerivedDataLocationStyle</key>
<string>Default</string>
</dict>
</plist>
@@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IOKitPersonalities</key>
<dict>
<key>TinyGPUDriver</key>
<dict>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>IOClass</key>
<string>IOUserService</string>
<key>IOMatchCategory</key>
<string>TinyGPUDriver</string>
<key>IOPCIClassMatch</key>
<string>0x03000000</string>
<key>IOPCITunnelCompatible</key>
<true/>
<key>IOProviderClass</key>
<string>IOPCIDevice</string>
<key>IOResourceMatch</key>
<string>IOKit</string>
<key>IOUserClass</key>
<string>TinyGPUDriver</string>
<key>IOUserServerName</key>
<string>org.tinygrad.tinygpu.Driver</string>
<key>TinyGPUDriverUserClientProperties</key>
<dict>
<key>IOClass</key>
<string>IOUserUserClient</string>
<key>IOUserClass</key>
<string>TinyGPUDriverUserClient</string>
</dict>
</dict>
</dict>
</dict>
</plist>
@@ -1,259 +0,0 @@
#include "TinyGPUDriver.h"
#include "TinyGPUDriverUserClient.h"
#include <AudioDriverKit/AudioDriverKit.h>
#include <DriverKit/IOUserServer.h>
#include <DriverKit/IOLib.h>
#include <DriverKit/OSString.h>
#include <DriverKit/IOMemoryMap.h>
#include <DriverKit/IODMACommand.h>
#include <DriverKit/IODispatchQueue.h>
#include <PCIDriverKit/PCIDriverKit.h>
#include <DriverKit/OSAction.h>
struct TinyGPUDriver_IVars
{
IOPCIDevice *pci = nullptr;
};
bool TinyGPUDriver::init()
{
os_log(OS_LOG_DEFAULT, "tinygpu: init");
auto answer = super::init();
if (!answer) {
return false;
}
ivars = new TinyGPUDriver_IVars();
if (ivars == nullptr) {
return false;
}
return true;
}
void TinyGPUDriver::free()
{
if (ivars != nullptr) {
}
IOSafeDeleteNULL(ivars, TinyGPUDriver_IVars, 1);
super::free();
}
kern_return_t TinyGPUDriver::Start_Impl(IOService* in_provider)
{
IOServiceName service_name;
os_log(OS_LOG_DEFAULT, "tinygpu: on gpu detected");
kern_return_t err = Start(in_provider, SUPERDISPATCH);
if (err) return err;
ivars->pci = OSDynamicCast(IOPCIDevice, in_provider);
if (!ivars->pci) return kIOReturnNoDevice;
err = ivars->pci->Open(this, 0);
if (err) {
os_log(OS_LOG_DEFAULT, "tinygpu: Open() failed 0x%08x", err);
ivars->pci = nullptr;
return err;
}
uint16_t ven = 0, dev = 0;
ivars->pci->ConfigurationRead16(kIOPCIConfigurationOffsetVendorID, &ven);
ivars->pci->ConfigurationRead16(kIOPCIConfigurationOffsetDeviceID, &dev);
os_log(OS_LOG_DEFAULT, "tinygpu: opened device ven=0x%04x dev=0x%04x", ven, dev);
#if 0
uint32_t off = 0x100;
while (off) {
uint32_t hdr = 0, next = 0, cap_id = 0;
ivars->pci->ConfigurationRead32(off, &hdr);
cap_id = hdr & 0xFFFFu;
next = (hdr >> 20) & 0xFFCu;
os_log(OS_LOG_DEFAULT, "tinygpu: cap: %u", cap_id);
if (cap_id == 0x15) {
uint32_t cap = 0, ctrl = 0;
ivars->pci->ConfigurationRead32(off+0x4, &cap);
ivars->pci->ConfigurationRead32(off+0x8, &ctrl);
uint32_t new_bar_size = 31 - __builtin_clz(cap >> 4);
uint32_t new_ctrl = (ctrl & ~0x1f00) | (new_bar_size << 8);
ivars->pci->ConfigurationWrite32(off+0x8, new_ctrl);
os_log(OS_LOG_DEFAULT, "tinygpu: rebar: cap=%u ctrl=%u new_bar_size=%u new_ctrl=%u", cap, ctrl, new_bar_size, new_ctrl);
ivars->pci->Reset(0);
break;
}
off = next;
}
ivars->pci->Reset(kIOPCIDeviceResetTypeHotReset);
#endif
uint16_t commandRegister;
ivars->pci->ConfigurationRead16(kIOPCIConfigurationOffsetCommand, &commandRegister);
commandRegister |= (kIOPCICommandIOSpace | kIOPCICommandBusMaster | kIOPCICommandMemorySpace);
ivars->pci->ConfigurationWrite16(kIOPCIConfigurationOffsetCommand, commandRegister);
memcpy((void*)service_name, (void*)"tinygpu\0", 8);
SetName(service_name);
os_log(OS_LOG_DEFAULT, "tinygpu: will register service %s", service_name);
RegisterService();
os_log(OS_LOG_DEFAULT, "tinygpu: service started %s", service_name);
return 0;
}
kern_return_t TinyGPUDriver::Stop_Impl(IOService* in_provider)
{
ivars->pci->Close(this, 0);
return 0;
}
kern_return_t TinyGPUDriver::NewUserClient_Impl(uint32_t in_type, IOUserClient** out_user_client)
{
kern_return_t err = 0;
IOService* user_client_service = nullptr;
err = Create(this, "TinyGPUDriverUserClientProperties", &user_client_service);
if (err) {
os_log(OS_LOG_DEFAULT, "tinygpu: failed to create NewUserClient");
goto error;
}
*out_user_client = OSDynamicCast(IOUserClient, user_client_service);
os_log(OS_LOG_DEFAULT, "tinygpu: NewUserClient created");
error:
return err;
}
kern_return_t TinyGPUDriver::MapBar(uint32_t bar, IOMemoryDescriptor** memory)
{
kern_return_t err = 0;
uint8_t barMemoryIndex, barMemoryType;
uint64_t barMemorySize;
err = ivars->pci->GetBARInfo(bar, &barMemoryIndex, &barMemorySize, &barMemoryType);
if (err) return err;
os_log(OS_LOG_DEFAULT, "tinygpu: requested bar mapping %d, %d", bar, (uint32_t)barMemoryIndex);
err = ivars->pci->_CopyDeviceMemoryWithIndex(barMemoryIndex, memory, this);
return err;
}
kern_return_t TinyGPUDriver::CreateDMA(size_t size, TinyGPUCreateDMAResp* dmaDesc)
{
kern_return_t err = 0;
IOMemoryMap* memoryMap = nullptr;
IOBufferMemoryDescriptor* sharedBuf = nullptr;
IODMACommand* dmaCmd = nullptr;
uint64_t flags = kIOMemoryDirectionInOut;
uint32_t segCount = 32;
IOAddressSegment segments[32];
IODMACommandSpecification dmaSpec = {
.options = 0,
.maxAddressBits = 40,
};
err = IOBufferMemoryDescriptor::Create(kIOMemoryDirectionInOut, size, IOVMPageSize, &sharedBuf);
if (err) {
os_log(OS_LOG_DEFAULT, "tinygpu: failed to alloc user buffer, err=%d", err);
goto error;
}
err = IODMACommand::Create(ivars->pci, kIODMACommandCreateNoOptions, &dmaSpec, &dmaCmd);
if (err) {
os_log(OS_LOG_DEFAULT, "tinygpu: failed to create dma command, err=%d", err);
goto error;
}
err = dmaCmd->PrepareForDMA(kIODMACommandPrepareForDMANoOptions, sharedBuf, 0, size,
&flags, &segCount, segments);
if (err) {
os_log(OS_LOG_DEFAULT, "tinygpu: failed to prepare for dma, err=%d", err);
goto error;
}
// pass addresses to userland
{
// debug
for (int i = 0; i < segCount; i++) {
os_log(OS_LOG_DEFAULT, "tinygpu: new dma mapping (sz=0x%zx) %d 0x%llx 0x%llx", size, i, segments[i].address, segments[i].length);
}
err = sharedBuf->CreateMapping(0, 0, 0, IOVMPageSize, IOVMPageSize, &memoryMap); // one page should be fine
if (err) {
os_log(OS_LOG_DEFAULT, "tinygpu: failed to map memory, err=%d", err);
goto error;
}
// Send back gpu addresses
uint64_t* addr = (uint64_t*)memoryMap->GetAddress();
for (int i = 0; i < segCount; i++) {
addr[i * 2] = segments[i].address;
addr[i * 2 + 1] = segments[i].length;
}
addr[segCount * 2] = 0;
addr[segCount * 2 + 1] = 0;
// free memoryMap
memoryMap->release();
memoryMap = nullptr;
}
dmaDesc->sharedBuf = sharedBuf;
dmaDesc->dmaCmd = dmaCmd;
return 0;
error:
if (memoryMap) {
memoryMap->release();
memoryMap = nullptr;
}
if (dmaCmd) {
dmaCmd->CompleteDMA(kIODMACommandCompleteDMANoOptions);
dmaCmd->release();
dmaCmd = nullptr;
}
if (sharedBuf) {
sharedBuf->release();
sharedBuf = nullptr;
}
return err;
}
kern_return_t TinyGPUDriver::CfgRead(uint32_t off, uint32_t size, uint32_t* outVal)
{
if (!ivars->pci || !outVal) return kIOReturnNotReady;
if (size == 1) {
uint8_t v8 = 0;
ivars->pci->ConfigurationRead8(off, &v8);
*outVal = v8;
} else if (size == 2) {
uint16_t v16 = 0;
ivars->pci->ConfigurationRead16(off, &v16);
*outVal = v16;
} else if (size == 4) {
uint32_t v32 = 0;
ivars->pci->ConfigurationRead32(off, &v32);
*outVal = v32;
}
return 0;
}
kern_return_t TinyGPUDriver::CfgWrite(uint32_t off, uint32_t size, uint32_t val)
{
if (!ivars->pci) return kIOReturnNotReady;
if (size == 1) ivars->pci->ConfigurationWrite8 (off, (uint8_t)val);
else if (size == 2) ivars->pci->ConfigurationWrite16(off, (uint16_t)val);
else if (size == 4) ivars->pci->ConfigurationWrite32(off, (uint32_t)val);
return 0;
}
kern_return_t TinyGPUDriver::ResetDevice()
{
if (!ivars->pci) return kIOReturnNotReady;
ivars->pci->Reset(kIOPCIDeviceResetTypeFunctionReset);
return 0;
}
@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.driverkit.transport.pci</key>
<array>
<dict>
<key>IOPCIMatch</key>
<string>0x00001002&amp;0x0000FFFF</string>
</dict>
<dict>
<key>IOPCIMatch</key>
<string>0x000010de&amp;0x0000FFFF</string>
</dict>
</array>
<key>com.apple.developer.driverkit.allow-any-userclient-access</key>
<true/>
<key>com.apple.developer.driverkit</key>
<true/>
</dict>
</plist>
@@ -1,38 +0,0 @@
#ifndef TinyGPUDriver_h
#define TinyGPUDriver_h
#include <Availability.h>
#include <DriverKit/IOService.iig>
#include <PCIDriverKit/IOPCIDevice.iig>
#include <DriverKit/IOMemoryMap.iig>
#include <DriverKit/IODMACommand.iig>
struct TinyGPUCreateDMAResp
{
IOBufferMemoryDescriptor* sharedBuf;
IODMACommand* dmaCmd;
};
class TinyGPUDriver: public IOService
{
public:
virtual bool init() override;
virtual void free() override;
virtual kern_return_t Start(IOService * provider) override;
virtual kern_return_t Stop(IOService * provider) override;
virtual kern_return_t NewUserClient(uint32_t in_type, IOUserClient** out_user_client) override;
kern_return_t MapBar(uint32_t bar, IOMemoryDescriptor** memory) LOCALONLY;
kern_return_t CreateDMA(size_t size, TinyGPUCreateDMAResp* dmaDesc) LOCALONLY;
kern_return_t CfgRead(uint32_t off, uint32_t size, uint32_t* val) LOCALONLY;
kern_return_t CfgWrite(uint32_t off, uint32_t size, uint32_t val) LOCALONLY;
kern_return_t ResetDevice() LOCALONLY;
kern_return_t BarInfo() LOCALONLY;
};
#endif /* TinyGPUDriver_h */
@@ -1,161 +0,0 @@
#include "TinyGPUDriverUserClient.h"
#include "TinyGPUDriver.h"
#include <DriverKit/DriverKit.h>
#include <DriverKit/OSSharedPtr.h>
#include <PCIDriverKit/PCIDriverKit.h>
struct TinyGPUDriverUserClient_IVars
{
OSSharedPtr<TinyGPUDriver> provider = nullptr;
TinyGPUCreateDMAResp *dmas = nullptr;
size_t dmaCount = 0;
size_t dmaCap = 0;
int ensureDMACap(size_t need)
{
// not thread-safe
if (need <= dmaCap) return 0;
size_t newCap = dmaCap ? dmaCap * 2 : 16;
while (newCap < need) newCap *= 2;
auto *newArr = IONewZero(TinyGPUCreateDMAResp, newCap);
if (!newArr) return -kIOReturnNoMemory;
if (dmas && dmaCount) {
memcpy(newArr, dmas, dmaCount * sizeof(TinyGPUCreateDMAResp));
}
IOSafeDeleteNULL(dmas, TinyGPUCreateDMAResp, dmaCap);
dmas = newArr;
dmaCap = newCap;
return 0;
}
};
bool TinyGPUDriverUserClient::init()
{
auto ok = super::init();
if (!ok) return false;
ivars = IONewZero(TinyGPUDriverUserClient_IVars, 1);
if (!ivars) return false;
return true;
}
void TinyGPUDriverUserClient::free()
{
if (ivars) {
IOSafeDeleteNULL(ivars, TinyGPUDriverUserClient_IVars, 1);
}
super::free();
}
kern_return_t TinyGPUDriverUserClient::Start_Impl(IOService* in_provider)
{
kern_return_t err = kIOReturnSuccess;
if (!in_provider) {
os_log(OS_LOG_DEFAULT, "tinygpu: provider is null");
err = kIOReturnBadArgument;
goto error;
}
err = Start(in_provider, SUPERDISPATCH);
if (err) {
os_log(OS_LOG_DEFAULT, "tinygpu: failed to start super (%d)", err);
goto error;
}
ivars->provider = OSSharedPtr(OSDynamicCast(TinyGPUDriver, in_provider), OSRetain);
return 0;
error:
ivars->provider.reset();
return err;
}
kern_return_t TinyGPUDriverUserClient::Stop_Impl(IOService* in_provider)
{
// release all DMA allocations for this client
if (ivars) {
for (size_t i = 0; i < ivars->dmaCount; i++) {
auto &d = ivars->dmas[i];
if (d.dmaCmd) {
d.dmaCmd->CompleteDMA(kIODMACommandCompleteDMANoOptions);
d.dmaCmd->release();
d.dmaCmd = nullptr;
}
}
ivars->dmaCount = 0;
IOSafeDeleteNULL(ivars->dmas, TinyGPUCreateDMAResp, ivars->dmaCap);
ivars->dmas = nullptr;
ivars->provider.reset();
}
return Stop(in_provider, SUPERDISPATCH);
}
kern_return_t TinyGPUDriverUserClient::ExternalMethod(uint64_t selector, IOUserClientMethodArguments* args, const IOUserClientMethodDispatch* in_dispatch, OSObject* in_target, void* in_reference)
{
kern_return_t err = 0;
os_log(OS_LOG_DEFAULT, "tinygpu: rpc (%llu) in:%d, out:%d", selector, args->scalarInputCount, args->scalarOutputCount);
if (selector == TinyGPURPC::ReadCfg) {
if (args->scalarInputCount != 2 or args->scalarOutputCount < 1) return kIOReturnBadArgument;
uint32_t off = uint32_t(args->scalarInput[0]);
uint32_t size = uint32_t(args->scalarInput[1]);
uint32_t val = 0;
err = ivars->provider->CfgRead(off, size, &val);
os_log(OS_LOG_DEFAULT, "tinygpu: read cfg off:%x sz:%d, val:%x", off, size, val);
if (!err) {
args->scalarOutput[0] = val;
args->scalarOutputCount = 1;
}
return err;
} else if (selector == TinyGPURPC::WriteCfg) {
if (args->scalarInputCount != 3) return kIOReturnBadArgument;
uint32_t off = uint32_t(args->scalarInput[0]);
uint32_t size = uint32_t(args->scalarInput[1]);
uint32_t val = uint32_t(args->scalarInput[2]);
os_log(OS_LOG_DEFAULT, "tinygpu: wr cfg off:%x sz:%d, val:%x", off, size, val);
return ivars->provider->CfgWrite(off, size, val);
} else if (selector == TinyGPURPC::Reset) {
os_log(OS_LOG_DEFAULT, "tinygpu: reset");
return ivars->provider->ResetDevice();
}
return kIOReturnUnsupported;
}
kern_return_t IMPL(TinyGPUDriverUserClient, CopyClientMemoryForType)
{
if (!memory) return kIOReturnBadArgument;
if (!ivars->provider.get()) return kIOReturnNotAttached;
// bar handling, type is bar num
if (type < 6) {
uint32_t bar = (uint32_t)type;
return ivars->provider->MapBar(bar, memory);
}
// dma handling, type is size
if (ivars->ensureDMACap(ivars->dmaCount + 1)) {
os_log(OS_LOG_DEFAULT, "tinygpu: cannot grow dma array");
return kIOReturnNoMemory;
}
TinyGPUCreateDMAResp buf{};
kern_return_t err = ivars->provider->CreateDMA(type, &buf);
if (err) return err;
ivars->dmas[ivars->dmaCount++] = buf;
*memory = buf.sharedBuf;
return 0;
}
@@ -1,28 +0,0 @@
#ifndef TinyGPUDriverUserClient_h
#define TinyGPUDriverUserClient_h
#include <DriverKit/IOUserClient.iig>
enum TinyGPURPC
{
ReadCfg,
WriteCfg,
Reset
};
class TinyGPUDriverUserClient : public IOUserClient
{
public:
virtual bool init() final;
virtual void free() final;
virtual kern_return_t Start(IOService* in_provider) final;
virtual kern_return_t Stop(IOService* in_provider) final;
virtual kern_return_t ExternalMethod(uint64_t in_selector, IOUserClientMethodArguments* in_arguments, const IOUserClientMethodDispatch* in_dispatch, OSObject* in_target, void* in_reference) final;
virtual kern_return_t CopyClientMemoryForType(
uint64_t type, uint64_t *options, IOMemoryDescriptor **memory) final;
};
#endif /* TinyGPUDriverUserClient_h */
@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<false/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<key>com.apple.developer.system-extension.install</key>
<true/>
</dict>
</plist>
-39
View File
@@ -1,39 +0,0 @@
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <mach/mach.h>
#include <stdio.h>
#include <inttypes.h>
static io_connect_t open_uc_by_name(const char *svc_name) {
io_connect_t conn = IO_OBJECT_NULL;
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceNameMatching(svc_name));
if (!service) { fprintf(stderr, "service not found: %s\n", svc_name); return IO_OBJECT_NULL; }
kern_return_t kr = IOServiceOpen(service, mach_task_self(), /*type*/0, &conn);
IOObjectRelease(service);
if (kr) { fprintf(stderr, "IOServiceOpen 0x%x\n", kr); return IO_OBJECT_NULL; }
return conn;
}
int main(int argc, char **argv) {
uint32_t bar = (argc > 1) ? (uint32_t)strtoul(argv[1], NULL, 0) : 0; // pick BAR index
io_connect_t conn = open_uc_by_name("tinygpu");
if (!conn) return 2;
mach_vm_address_t addr = 0;
mach_vm_size_t size = 0;
kern_return_t kr = IOConnectMapMemory64(conn, bar, mach_task_self(), &addr, &size, kIOMapAnywhere);
if (kr) { fprintf(stderr, "Map BAR%u failed 0x%x\n", bar, kr); IOServiceClose(conn); return 3; }
printf("BAR%u mapped at 0x%llx, size 0x%llx\n", bar, (unsigned long long)addr, (unsigned long long)size);
// example: read a 32-bit register at offset 0x0 (make sure its safe!)
volatile uint32_t *mmio = (volatile uint32_t*)(uintptr_t)addr;
uint32_t v = mmio[0];
printf("mmio[0]=0x%08x\n", v);
kr = IOConnectUnmapMemory64(conn, bar, mach_task_self(), addr);
if (kr) fprintf(stderr, "Unmap failed 0x%x\n", kr);
IOServiceClose(conn);
return 0;
}
-82
View File
@@ -1,82 +0,0 @@
import ctypes, ctypes.util, sys
cf = ctypes.CDLL(ctypes.util.find_library("CoreFoundation"))
iokit = ctypes.CDLL(ctypes.util.find_library("IOKit"))
libsys = ctypes.CDLL(ctypes.util.find_library("System"))
kern_return_t = ctypes.c_int
mach_port_t = ctypes.c_uint
io_object_t = mach_port_t
io_service_t = io_object_t
io_connect_t = mach_port_t
CFMutableDictionaryRef = ctypes.c_void_p
CFStringRef = ctypes.c_void_p
kIOMasterPortDefault = mach_port_t(0)
libsys.mach_task_self_.restype = mach_port_t
iokit.IOServiceNameMatching.argtypes = [ctypes.c_char_p]
iokit.IOServiceNameMatching.restype = CFMutableDictionaryRef
iokit.IOServiceGetMatchingService.argtypes = [mach_port_t, CFMutableDictionaryRef]
iokit.IOServiceGetMatchingService.restype = io_service_t
iokit.IOObjectRelease.argtypes = [io_object_t]
iokit.IOObjectRelease.restype = kern_return_t
iokit.IOServiceOpen.argtypes = [io_service_t, mach_port_t, ctypes.c_uint32, ctypes.POINTER(io_connect_t)]
iokit.IOServiceOpen.restype = kern_return_t
iokit.IOConnectCallMethod.argtypes = [io_connect_t, ctypes.c_uint32, ctypes.POINTER(ctypes.c_uint64), ctypes.c_uint32, ctypes.c_void_p,
ctypes.c_size_t, ctypes.POINTER(ctypes.c_uint64), ctypes.POINTER(ctypes.c_uint32), ctypes.c_void_p, ctypes.POINTER(ctypes.c_size_t)]
iokit.IOConnectCallMethod.restype = kern_return_t
def open_userclient_by_name(name: str, uc_type: int = 0) -> io_connect_t:
mdict = iokit.IOServiceNameMatching(name.encode("utf-8"))
if not mdict: raise RuntimeError("IOServiceNameMatching returned NULL")
# Grab the first matching service
service = iokit.IOServiceGetMatchingService(kIOMasterPortDefault, mdict)
if not service: raise RuntimeError(f'service "{name}" not found')
# print("lol", service)
# print(libsys.mach_task_self_)
# cast libsys.mach_task_self_ to uint and print
# print("lol", ctypes.cast(libsys.mach_task_self_, ctypes.POINTER(ctypes.c_uint)).contents.value)
try:
# Open user client (type -> passed to NewUserClient_Impl)
conn = io_connect_t(0)
# print("lol", libsys.mach_task_self_)
kr = iokit.IOServiceOpen(service, ctypes.cast(libsys.mach_task_self_, ctypes.POINTER(ctypes.c_uint)).contents.value,
ctypes.c_uint32(uc_type), ctypes.byref(conn))
if kr != 0: raise OSError(kr, f"IOServiceOpen failed (0x{kr:08x})")
return conn
finally: iokit.IOObjectRelease(service)
def external_method(conn: io_connect_t, selector: int = 0) -> int:
# no scalars in/out, no struct in/out — just ping selector 0
in_scalars = ctypes.POINTER(ctypes.c_uint64)() # NULL
out_scalars = (ctypes.c_uint64 * 1)() # space if driver returns something
out_scalars_cnt = ctypes.c_uint32(0) # driver can set this
return iokit.IOConnectCallMethod(conn, ctypes.c_uint32(selector), in_scalars, ctypes.c_uint32(0), None, ctypes.c_size_t(0),
out_scalars, ctypes.byref(out_scalars_cnt), None, ctypes.byref(ctypes.c_size_t(0)))
def close_userclient(conn: io_connect_t) -> None:
# IOServiceClose is a macro; exported symbol is IOServiceClose in IOKit
iokit.IOServiceClose.argtypes = [io_connect_t]
iokit.IOServiceClose.restype = kern_return_t
iokit.IOServiceClose(conn)
if __name__ == "__main__":
try:
conn = open_userclient_by_name("tinygpu", uc_type=0)
kr = external_method(conn, selector=0)
print(f"ExternalMethod(0) -> 0x{kr:08x}")
except Exception as e:
print(e)
sys.exit(1)
finally:
if 'conn' in locals() and conn.value: close_userclient(conn)
+2 -2
View File
@@ -1,7 +1,7 @@
# extra/weekly_commits_table.py
import os, subprocess, datetime as dt
NAMES = ["chenyu","George Hotz","nimlgen","qazal","wozeparrot"]
NAMES = ["chenyu","George Hotz","nimlgen","qazal","Sieds Lykles","wozeparrot"]
REPO = os.environ.get("REPO_PATH",".")
today = dt.date.today()
days = [(today - dt.timedelta(i)).strftime("%Y-%m-%d") for i in range(6,-1,-1)]
@@ -40,4 +40,4 @@ for d in days:
print("** Commits by day (last 7) **")
print("```")
print("\n".join([header, rule] + rows))
print("```")
print("```")
+2
View File
@@ -25,6 +25,8 @@ nav:
- Layout: developer/layout.md
- Speed: developer/speed.md
- UOp: developer/uop.md
- Grouper:
- developer/kernelize.md
- Runtime:
- developer/runtime.md
- HCQ: developer/hcq.md
+1 -4
View File
@@ -1,8 +1,5 @@
[pytest]
norecursedirs =
extra
.hypothesis
.git
norecursedirs = extra
timeout = 300
timeout_method = thread
timeout_func_only = true
+1 -1
View File
@@ -1,6 +1,6 @@
indent-width = 2
preview = true
target-version = "py311"
target-version = "py310"
lint.select = [
"F", # Pyflakes
-21
View File
@@ -1,21 +0,0 @@
[mutmut]
paths_to_mutate=tinygrad
do_not_mutate=
tinygrad/apps/*
tinygrad/codegen/*
tinygrad/engine/*
tinygrad/nn/*
tinygrad/renderer/*
tinygrad/runtime/*
tinygrad/schedule/*
tinygrad/uop/*
tinygrad/viz/*
tinygrad/device.py
tinygrad/dtype.py
tinygrad/gradient.py
tinygrad/helpers.py
tinygrad/tensor.py
tests_dir=
test/test_tiny.py
test/test_ops.py
debug=true
+3 -4
View File
@@ -9,11 +9,10 @@ with open(directory / 'README.md', encoding='utf-8') as f:
testing_minimal = [
"numpy",
"torch==2.9.0",
"torch==2.8.0",
"pytest",
"pytest-xdist",
"pytest-timeout",
"pytest-split",
"hypothesis",
"z3-solver",
]
@@ -32,7 +31,6 @@ setup(name='tinygrad',
'tinygrad.codegen.opt',
'tinygrad.codegen.late',
'tinygrad.engine',
'tinygrad.mixin',
'tinygrad.nn',
'tinygrad.renderer',
'tinygrad.runtime',
@@ -44,6 +42,7 @@ setup(name='tinygrad',
'tinygrad.runtime.support.am',
'tinygrad.runtime.support.nv',
'tinygrad.schedule',
'tinygrad.shape',
'tinygrad.uop',
'tinygrad.viz',
],
@@ -53,7 +52,7 @@ setup(name='tinygrad',
"License :: OSI Approved :: MIT License"
],
install_requires=[],
python_requires='>=3.11',
python_requires='>=3.10',
extras_require={
'arm': ["unicorn"],
'triton': ["triton-nightly>=2.1.0.dev20231014192330"],
+2 -7
View File
@@ -54,8 +54,6 @@ def gen_diff(table_old, table_new):
def display_diff(diff): return "+"+str(diff) if diff > 0 else str(diff)
NONCORE_DIRS = {"tinygrad/apps", "tinygrad/nn", "tinygrad/renderer", "tinygrad/runtime", "tinygrad/viz"}
if __name__ == "__main__":
if len(sys.argv) == 3:
headers = ["Name", "Lines", "Diff", "Tokens/Line", "Diff"]
@@ -78,12 +76,9 @@ if __name__ == "__main__":
else:
print(tabulate([headers] + sorted(table, key=lambda x: -x[1]), headers="firstrow", floatfmt=".1f")+"\n")
groups = sorted([('/'.join(x[0].rsplit("/", 1)[0].split("/")[0:2]), x[1], x[2]) for x in table])
dir_sizes = {}
for dir_name, group in itertools.groupby(groups, key=lambda x:x[0]):
dir_sizes[dir_name] = sum([x[1] for x in group])
print(f"{dir_name:30s} : {dir_sizes[dir_name]:6d}")
print(f"\n core line count: {sum([v for k,v in dir_sizes.items() if k not in NONCORE_DIRS])}")
print(f"{dir_name:30s} : {sum([x[1] for x in group]):6d}")
total_lines = sum([x[1] for x in table])
print(f"total line count: {total_lines}")
print(f"\ntotal line count: {total_lines}")
max_line_count = int(os.getenv("MAX_LINE_COUNT", "-1"))
assert max_line_count == -1 or total_lines <= max_line_count, f"OVER {max_line_count} LINES"
+3 -2
View File
@@ -1,4 +1,4 @@
from tinygrad import Tensor, dtypes, GlobalCounters
from tinygrad import Tensor, dtypes, Context, GlobalCounters
dtypes.default_float = dtypes.float16
from tinygrad.dtype import to_dtype
from tinygrad.helpers import getenv
@@ -13,5 +13,6 @@ if __name__ == "__main__":
# test single kernel softmax
GlobalCounters.reset()
single_kernel_softmax(t, -1, acc_dtype).realize()
with Context(DONT_GROUP_REDUCES=1):
single_kernel_softmax(t, -1, acc_dtype).realize()
-163
View File
@@ -1,163 +0,0 @@
# ruff: noqa: E501 E712
from tinygrad import dtypes, Device
from tinygrad.uop.ops import UOp, AxisType, Ops, KernelInfo
from tinygrad.codegen import full_rewrite
from tinygrad.renderer import ProgramSpec
from tinygrad.engine.realize import CompiledRunner
from tinygrad.helpers import dedup
from tinygrad.device import Buffer
from tinygrad.dtype import ImageDType, Invalid
c0 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(1576), (), 0)
c2 = UOp.range(1576, 20, AxisType.LOOP)
c5 = c2<55
c6 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((1, 16, 4)), (), 1)
c8 = UOp.range(16, 0, AxisType.REDUCE)
c11 = UOp.range(4, 1, AxisType.REDUCE)
c14 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((14, 64, 4)), (), 2)
c25 = c5.where((c2%4*4+c11+c8*16+c2//4*256), UOp.const(dtypes.index, Invalid))
c27 = c6.index((c8*4+c11))*c14.index(c25)
c29 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(55), (), 3)
c30 = c5.where(c2, UOp.const(dtypes.index, Invalid))
c34 = c5.where((c27.reduce(c8, c11, arg=Ops.ADD)+c29.index(c30)), UOp.const(dtypes.float, 0.0))
c38 = c2<87
c39 = (c5!=True)&c38
c40 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((1, 8, 4)), (), 4)
c42 = UOp.range(8, 2, AxisType.REDUCE)
c44 = UOp.range(4, 3, AxisType.REDUCE)
c47 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((8, 32, 4)), (), 5)
c49 = c2+1
c51 = c49%4*4
c57 = c49//4*128
c61 = c39.where((c51+c44+c42*16+c57+-1792), UOp.const(dtypes.index, Invalid))
c63 = c40.index((c42*4+c44))*c47.index(c61)
c65 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(32), (), 6)
c68 = c39.where((c2+-55), UOp.const(dtypes.index, Invalid))
c71 = c39.where((c63.reduce(c42, c44, arg=Ops.ADD)+c65.index(c68)), UOp.const(dtypes.float, 0.0))
c75 = c2<99
c76 = (c38!=True)&c75
c77 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((1, 8, 4)), (), 7)
c78 = UOp.range(8, 4, AxisType.REDUCE)
c80 = UOp.range(4, 5, AxisType.REDUCE)
c83 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((3, 32, 4)), (), 8)
c90 = c76.where((c51+c80+c78*16+c57+-2816), UOp.const(dtypes.index, Invalid))
c92 = c77.index((c78*4+c80))*c83.index(c90)
c94 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(12), (), 9)
c97 = c76.where((c2+-87), UOp.const(dtypes.index, Invalid))
c100 = c76.where((c92.reduce(c78, c80, arg=Ops.ADD)+c94.index(c97)), UOp.const(dtypes.float, 0.0))
c104 = c2<105
c105 = (c75!=True)&c104
c106 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((1, 8, 4)), (), 10)
c107 = UOp.range(8, 6, AxisType.REDUCE)
c109 = UOp.range(4, 7, AxisType.REDUCE)
c112 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((2, 32, 4)), (), 11)
c119 = c105.where((c51+c109+c107*16+c57+-3200), UOp.const(dtypes.index, Invalid))
c121 = c106.index((c107*4+c109))*c112.index(c119)
c123 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(6), (), 12)
c126 = c105.where((c2+-99), UOp.const(dtypes.index, Invalid))
c129 = c105.where((c121.reduce(c107, c109, arg=Ops.ADD)+c123.index(c126)), UOp.const(dtypes.float, 0.0))
c133 = c2<117
c134 = (c104!=True)&c133
c135 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((1, 8, 4)), (), 13)
c136 = UOp.range(8, 8, AxisType.REDUCE)
c138 = UOp.range(4, 9, AxisType.REDUCE)
c141 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((3, 32, 4)), (), 14)
c143 = c2+3
c145 = c143%4*4
c149 = c143//4
c150 = c149*128
c154 = c134.where((c145+c138+c136*16+c150+-3456), UOp.const(dtypes.index, Invalid))
c156 = c135.index((c136*4+c138))*c141.index(c154)
c158 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(12), (), 15)
c161 = c134.where((c2+-105), UOp.const(dtypes.index, Invalid))
c164 = c134.where((c156.reduce(c136, c138, arg=Ops.ADD)+c158.index(c161)), UOp.const(dtypes.float, 0.0))
c168 = c2<645
c169 = (c133!=True)&c168
c170 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((1, 16, 4)), (), 16)
c171 = UOp.range(16, 10, AxisType.REDUCE)
c173 = UOp.range(4, 11, AxisType.REDUCE)
c176 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((132, 64, 4)), (), 17)
c180 = c149*256
c184 = c169.where((c145+c173+c171*16+c180+-7680), UOp.const(dtypes.index, Invalid))
c186 = c170.index((c171*4+c173))*c176.index(c184)
c188 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(528), (), 18)
c191 = c169.where((c2+-117), UOp.const(dtypes.index, Invalid))
c194 = c169.where((c186.reduce(c171, c173, arg=Ops.ADD)+c188.index(c191)), UOp.const(dtypes.float, 0.0))
c198 = c2<653
c199 = (c168!=True)&c198
c200 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((1, 4, 4)), (), 19)
c201 = UOp.range(4, 12, AxisType.REDUCE)
c203 = UOp.range(4, 13, AxisType.REDUCE)
c206 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((2, 16, 4)), (), 20)
c215 = c199.where((c145+c203+c201*16+c149*64+-10368), UOp.const(dtypes.index, Invalid))
c217 = c200.index((c201*4+c203))*c206.index(c215)
c219 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(8), (), 21)
c222 = c199.where((c2+-645), UOp.const(dtypes.index, Invalid))
c225 = c199.where((c217.reduce(c201, c203, arg=Ops.ADD)+c219.index(c222)), UOp.const(dtypes.float, 0.0))
c229 = c2<917
c230 = (c198!=True)&c229
c231 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((1, 8, 4)), (), 22)
c232 = UOp.range(8, 14, AxisType.REDUCE)
c234 = UOp.range(4, 15, AxisType.REDUCE)
c237 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((66, 32, 4)), (), 23)
c244 = c230.where((c145+c234+c232*16+c150+-20992), UOp.const(dtypes.index, Invalid))
c246 = c231.index((c232*4+c234))*c237.index(c244)
c248 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(264), (), 24)
c251 = c230.where((c2+-653), UOp.const(dtypes.index, Invalid))
c254 = c230.where((c246.reduce(c232, c234, arg=Ops.ADD)+c248.index(c251)), UOp.const(dtypes.float, 0.0))
c258 = c2<1061
c259 = (c229!=True)&c258
c260 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((1, 16, 4)), (), 25)
c261 = UOp.range(16, 16, AxisType.REDUCE)
c263 = UOp.range(4, 17, AxisType.REDUCE)
c266 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((36, 64, 4)), (), 26)
c273 = c259.where((c145+c263+c261*16+c180+-58880), UOp.const(dtypes.index, Invalid))
c275 = c260.index((c261*4+c263))*c266.index(c273)
c277 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(144), (), 27)
c280 = c259.where((c2+-917), UOp.const(dtypes.index, Invalid))
c283 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(144), (), 28)
c286 = c259.where(((c275.reduce(c261, c263, arg=Ops.ADD)+c277.index(c280))*c283.index(c280)), UOp.const(dtypes.float, 0.0))
c290 = c2<1064
c291 = (c258!=True)&c290
c292 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((1, 4, 4)), (), 29)
c293 = UOp.range(4, 18, AxisType.REDUCE)
c295 = UOp.range(4, 19, AxisType.REDUCE)
c298 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((1, 16, 4)), (), 30)
c305 = c291.where((c2*4+c295+c293*16+-4244), UOp.const(dtypes.index, Invalid))
c307 = c292.index((c293*4+c295))*c298.index(c305)
c309 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(3), (), 31)
c312 = c291.where((c2+-1061), UOp.const(dtypes.index, Invalid))
c315 = c291.where((c307.reduce(c293, c295, arg=Ops.ADD)+c309.index(c312)), UOp.const(dtypes.float, 0.0))
c317 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((1, 128, 4)), (), 32)
c321 = (c290!=True).where((c2+-1064), UOp.const(dtypes.index, Invalid))
c323 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(1), (), 33)
c328 = c290.where(UOp.const(dtypes.float, 0.0), (c317.index(c321)*c323.index(UOp.const(dtypes.index, 0)).reciprocal()))
c329 = c34+c71+c100+c129+c164+c194+c225+c254+c286+c315+c328
c331 = c0.index(c2, ptr=True).store(c329).end(c2)
ast = c331.sink(arg=KernelInfo(name="cat", opts_to_apply=None))
compiler = Device.default.compiler
renderer = Device.default.renderer
allocator = Device.default.allocator
uops = full_rewrite(ast, renderer)
src = renderer.render(uops)
# NOLOCALS=1 IMAGE=2 DEV=CL
lib = compiler.compile(src)
ps = ProgramSpec("cat", src, Device.DEFAULT, ast, uops)
# print(ps.src)
# print(ps.applied_opts)
# NOTE: this is faster with no GROUP and with NOLOCALS
# (Opt(op=OptOps.UPCAST, axis=1, arg=4), Opt(op=OptOps.UNROLL, axis=19, arg=4), Opt(op=OptOps.UNROLL, axis=17, arg=4), Opt(op=OptOps.UNROLL, axis=15, arg=4), Opt(op=OptOps.UNROLL, axis=13, arg=4), Opt(op=OptOps.UNROLL, axis=11, arg=4), Opt(op=OptOps.UNROLL, axis=9, arg=4), Opt(op=OptOps.UNROLL, axis=7, arg=4), Opt(op=OptOps.UNROLL, axis=5, arg=4), Opt(op=OptOps.UNROLL, axis=3, arg=4), Opt(op=OptOps.UNROLL, axis=1, arg=4), Opt(op=OptOps.NOLOCALS, axis=None, arg=None))
cr = CompiledRunner(ps, precompiled=lib)
gs = sorted(dedup([u for u in ast.toposort() if u.op is Ops.DEFINE_GLOBAL]), key=lambda u: u.arg)
print(len(gs))
print([g.dtype for g in gs])
bufs = [Buffer(ps.device, g.size, g.dtype if isinstance(g.dtype, ImageDType) else g.dtype._base).ensure_allocated() for g in gs]
t = cr(bufs, wait=True)
print(f"{t*1e6:.2f} us")
-81
View File
@@ -1,81 +0,0 @@
# ruff: noqa: E501 E712
from tinygrad import dtypes, Device
from tinygrad.uop.ops import UOp, AxisType, Ops, KernelInfo
from tinygrad.codegen import full_rewrite
# from tinygrad.codegen.opt import Opt, OptOps
from tinygrad.renderer import ProgramSpec
from tinygrad.engine.realize import CompiledRunner
from tinygrad.helpers import dedup, getenv
from tinygrad.device import Buffer
from tinygrad.dtype import ImageDType, Invalid
# PYTHONPATH="." DEV=QCOM FLOAT16=1 IMAGE=2 NOLOCALS=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/720392c9a5b986981fdbed1bb8c47a6c5573a50e/selfdrive/modeld/models/driving_vision.onnx
def vision_conv_143():
c0 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((16, 1024, 4)), (), 0)
c2 = UOp.range(32, 3, AxisType.LOOP)
c5 = UOp.range(128, 4, AxisType.LOOP)
c8 = UOp.range(16, 2, AxisType.LOOP)
c16 = UOp.range(7, 0, AxisType.REDUCE)
c17 = c8*2+c16
c24 = ((c17<3)!=True)&(c17<35)
c26 = UOp.range(7, 1, AxisType.REDUCE)
c27 = c2*2+c26
c32 = ((c27<3)!=True)&(c27<67)
c34 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((32, 1024, 4)), (), 1)
c38 = c5//2
c45 = (c32&c24).where((c27*64+c38+c17*4096+-12480), UOp.const(dtypes.index, Invalid))
c48 = (c24&c32).where(c34.index(c45), UOp.const(dtypes.float, 0.0))
c49 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((64, 49, 4)), (), 2)
c61 = c48*c49.index((c26*4+c5%2+c16*28+c38*196))
c63 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(128), (), 3)
c65 = c61.reduce(c16, c26, arg=Ops.ADD)+c63.index(c5)
c67 = c0.index((c2*128+c5+c8*4096), ptr=True).store(c65).end(c8, c2, c5)
opts = None
return c67.sink(arg=KernelInfo(name="conv", opts_to_apply=opts))
def vision_conv_153():
c0 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((8, 1024, 4)), (), 0)
c2 = UOp.range(16, 3, AxisType.LOOP)
c5 = UOp.range(256, 4, AxisType.LOOP)
c8 = UOp.range(8, 2, AxisType.LOOP)
c16 = UOp.range(7, 0, AxisType.REDUCE)
c17 = c8*2+c16
c24 = ((c17<3)!=True)&(c17<19)
c26 = UOp.range(7, 1, AxisType.REDUCE)
c27 = c2*2+c26
c32 = ((c27<3)!=True)&(c27<35)
c34 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((16, 1024, 4)), (), 1)
c38 = c5//2
c45 = (c32&c24).where((c27*128+c38+c17*4096+-12672), UOp.const(dtypes.index, Invalid))
c48 = (c24&c32).where(c34.index(c45), UOp.const(dtypes.float, 0.0))
c49 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((128, 49, 4)), (), 2)
c61 = c48*c49.index((c26*4+c5%2+c16*28+c38*196))
c63 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(256), (), 3)
c65 = c61.reduce(c16, c26, arg=Ops.ADD)+c63.index(c5)
c67 = c0.index((c2*256+c5+c8*4096), ptr=True).store(c65).end(c8, c2, c5)
opts = None
return c67.sink(arg=KernelInfo(name="conv", opts_to_apply=opts))
ast = vision_conv_143() if getenv("NUM", 143) == 143 else vision_conv_153()
compiler = Device.default.compiler
renderer = Device.default.renderer
allocator = Device.default.allocator
uops = full_rewrite(ast, renderer)
src = renderer.render(uops)
lib = compiler.compile(src)
ps = ProgramSpec("conv", src, Device.DEFAULT, ast, uops)
cr = CompiledRunner(ps, precompiled=lib)
gs = sorted(dedup([u for u in ast.toposort() if u.op is Ops.DEFINE_GLOBAL]), key=lambda u: u.arg)
# print(len(gs))
# print([g.dtype for g in gs])
bufs = [Buffer(ps.device, g.size, g.dtype if isinstance(g.dtype, ImageDType) else g.dtype._base).ensure_allocated() for g in gs]
t = cr(bufs, wait=True)
print(f"{t*1e6:.2f} us")
+63
View File
@@ -0,0 +1,63 @@
import time, sys, hashlib
from pathlib import Path
from tinygrad.nn.onnx import OnnxRunner
from tinygrad import Tensor, dtypes, TinyJit
from tinygrad.helpers import IMAGE, GlobalCounters, fetch, colored, getenv, trange
import numpy as np
from extra.bench_log import BenchEvent, WallTimeEvent
OPENPILOT_MODEL = sys.argv[1] if len(sys.argv) > 1 else "https://github.com/commaai/openpilot/raw/v0.9.4/selfdrive/modeld/models/supercombo.onnx"
if __name__ == "__main__":
run_onnx = OnnxRunner(fetch(OPENPILOT_MODEL))
Tensor.manual_seed(100)
input_shapes = {name: spec.shape for name, spec in run_onnx.graph_inputs.items()}
input_types = {name: spec.dtype for name, spec in run_onnx.graph_inputs.items()}
new_inputs = {k:Tensor.randn(*shp, dtype=input_types[k]).mul(8).realize() for k,shp in input_shapes.items()}
new_inputs_junk = {k:Tensor.randn(*shp, dtype=input_types[k]).mul(8).realize() for k,shp in input_shapes.items()}
new_inputs_junk_numpy = {k:v.numpy() for k,v in new_inputs_junk.items()}
# benchmark
for _ in range(5):
GlobalCounters.reset()
st = time.perf_counter_ns()
ret = next(iter(run_onnx(new_inputs_junk).values())).cast(dtypes.float32).numpy()
print(f"unjitted: {(time.perf_counter_ns() - st)*1e-6:7.4f} ms")
# NOTE: the inputs to a JIT must be first level arguments
run_onnx_jit = TinyJit(lambda **kwargs: run_onnx(kwargs), prune=True)
step_times = []
for _ in range(20):
GlobalCounters.reset()
st = time.perf_counter_ns()
with WallTimeEvent(BenchEvent.STEP):
# Need to cast non-image inputs from numpy, this is only realistic way to run model
inputs = {**{k:v for k,v in new_inputs_junk.items() if 'img' in k},
**{k:Tensor(v) for k,v in new_inputs_junk_numpy.items() if 'img' not in k}}
ret = next(iter(run_onnx_jit(**inputs).values())).cast(dtypes.float32).numpy()
step_times.append(t:=(time.perf_counter_ns() - st)*1e-6)
print(f"jitted: {t:7.4f} ms")
suffix = ""
if IMAGE.value < 2: suffix += f"_image{IMAGE.value}" # image=2 has no suffix for compatibility
if getenv("FLOAT16") == 1: suffix += "_float16"
path = Path(__file__).parent / "openpilot" / f"{hashlib.md5(OPENPILOT_MODEL.encode()).hexdigest()}{suffix}.npy"
# validate if we have records
tinygrad_out = next(iter(run_onnx_jit(**new_inputs).values())).cast(dtypes.float32).numpy()
if getenv("SAVE_OUTPUT"):
np.save(path, tinygrad_out)
print(f"saved output to {path}!")
elif getenv("FUZZ") and path.exists():
known_good_out = np.load(path)
for _ in trange(1000):
ret = next(iter(run_onnx_jit(**new_inputs).values())).cast(dtypes.float32).numpy()
np.testing.assert_allclose(known_good_out, ret, atol=1e-2, rtol=1e-2)
print(colored("fuzz validated!", "green"))
elif path.exists():
known_good_out = np.load(path)
np.testing.assert_allclose(known_good_out, tinygrad_out, atol=1e-2, rtol=1e-2)
print(colored("outputs validated!", "green"))
else:
print(colored("skipping validation", "yellow"))
+7 -7
View File
@@ -2,9 +2,8 @@ from extra.models.resnet import ResNet50
from tinygrad import Tensor, nn, Device
from tinygrad.helpers import Profiling, Timing, getenv
from tinygrad.uop.ops import Ops
from tinygrad.codegen import full_rewrite_to_sink
from tinygrad.codegen.late.linearizer import linearize
from tinygrad.uop.spec import type_verify, program_spec
from tinygrad.codegen import get_rewrites_for_renderer, apply_rewrites, rewrites_for_linearizer
from tinygrad.uop.spec import type_verify
if __name__ == "__main__":
mdl = ResNet50()
@@ -29,17 +28,18 @@ if __name__ == "__main__":
asts = list({x.ast.key:x.ast for x in sched if x.ast.op is Ops.SINK}.values())
if (restrict_kernel := getenv("RESTRICT_KERNEL", -1)) != -1: asts = asts[restrict_kernel:restrict_kernel+1]
rewrites = get_rewrites_for_renderer(Device.default.renderer, linearizer=False)
with Profiling(PROFILE, fn="/tmp/rewrite.prof"):
with Timing("***** model rewrite in "):
rewritten_uops = []
for u in asts:
rewritten_uops.append(full_rewrite_to_sink(u, ren=Device.default.renderer))
rewritten_uops.append(apply_rewrites(u, rewrites))
if LINEARIZE:
with Timing("***** model linearize in "):
uops_line = []
for u in rewritten_uops:
uops_line.append(linearize(u))
uops_line.append(apply_rewrites(u, rewrites_for_linearizer))
with Timing("***** model verify in "):
for u in uops_line: type_verify(u, program_spec)
print(sum(len(u) for u in uops_line))
for u in uops_line: type_verify(u.arg.lst)
print(sum(len(u.arg.lst) for u in uops_line))
+46
View File
@@ -0,0 +1,46 @@
# ruff: noqa: E501
from tinygrad.codegen.opt.kernel import Kernel, Opt, OptOps
from tinygrad.dtype import dtypes
from tinygrad.engine.realize import CompiledRunner, get_program
from tinygrad.codegen.opt.search import bufs_from_lin
from tinygrad.uop.ops import UOp, Ops
from tinygrad.shape.shapetracker import ShapeTracker
from tinygrad.shape.view import View
ast = UOp(Ops.SINK, dtypes.void, arg=None, src=(
UOp(Ops.STORE, dtypes.void, arg=None, src=(
UOp(Ops.DEFINE_GLOBAL, dtypes.half.ptr(), arg=0, src=()),
UOp(Ops.VIEW, dtypes.void, arg=ShapeTracker(views=(View(shape=(2, 1, 1280, 8, 8, 1, 1, 1), strides=(81920, 0, 64, 8, 1, 0, 0, 0), offset=0, mask=None, contiguous=True),)), src=()),
UOp(Ops.ADD, dtypes.half, arg=None, src=(
UOp(Ops.ADD, dtypes.half, arg=None, src=(
UOp(Ops.CAST, dtypes.half, arg=None, src=(
UOp(Ops.REDUCE_AXIS, dtypes.float, arg=(Ops.ADD, (5, 6, 7)), src=(
UOp(Ops.CAST, dtypes.float, arg=None, src=(
UOp(Ops.MUL, dtypes.half, arg=None, src=(
UOp(Ops.LOAD, dtypes.half, arg=None, src=(
UOp(Ops.DEFINE_GLOBAL, dtypes.half.ptr(), arg=1, src=()),
UOp(Ops.VIEW, dtypes.void, arg=ShapeTracker(views=(View(shape=(1, 2, 1, 2560, 4, 10, 4, 10), strides=(0, 163840, 0, 64, 0, 8, 0, 1), offset=-9, mask=((0, 1), (0, 2), (0, 1), (0, 2560), (0, 4), (1, 9), (0, 4), (1, 9)), contiguous=False), View(shape=(2, 1, 1280, 8, 8, 2560, 3, 3), strides=(4096000, 0, 0, 40, 1, 1600, 440, 11), offset=0, mask=None, contiguous=False))), src=()),)),
UOp(Ops.LOAD, dtypes.half, arg=None, src=(
UOp(Ops.DEFINE_GLOBAL, dtypes.half.ptr(), arg=2, src=()),
UOp(Ops.VIEW, dtypes.void, arg=ShapeTracker(views=(View(shape=(2, 1, 1280, 8, 8, 2560, 3, 3), strides=(0, 0, 23040, 0, 0, 9, 3, 1), offset=0, mask=None, contiguous=False),)), src=()),)),)),)),)),)),
UOp(Ops.LOAD, dtypes.half, arg=None, src=(
UOp(Ops.DEFINE_GLOBAL, dtypes.half.ptr(), arg=3, src=()),
x17:=UOp(Ops.VIEW, dtypes.void, arg=ShapeTracker(views=(View(shape=(2, 1, 1280, 8, 8, 1, 1, 1), strides=(0, 0, 1, 0, 0, 0, 0, 0), offset=0, mask=None, contiguous=False),)), src=()),)),)),
UOp(Ops.LOAD, dtypes.half, arg=None, src=(
UOp(Ops.DEFINE_GLOBAL, dtypes.half.ptr(), arg=4, src=()),
x17,)),)),)),))
opts = [Opt(op=OptOps.UPCAST, axis=3, arg=4), Opt(op=OptOps.UPCAST, axis=1, arg=4), Opt(op=OptOps.UNROLL, axis=2, arg=0), Opt(op=OptOps.UNROLL, axis=1, arg=0), Opt(op=OptOps.LOCAL, axis=1, arg=8), Opt(op=OptOps.LOCAL, axis=2, arg=8), Opt(op=OptOps.LOCAL, axis=2, arg=2)]
k = Kernel(ast)
k.apply_opts(opts)
bufs = bufs_from_lin(k)
prg = CompiledRunner(get_program(k.ast, k.opts, k.applied_opts))
for i in range(10):
speed = prg(bufs, var_vals={}, wait=True)
print(f"kernel time: {speed*1e3:.2f} ms")
# on M1 Max
# 11ms before block 9b0859d71780fef5cf3831e317f74e53f2483229
# 15ms after block cbcc1c20eb09a1342f6581cfbb99632bade982a8

Some files were not shown because too many files have changed in this diff Show More