mirror of
https://github.com/tinygrad/tinygrad.git
synced 2026-08-14 12:38:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ffd33e1e5 | ||
|
|
4198efb8bc | ||
|
|
13e8914deb | ||
|
|
8cbef912d2 | ||
|
|
1ff341bae5 | ||
|
|
267be7fc5e | ||
|
|
8206eab4fc | ||
|
|
885b6dea9e | ||
|
|
f97fb703c8 | ||
|
|
ecb8565f67 | ||
|
|
c99b7dfd4a | ||
|
|
051aab5481 | ||
|
|
2db57f3a97 | ||
|
|
bebec73471 | ||
|
|
e98506735b | ||
|
|
65a0a31475 | ||
|
|
f396df26ea | ||
|
|
a23226e61e | ||
|
|
f6786c1bfd | ||
|
|
d532117df5 | ||
|
|
a9e5ffd3d1 | ||
|
|
3dc593c536 | ||
|
|
bc178d14a9 | ||
|
|
e066b3176b | ||
|
|
54f48f93c6 | ||
|
|
b791d70725 | ||
|
|
9f0c25ec48 | ||
|
|
b2caf4c2b3 | ||
|
|
564e9ccc31 | ||
|
|
6cd341354e | ||
|
|
b46229ca51 | ||
|
|
78f7650eec | ||
|
|
512513c403 | ||
|
|
f6430a0559 | ||
|
|
73002ebffa | ||
|
|
99e76f33a0 | ||
|
|
629b177b66 | ||
|
|
4c8362128b | ||
|
|
c78dfcc5a1 | ||
|
|
363a201cc6 | ||
|
|
5be3a93d02 | ||
|
|
cf5ab93b8e | ||
|
|
4d7a7096c9 | ||
|
|
985b6eb95f | ||
|
|
5eb87ab131 | ||
|
|
4a741e8364 | ||
|
|
66ea3a0be4 | ||
|
|
e456f2cb1e | ||
|
|
c18b283f58 | ||
|
|
92a87e37e4 | ||
|
|
e64d4b3b44 | ||
|
|
5894df059c | ||
|
|
2da02f1ae1 | ||
|
|
4b001ec723 | ||
|
|
a6f5b1482e | ||
|
|
457602b350 | ||
|
|
70bce62c67 | ||
|
|
79903ae2be | ||
|
|
819592ee67 | ||
|
|
30ca3f2af8 | ||
|
|
9f39f6391c | ||
|
|
1c362736aa | ||
|
|
e42b4edf8c | ||
|
|
8c47cf4323 | ||
|
|
35b6f4148d | ||
|
|
5ce8a1d2f2 | ||
|
|
b147e7e8e6 | ||
|
|
a7dac11aad | ||
|
|
37967fa17b | ||
|
|
fb53bdad5d | ||
|
|
ef16e6c68c | ||
|
|
f55fcfecf9 | ||
|
|
9442442cb1 | ||
|
|
d66c997a39 | ||
|
|
bb307b9e81 | ||
|
|
c11dd56956 | ||
|
|
5e01cc299b | ||
|
|
e936aa7974 | ||
|
|
901d27b3ba | ||
|
|
f5a3b33d33 | ||
|
|
907499b02c | ||
|
|
e22c5e7e73 | ||
|
|
6c9560a846 | ||
|
|
b0da173f2f | ||
|
|
e110f4632a | ||
|
|
3b82dee625 | ||
|
|
99589dea81 | ||
|
|
bbe0bebbf3 | ||
|
|
39c2117dea | ||
|
|
2832954bcb | ||
|
|
7784cec48e | ||
|
|
4d817a289e | ||
|
|
62e62d8760 | ||
|
|
24884c6768 | ||
|
|
372d9e5753 | ||
|
|
f2ffe9c8cf | ||
|
|
63484d837e | ||
|
|
a79832b01f | ||
|
|
45e2f916a3 | ||
|
|
25c2da1579 | ||
|
|
072f7c35c5 | ||
|
|
e93c9bf6a7 | ||
|
|
273b1f914d | ||
|
|
701a632907 | ||
|
|
95748a4518 | ||
|
|
8fb545c475 | ||
|
|
7139e036c5 | ||
|
|
804133cffd | ||
|
|
f4da94af28 | ||
|
|
6b54378eba | ||
|
|
189582db5e | ||
|
|
70ba84eb04 | ||
|
|
eaeaea2f9c | ||
|
|
8c1368cab6 | ||
|
|
f00009c731 | ||
|
|
99a519f068 | ||
|
|
c0c24d3a70 | ||
|
|
0a32ab0006 | ||
|
|
db5c918215 | ||
|
|
c94e597b3e | ||
|
|
94701d4838 | ||
|
|
e18922f111 | ||
|
|
92324172be | ||
|
|
3b192f5eac | ||
|
|
6415e3e8a7 | ||
|
|
b4f6a2c7a3 | ||
|
|
8a941d95a4 | ||
|
|
456560c1ff | ||
|
|
a5b0f57067 | ||
|
|
4b7329001d | ||
|
|
6b35467f53 | ||
|
|
5b5ba31a86 | ||
|
|
e1f8c82938 | ||
|
|
0bde87d8d7 | ||
|
|
9dac505565 | ||
|
|
154b4f9f40 | ||
|
|
6e4ee8deea | ||
|
|
f835566e27 | ||
|
|
c1db62ff7c | ||
|
|
04b3e51f1b | ||
|
|
cdfb8e31ae | ||
|
|
6df19a4ac6 | ||
|
|
ff68a6263b | ||
|
|
ddb53d1d48 | ||
|
|
2a5c22436e | ||
|
|
bcc30e5e10 | ||
|
|
e85cee0aad | ||
|
|
74b4cfe44b | ||
|
|
914defd55d | ||
|
|
2f95c10702 | ||
|
|
e718254004 | ||
|
|
6e00dec95d | ||
|
|
3a9aa05359 | ||
|
|
f0831c8c30 | ||
|
|
e7e535cd53 | ||
|
|
81108f91ee | ||
|
|
bf173c0a37 | ||
|
|
a7bc0104c2 | ||
|
|
b6eb9172ea | ||
|
|
174811fc0f | ||
|
|
7762b3558b | ||
|
|
726988fa4b | ||
|
|
6abe90fb7c | ||
|
|
cebc2b5721 | ||
|
|
8d0256c46b | ||
|
|
6d86e962c7 | ||
|
|
92778c7a8b | ||
|
|
c5cee74706 | ||
|
|
0b673eddec | ||
|
|
60d7e232f2 | ||
|
|
587ccc0e5c | ||
|
|
c3149c618a | ||
|
|
8baa61bd67 | ||
|
|
f51f9aaa16 | ||
|
|
62e7b8b870 | ||
|
|
c7336c3e31 | ||
|
|
8960ac54f3 | ||
|
|
7f798a9630 | ||
|
|
1ad6598963 | ||
|
|
cdc72556a1 | ||
|
|
20a232f1c5 | ||
|
|
0435d31f1c | ||
|
|
7d9551ce2e | ||
|
|
d711a4b933 | ||
|
|
40633ab34d | ||
|
|
c780cd9abb | ||
|
|
d59d4cdbe4 | ||
|
|
32af1ff84b | ||
|
|
367fbabc30 | ||
|
|
57f6b6f229 | ||
|
|
154cdfe46d | ||
|
|
a71a41f6d1 | ||
|
|
8521fd5263 | ||
|
|
df2f8b9295 | ||
|
|
68c045bf0a | ||
|
|
990e8b97ee | ||
|
|
565a7a6218 | ||
|
|
25beea5769 | ||
|
|
c7c59e6dd7 | ||
|
|
e284f6325a | ||
|
|
203a93363c | ||
|
|
5d0d3d7aac | ||
|
|
d1e2c393f8 | ||
|
|
a8e4614436 | ||
|
|
1e93d19ee3 | ||
|
|
b5e36e3c6c | ||
|
|
b8a9cce783 | ||
|
|
12fd2c9c7b | ||
|
|
734c99f722 | ||
|
|
2e9082e0bc | ||
|
|
339e6edb7d | ||
|
|
357dac8425 | ||
|
|
ba593f7b98 | ||
|
|
cad3ada909 | ||
|
|
9cd35deae7 | ||
|
|
59784a5972 | ||
|
|
63a23dfe80 | ||
|
|
e8158afd4b | ||
|
|
1df9c7d7e7 | ||
|
|
fd6ef4801c | ||
|
|
89e7f2fa00 | ||
|
|
617614beb7 | ||
|
|
c8ef4b60f6 | ||
|
|
350a4754a9 | ||
|
|
30ff84d050 | ||
|
|
442218266d | ||
|
|
addc54b96c | ||
|
|
037f6e8fa0 | ||
|
|
82f10cfe2e | ||
|
|
fcdf4ab37e | ||
|
|
910d698b78 | ||
|
|
062a6d68d7 | ||
|
|
33025b99f6 | ||
|
|
e0d0d4372d | ||
|
|
bd662bea67 | ||
|
|
c9a3464f76 | ||
|
|
0160f034d6 | ||
|
|
253d32b065 | ||
|
|
935a60db72 | ||
|
|
f6bc620169 | ||
|
|
d1bb5c0426 | ||
|
|
5417e4b099 | ||
|
|
3196a7aae3 | ||
|
|
dfb8f9fc9e | ||
|
|
79c2f1ae26 | ||
|
|
9561803cb0 | ||
|
|
285534ce64 | ||
|
|
98239f1156 | ||
|
|
53478c741d | ||
|
|
5d209ee7ec | ||
|
|
bce2bc0465 | ||
|
|
f34f26bca0 | ||
|
|
55db1b0e0e | ||
|
|
cf9baeea61 | ||
|
|
8be7844b2e | ||
|
|
3aa2277b8f | ||
|
|
a069a45d14 | ||
|
|
a498ec9c18 | ||
|
|
8f740e07ff | ||
|
|
533f18b22c | ||
|
|
af4479c169 | ||
|
|
e7c057d5dc | ||
|
|
b86a33a312 | ||
|
|
b8cd66c7a2 | ||
|
|
1d1e1d9d88 | ||
|
|
592e86f6f5 | ||
|
|
cc2dfe22f5 | ||
|
|
3ed543f956 | ||
|
|
b77bdbbc62 | ||
|
|
7c19db00f1 | ||
|
|
069177c1be | ||
|
|
4a151e7533 | ||
|
|
c3278e5622 | ||
|
|
b8cf35fb77 | ||
|
|
d65bd669f8 | ||
|
|
db5ae846aa | ||
|
|
3ab23af829 | ||
|
|
fafbf3daea | ||
|
|
85a907605c | ||
|
|
e1996d358c | ||
|
|
312c622d35 | ||
|
|
612e3d6143 | ||
|
|
9ec4c06d7d | ||
|
|
99aa3bd5f9 | ||
|
|
91ac4f1f92 | ||
|
|
768dc952de | ||
|
|
2e50ed0767 | ||
|
|
0aabc1e938 | ||
|
|
f0268d13f6 | ||
|
|
aa81bde150 | ||
|
|
236c4590c3 | ||
|
|
7597e1dcac | ||
|
|
60e03eec37 | ||
|
|
a59439d013 | ||
|
|
89df6f611d | ||
|
|
d25ceffe8d | ||
|
|
e8380968f2 | ||
|
|
f228c03f9f | ||
|
|
70dd297a05 | ||
|
|
852d80dff9 | ||
|
|
c7e63601fd | ||
|
|
db4a359374 | ||
|
|
4918c827c2 | ||
|
|
0c9d47deab | ||
|
|
d3bfcd3277 | ||
|
|
1e6e5a0efd | ||
|
|
471bd30d16 | ||
|
|
fb61f3519f | ||
|
|
30ee7c4c26 | ||
|
|
e06cbfcb8a | ||
|
|
84d4589ed4 | ||
|
|
8ecaf839e2 | ||
|
|
b9eb5b5d49 | ||
|
|
a9ef93176f | ||
|
|
ecdc7539a2 | ||
|
|
9bf032de69 |
@@ -41,6 +41,10 @@ inputs:
|
||||
description: "Install LLVM?"
|
||||
required: false
|
||||
default: 'false'
|
||||
mesa:
|
||||
description: "Install mesa"
|
||||
required: false
|
||||
default: 'false'
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
@@ -289,3 +293,13 @@ 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,7 +2,7 @@ name: Autogen
|
||||
env:
|
||||
# increment this when downloads substantially change to avoid the internet
|
||||
DOWNLOAD_CACHE_VERSION: '12'
|
||||
PYTHON_CACHE_VERSION: '3'
|
||||
PYTHON_CACHE_VERSION: '4'
|
||||
APT_CACHE_VERSION: '1'
|
||||
BUILD_CACHE_VERSION: '1'
|
||||
CAPTURE_PROCESS_REPLAY: 1
|
||||
@@ -36,8 +36,9 @@ 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
|
||||
run: sudo apt-get install -y --no-install-recommends llvm-14-dev libclang-14-dev llvm-20-dev
|
||||
- name: Verify OpenCL autogen
|
||||
run: |
|
||||
cp tinygrad/runtime/autogen/opencl.py /tmp/opencl.py.bak
|
||||
@@ -89,3 +90,8 @@ 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
|
||||
|
||||
@@ -51,17 +51,18 @@ 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=800 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=720 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=900 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=800 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=10000 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=4500 python3.11 examples/sdv2.py --fp16 --seed 0 --noshow --timing | tee sdv2.txt
|
||||
# process replay can't capture this, the graph is too large
|
||||
# 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 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
|
||||
@@ -130,7 +131,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 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
|
||||
# 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
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Speed (Mac)
|
||||
@@ -210,6 +211,7 @@ 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)
|
||||
@@ -237,6 +239,8 @@ 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
|
||||
@@ -270,6 +274,7 @@ jobs:
|
||||
llama3_beam.txt
|
||||
llama3_four_gpu.txt
|
||||
llama3_six_gpu.txt
|
||||
llama3_fp8.txt
|
||||
llama_2_70B.txt
|
||||
mixtral.txt
|
||||
gpt2_unjitted.txt
|
||||
@@ -318,9 +323,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=310 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=240 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=310 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=270 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
|
||||
@@ -522,7 +527,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=330 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=350 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
|
||||
@@ -618,22 +623,24 @@ 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: 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
|
||||
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
|
||||
- name: openpilot compile3 0.9.9 driving_policy
|
||||
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
|
||||
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
|
||||
- name: openpilot compile3 0.9.9 dmonitoring
|
||||
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
|
||||
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=5 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=13 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=25 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=5 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=13 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
|
||||
- name: benchmark MobileNetV2 on DSP
|
||||
run: |
|
||||
# generate quantized weights
|
||||
@@ -641,19 +648,9 @@ 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 DONT_REALIZE_EXPAND=1 NOOPT=1 CNT=2 DEBUG=2 python3 examples/test_onnx_imagenet.py /tmp/model.quant.onnx
|
||||
PYTHONPATH=. CC=clang-19 DSP=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
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
run_script_job:
|
||||
runs-on: [self-hosted, Linux, tinybox]
|
||||
if: github.repository_owner == 'tinygrad'
|
||||
timeout-minutes: 360
|
||||
timeout-minutes: 720
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
|
||||
+105
-91
@@ -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: '3'
|
||||
PYTHON_CACHE_VERSION: '4'
|
||||
APT_CACHE_VERSION: '1'
|
||||
BUILD_CACHE_VERSION: '1'
|
||||
CAPTURE_PROCESS_REPLAY: 1
|
||||
@@ -89,64 +89,65 @@ jobs:
|
||||
clang -O2 recognize.c -lm -o recognize
|
||||
cat test/models/efficientnet/Chicken.jpg | ./recognize | grep cock
|
||||
|
||||
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
|
||||
# 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
|
||||
|
||||
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
|
||||
# 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
|
||||
|
||||
bepython:
|
||||
name: Python Backend
|
||||
@@ -203,7 +204,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 ALLOW_TF32=1 FORWARD_ONLY=1 PYTHON=1 python3 test/opt/test_tensor_cores.py
|
||||
DEBUG=2 EMULATE=CUDA_SM89 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
|
||||
@@ -229,7 +230,7 @@ jobs:
|
||||
uses: ./.github/actions/setup-tinygrad
|
||||
with:
|
||||
key: linting-only
|
||||
python-version: '3.10'
|
||||
python-version: '3.11'
|
||||
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 .
|
||||
@@ -238,14 +239,13 @@ jobs:
|
||||
pip3 install --upgrade --force-reinstall ruff==0.11.0
|
||||
python3 -m ruff check .
|
||||
python3 -m ruff check examples/mlperf/ --ignore E501
|
||||
- name: Lint tinygrad with pylint
|
||||
run: python -m pylint tinygrad/
|
||||
- name: Run mypy
|
||||
run: |
|
||||
python -m mypy --strict-equality --lineprecision-report .
|
||||
cat lineprecision.txt
|
||||
- name: Run TYPED=1
|
||||
run: TYPED=1 python -c "import tinygrad"
|
||||
# broken because of UPatAny
|
||||
#- name: Run TYPED=1
|
||||
# run: TYPED=1 python -c "import tinygrad"
|
||||
|
||||
unittest:
|
||||
name: Unit Tests
|
||||
@@ -265,8 +265,6 @@ 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
|
||||
@@ -274,6 +272,8 @@ 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
|
||||
@@ -293,6 +293,25 @@ jobs:
|
||||
- name: Repo line count < 18000 lines
|
||||
run: MAX_LINE_COUNT=18000 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 }}
|
||||
|
||||
fuzzing:
|
||||
name: Fuzzing
|
||||
runs-on: ubuntu-latest
|
||||
@@ -309,10 +328,6 @@ 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
|
||||
|
||||
@@ -354,7 +369,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
|
||||
run: CL=1 FUSE_OPTIM=1 python -m pytest -n=auto test/models/test_mnist.py test/test_optim.py -k "not muon"
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -377,17 +392,13 @@ jobs:
|
||||
llvm: 'true'
|
||||
- name: Test openpilot model kernel count and gate usage
|
||||
run: |
|
||||
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
|
||||
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
|
||||
- name: Run process replay tests
|
||||
uses: ./.github/actions/process-replay
|
||||
|
||||
@@ -529,11 +540,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 -k "not test_avg_pool3d_failure"
|
||||
run: CPU=1 CPU_LLVM=1 DEVECTORIZE=0 python3 -m pytest -n auto test/test_tiny.py test/test_ops.py
|
||||
- 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 -k "not test_avg_pool3d_failure"
|
||||
run: CPU=1 CPU_LLVM=0 DEVECTORIZE=0 python3 -m pytest -n auto test/test_tiny.py test/test_ops.py
|
||||
|
||||
testdsp:
|
||||
name: Linux (DSP)
|
||||
@@ -634,6 +645,7 @@ 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
|
||||
@@ -677,7 +689,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
backend: [llvm, cpu, opencl]
|
||||
backend: [llvm, cpu, opencl, lvp]
|
||||
|
||||
name: Linux (${{ matrix.backend }})
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -691,9 +703,10 @@ jobs:
|
||||
key: ${{ matrix.backend }}-minimal
|
||||
deps: testing_minimal
|
||||
opencl: ${{ matrix.backend == 'opencl' && 'true' }}
|
||||
llvm: ${{ matrix.backend == 'llvm' && 'true' }}
|
||||
llvm: ${{ matrix.backend == 'llvm' || matrix.backend == 'lvp' }}
|
||||
mesa: ${{ matrix.backend == 'lvp' && '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' }}" >> $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' || matrix.backend == 'lvp' && 'CPU=1\nCPU_LVP=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"
|
||||
@@ -895,7 +908,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
backend: [metal, llvm, cpu]
|
||||
backend: [metal, llvm, cpu, lvp]
|
||||
name: MacOS (${{ matrix.backend }})
|
||||
runs-on: macos-15
|
||||
timeout-minutes: 20
|
||||
@@ -908,12 +921,13 @@ jobs:
|
||||
key: macos-${{ matrix.backend }}-minimal
|
||||
deps: testing_minimal
|
||||
pydeps: "capstone"
|
||||
llvm: ${{ matrix.backend == 'llvm' && 'true' }}
|
||||
llvm: ${{ matrix.backend == 'llvm' || matrix.backend == 'lvp' }}
|
||||
mesa: ${{ matrix.backend == 'lvp' && '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'}}" >> $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' || matrix.backend == 'lvp' && 'CPU=1\nCPU_LVP=1' }}" >> $GITHUB_ENV
|
||||
- name: Check Device.DEFAULT and print some source
|
||||
run: |
|
||||
python -c "from tinygrad import Device; assert Device.DEFAULT == {'LLVM':'CPU'}.get(x:='${{ matrix.backend }}'.upper(), x), Device.DEFAULT"
|
||||
python -c "from tinygrad import Device; assert Device.DEFAULT == {'LLVM':'CPU','LVP':'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
|
||||
|
||||
@@ -38,6 +38,7 @@ extra/huggingface_onnx/models/*
|
||||
extra/huggingface_onnx/*.yaml
|
||||
extra/weights
|
||||
venv
|
||||
venv_sd_mlperf
|
||||
examples/**/net.*[js,json]
|
||||
examples/**/*.safetensors
|
||||
node_modules
|
||||
|
||||
+5
-11
@@ -20,21 +20,15 @@ repos:
|
||||
language: system
|
||||
always_run: true
|
||||
pass_filenames: false
|
||||
- id: tests
|
||||
name: subset of tests
|
||||
entry: env PYTHONPATH="." python3 -m pytest -n=4 test/test_ops.py test/test_dtype.py test/test_schedule.py test/test_assign.py
|
||||
language: system
|
||||
always_run: true
|
||||
pass_filenames: false
|
||||
- id: example
|
||||
name: multi device tests
|
||||
name: test all devices
|
||||
entry: python3 test/external/external_test_example.py
|
||||
language: system
|
||||
always_run: true
|
||||
pass_filenames: false
|
||||
- id: pylint
|
||||
name: pylint
|
||||
entry: python3 -m pylint tinygrad/
|
||||
- id: tests
|
||||
name: subset of tests
|
||||
entry: env OMP_NUM_THREADS=1 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
|
||||
|
||||
+76
-1
@@ -461,6 +461,80 @@ 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
|
||||
@@ -484,6 +558,7 @@ 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" == "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
|
||||
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
|
||||
else echo "usage: $0 <type>"
|
||||
fi
|
||||
|
||||
@@ -53,9 +53,7 @@ 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)
|
||||
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
|
||||
alu = buf_1.index(idx) + buf_2.index(idx)
|
||||
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,))
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
# 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
@@ -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
|
||||
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).
|
||||
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).
|
||||
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
@@ -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.rand(args.batch_size, args.benchmark), Variable("a", 0, MAX_CONTEXT).bind(0)).realize()
|
||||
gpt2.model(Tensor.randint(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:
|
||||
|
||||
+37
-1
@@ -145,6 +145,41 @@ 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},
|
||||
@@ -167,6 +202,7 @@ 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)
|
||||
|
||||
@@ -242,7 +278,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"], help="Quantization method")
|
||||
parser.add_argument("--quantize", choices=["int8", "nf4", "float16", "fp8"], 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")
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
export PYTHONPATH="." NV=1
|
||||
export MODEL="bert"
|
||||
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=90 EVAL_BS=90
|
||||
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=96 EVAL_BS=96
|
||||
|
||||
export IGNORE_OOB=1
|
||||
export REWRITE_STACK_LIMIT=500000
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
export PYTHONPATH="." NV=1
|
||||
export MODEL="bert"
|
||||
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=90 EVAL_BS=90
|
||||
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=96 EVAL_BS=96
|
||||
|
||||
export IGNORE_OOB=1
|
||||
export REWRITE_STACK_LIMIT=500000
|
||||
|
||||
+1
-1
@@ -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=90 EVAL_BS=90
|
||||
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=96 EVAL_BS=96
|
||||
|
||||
export IGNORE_OOB=1
|
||||
export REWRITE_STACK_LIMIT=500000
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
export PYTHONPATH="." AMD=1
|
||||
export MODEL="bert"
|
||||
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=90 EVAL_BS=90
|
||||
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=96 EVAL_BS=96
|
||||
|
||||
export IGNORE_OOB=1
|
||||
export REWRITE_STACK_LIMIT=500000
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
export PYTHONPATH="." AMD=1
|
||||
export MODEL="bert"
|
||||
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=90 EVAL_BS=90
|
||||
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=96 EVAL_BS=96
|
||||
|
||||
export IGNORE_OOB=1
|
||||
export REWRITE_STACK_LIMIT=500000
|
||||
|
||||
+1
-1
@@ -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=90 EVAL_BS=90
|
||||
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=96 EVAL_BS=96
|
||||
|
||||
export IGNORE_OOB=1
|
||||
export REWRITE_STACK_LIMIT=500000
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
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
|
||||
@@ -21,11 +17,14 @@ 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)
|
||||
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()}
|
||||
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()}
|
||||
print("created tensors")
|
||||
|
||||
run_onnx_jit = TinyJit(lambda **kwargs:
|
||||
@@ -33,8 +32,6 @@ 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
|
||||
@@ -69,14 +66,9 @@ 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 test_val
|
||||
return inputs, test_val
|
||||
|
||||
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}}
|
||||
def test_vs_compile(run, inputs, test_val=None):
|
||||
|
||||
# run 20 times
|
||||
step_times = []
|
||||
@@ -93,68 +85,57 @@ def test_vs_compile(run, new_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
|
||||
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)
|
||||
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)
|
||||
return val
|
||||
|
||||
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()}
|
||||
def test_vs_onnx(new_inputs, test_val, onnx_file, tol):
|
||||
import onnxruntime as ort
|
||||
|
||||
onnx_inputs = {k:v.numpy() for k,v in new_inputs.items()}
|
||||
onnx_model = onnx.load(onnx_file)
|
||||
|
||||
timings = []
|
||||
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()
|
||||
ORT_TO_NP_DTYPES: dict[str, np.dtype] = {
|
||||
'tensor(float)': np.dtype('float32'),
|
||||
'tensor(float16)': np.dtype('float16'),
|
||||
'tensor(uint8)': np.dtype('uint8'),
|
||||
}
|
||||
|
||||
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")
|
||||
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}
|
||||
|
||||
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")
|
||||
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)
|
||||
test_val = compile(onnx_file) if not getenv("RUN") else None
|
||||
inputs, outputs = compile(onnx_file)
|
||||
|
||||
with open(OUTPUT, "rb") as f: pickle_loaded = pickle.load(f)
|
||||
|
||||
# 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"))
|
||||
test_vs_compile(pickle_loaded, inputs, outputs)
|
||||
if getenv("SELFTEST"):
|
||||
test_vs_onnx(inputs, outputs, onnx_file, 1e-4)
|
||||
|
||||
if getenv("BENCHMARK_LOG", ""):
|
||||
bench(pickle_loaded, inputs)
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
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()
|
||||
+12
-8
@@ -99,6 +99,7 @@ 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
|
||||
@@ -112,19 +113,22 @@ if __name__ == "__main__":
|
||||
|
||||
model = StableDiffusionV2(**params)
|
||||
|
||||
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)))
|
||||
|
||||
with WallTimeEvent(BenchEvent.LOAD_WEIGHTS):
|
||||
load_state_dict(model, safe_load(weights_fn), strict=False)
|
||||
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)))
|
||||
|
||||
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).realize())
|
||||
v.replace(v.cast(dtypes.float16))
|
||||
|
||||
Tensor.realize(*get_state_dict(model).values())
|
||||
|
||||
c = { "crossattn": model.cond_stage_model(args.prompt) }
|
||||
uc = { "crossattn": model.cond_stage_model("") }
|
||||
|
||||
@@ -263,13 +263,16 @@ 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):
|
||||
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 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)
|
||||
|
||||
if args.fp16:
|
||||
for k,v in get_state_dict(model).items():
|
||||
|
||||
@@ -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
|
||||
# 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
|
||||
# python3 examples/test_onnx_imagenet.py /tmp/model.quant.onnx
|
||||
# VIZ=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)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# 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
|
||||
|
||||
+145
-319
@@ -1,354 +1,180 @@
|
||||
from tinygrad import Tensor, Device, Context, GlobalCounters, dtypes
|
||||
from tinygrad.uop.ops import UOp, Ops, KernelInfo, graph_rewrite, AxisType, PatternMatcher, UPat
|
||||
from tinygrad.engine.realize import CompiledRunner, ExecItem, get_program
|
||||
from tinygrad.uop.ops import UOp, KernelInfo
|
||||
from tinygrad.engine.realize import ExecItem, get_runner
|
||||
from tinygrad.dtype import AddrSpace
|
||||
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)])
|
||||
from tinygrad.helpers import getenv
|
||||
|
||||
N = 4096
|
||||
run_count = 5
|
||||
|
||||
BN = 128
|
||||
BM = 128
|
||||
BK = 8
|
||||
# ---------------------------
|
||||
# launch/config constants
|
||||
# ---------------------------
|
||||
|
||||
TN = 4
|
||||
TM = 4
|
||||
WARP_SIZE = 32
|
||||
|
||||
# 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"
|
||||
# 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
|
||||
|
||||
# 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]
|
||||
# Register tile sizes (per-thread accumulator tile of C)
|
||||
TN = 4 # columns per thread
|
||||
TM = 4 # rows per thread
|
||||
|
||||
# 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)))))
|
||||
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
|
||||
|
||||
pm = PatternMatcher([
|
||||
(UPat(Ops.VIEW, src=(UPat(Ops.REDUCE_AXIS, src=(UPat.var("src"),), name="r"),), name="view"), swizzle_reduceop),
|
||||
])
|
||||
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"
|
||||
|
||||
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)
|
||||
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)
|
||||
N_PER_ITER = WAVE_TILE_N // ITERS_PER_WAVE_N
|
||||
M_PER_ITER = WAVE_TILE_M // ITERS_PER_WAVE_M
|
||||
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"
|
||||
|
||||
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():
|
||||
# ---------------------------
|
||||
# per-thread read mapping
|
||||
# ---------------------------
|
||||
# A: read BK x BN tiles; B: read BN x BK tiles
|
||||
tid = UOp.special(THREADS_PER_BLOCK, "lidx0")
|
||||
|
||||
return sink.replace(arg=KernelInfo(opts_to_apply=tuple(opts)))
|
||||
waveIdx = (tid // WARP_SIZE) % WAVES_IN_BLOCK_X
|
||||
waveIdy = (tid // WARP_SIZE) // WAVES_IN_BLOCK_X
|
||||
assert waveIdy.vmax+1 == WAVES_IN_BLOCK_Y
|
||||
|
||||
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))
|
||||
idxInWave = (tid % WARP_SIZE) % LANES_PER_WAVE_X
|
||||
idyInWave = (tid % WARP_SIZE) // LANES_PER_WAVE_X
|
||||
assert idyInWave.vmax+1 == LANES_PER_WAVE_Y
|
||||
|
||||
def hl_spec_kernel3():
|
||||
nbIterWaveM = 2
|
||||
nbIterWaveN = 2
|
||||
# ---------------------------
|
||||
# block indices & placeholders
|
||||
# ---------------------------
|
||||
blockIdx_x = UOp.special(N // BLOCK_N, "gidx0")
|
||||
blockIdx_y = UOp.special(N // BLOCK_M, "gidx1")
|
||||
|
||||
# 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,)))
|
||||
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)
|
||||
|
||||
# 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)
|
||||
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))
|
||||
Bs = UOp.placeholder((BLOCK_K, BLOCK_N), dtypes.float, slot=1, addrspace=AddrSpace.LOCAL)
|
||||
|
||||
# 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_col = UOp.placeholder((ITERS_PER_WAVE_M, TM), dtypes.float, slot=0, addrspace=AddrSpace.REG)
|
||||
B_row = UOp.placeholder((ITERS_PER_WAVE_N, TN), dtypes.float, slot=1, addrspace=AddrSpace.REG)
|
||||
c_regs = UOp.placeholder((ITERS_PER_WAVE_M, TM, ITERS_PER_WAVE_N, TN), dtypes.float, slot=2, addrspace=AddrSpace.REG)
|
||||
|
||||
# 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]
|
||||
i = UOp.range(c_regs.size, 16)
|
||||
c_regs = c_regs[i].set(0.0, end=i)
|
||||
|
||||
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]
|
||||
k_tile_range = UOp.range(N // BLOCK_K, 0)
|
||||
|
||||
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)
|
||||
# ---------------------------
|
||||
# GLOBAL -> LOCAL (As, Bs)
|
||||
# ---------------------------
|
||||
b = b.reshape(N // BLOCK_K, BLOCK_K,
|
||||
N // BLOCK_N, BLOCK_N)
|
||||
i = UOp.range(BLOCK_N * BLOCK_K // THREADS_PER_BLOCK, 1)
|
||||
index_x = tid % BLOCK_N
|
||||
index_y = (tid // BLOCK_N) + (THREADS_PER_BLOCK // BLOCK_N) * i
|
||||
Bs_store = Bs[index_y, index_x].store(b[k_tile_range, index_y, blockIdx_x, index_x]).end(i)
|
||||
|
||||
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)
|
||||
a = a.reshape(N // BLOCK_M, BLOCK_M,
|
||||
N // BLOCK_K, BLOCK_K)
|
||||
i = UOp.range(BLOCK_M * BLOCK_K // THREADS_PER_BLOCK, 2)
|
||||
index_x = tid % BLOCK_K
|
||||
index_y = (tid // BLOCK_K) + (THREADS_PER_BLOCK // BLOCK_K) * i
|
||||
As_store = As[index_x, index_y].store(a[blockIdx_y, index_y, k_tile_range, index_x]).end(i)
|
||||
|
||||
#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))
|
||||
# TODO: can we automate barrier?
|
||||
barrier = UOp.barrier(As_store, Bs_store)
|
||||
Bs = Bs.after(barrier)
|
||||
As = As.after(barrier)
|
||||
|
||||
axis_types = (
|
||||
AxisType.GLOBAL, AxisType.UPCAST, AxisType.LOCAL, AxisType.UPCAST,
|
||||
AxisType.GLOBAL, AxisType.UPCAST, AxisType.LOCAL, AxisType.UPCAST,
|
||||
AxisType.REDUCE, AxisType.REDUCE)
|
||||
# open inner k range
|
||||
k = UOp.range(BLOCK_K, 3)
|
||||
|
||||
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
|
||||
# ---------------------------
|
||||
# LOCAL -> REG (per-wave tiles)
|
||||
# ---------------------------
|
||||
Bs_view = Bs.reshape(BLOCK_K, WAVES_IN_BLOCK_X, ITERS_PER_WAVE_N, LANES_PER_WAVE_X, TN)
|
||||
iterWaveN = UOp.range(ITERS_PER_WAVE_N, 4)
|
||||
i = UOp.range(TN, 5)
|
||||
B_row = B_row[iterWaveN, i].set(Bs_view[k, waveIdx, iterWaveN, idxInWave, i], end=(iterWaveN, i))
|
||||
|
||||
def hand_spec_kernel3(kernel4=getenv("K4", 0), kernel5=getenv("K5", 0)):
|
||||
BLOCK_SIZE = 128 if kernel5 else 256
|
||||
As_view = As.reshape(BLOCK_K, WAVES_IN_BLOCK_Y, ITERS_PER_WAVE_M, LANES_PER_WAVE_Y, TM)
|
||||
iterWaveM = UOp.range(ITERS_PER_WAVE_M, 6)
|
||||
i = UOp.range(TM, 7)
|
||||
A_col = A_col[iterWaveM, i].set(As_view[k, waveIdy, iterWaveM, idyInWave, i], end=(iterWaveM, i))
|
||||
|
||||
nbWaves = BLOCK_SIZE // 32
|
||||
WN = 128 if kernel5 else 64
|
||||
WM = BN * BM // nbWaves // WN
|
||||
# ---------------------------
|
||||
# FMA: c_regs += A_col * B_row
|
||||
# ---------------------------
|
||||
iterWaveM = UOp.range(ITERS_PER_WAVE_M, 8)
|
||||
yt = UOp.range(TM, 9)
|
||||
iterWaveN = UOp.range(ITERS_PER_WAVE_N, 10)
|
||||
xt = UOp.range(TN, 12)
|
||||
c_idx = c_regs.after(k, k_tile_range)[iterWaveM, yt, iterWaveN, xt]
|
||||
sink = c_idx.store(c_idx + 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
|
||||
|
||||
nbThreadXPerWave = 8
|
||||
nbThreadYPerWave = 4
|
||||
|
||||
idxInWave = indexInWave % nbThreadXPerWave
|
||||
idyInWave = indexInWave // nbThreadXPerWave
|
||||
|
||||
nbIterWaveN = WN // (nbThreadXPerWave * TN)
|
||||
nbIterWaveM = WM // (nbThreadYPerWave * TM)
|
||||
|
||||
SUBWN = WN // nbIterWaveN
|
||||
SUBWM = WM // nbIterWaveM
|
||||
|
||||
# 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
|
||||
|
||||
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)
|
||||
# ---------------------------
|
||||
# REG -> GLOBAL (epilogue)
|
||||
# ---------------------------
|
||||
c = c.reshape(N//BLOCK_M, WAVES_IN_BLOCK_Y, ITERS_PER_WAVE_M, LANES_PER_WAVE_Y, TM,
|
||||
N//BLOCK_N, WAVES_IN_BLOCK_X, ITERS_PER_WAVE_N, LANES_PER_WAVE_X, TN)
|
||||
iterWaveM = UOp.range(ITERS_PER_WAVE_M, 1000)
|
||||
yt = UOp.range(TM, 1001)
|
||||
iterWaveN = UOp.range(nbIterWaveN, 1002)
|
||||
iterWaveN = UOp.range(ITERS_PER_WAVE_N, 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)
|
||||
c_glbl_idx = c[blockIdx_y, waveIdy, iterWaveM, idyInWave, yt, blockIdx_x, waveIdx, iterWaveN, idxInWave, xt]
|
||||
sink = c_glbl_idx.store(c_regs.after(sink)[iterWaveM, yt, iterWaveN, xt])
|
||||
sink = sink.end(iterWaveM, iterWaveN, yt, xt)
|
||||
|
||||
return sink.sink(arg=KernelInfo(name="tinygemm"))
|
||||
return sink.sink(arg=KernelInfo(opts_to_apply=())).simplify()
|
||||
|
||||
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)
|
||||
|
||||
ei = ExecItem(get_runner(Device.DEFAULT, sink), [t.uop.buffer for t in [hc, a, b]])
|
||||
|
||||
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}")
|
||||
|
||||
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!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
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!")
|
||||
test_matmul(hand_spec_kernel3(), N=N)
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
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)
|
||||
@@ -0,0 +1,229 @@
|
||||
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
|
||||
# -- add working slice a[gx, :, i] -> shape of the : (aka (16,16,32) becomes (16,))
|
||||
# -- 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)
|
||||
# -- 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"
|
||||
@@ -5,8 +5,10 @@ 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.float
|
||||
acc_dtype = dtypes.half if getenv("ACC_HALF") else dtypes.bfloat16 if getenv("ACC_BFLOAT16") else None
|
||||
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)
|
||||
if getenv("INT"): dtype_in, acc_dtype = dtypes.int8, dtypes.int32
|
||||
if getenv("UINT"): dtype_in, acc_dtype = dtypes.uint8, dtypes.int32
|
||||
|
||||
@@ -14,8 +16,10 @@ N = getenv("N", 4096)
|
||||
M = getenv("M", N)
|
||||
K = getenv("K", N)
|
||||
CNT = getenv("CNT", 10)
|
||||
ATOL = getenv("ATOL", 1e-4)
|
||||
RTOL = getenv("RTOL", 3e-2)
|
||||
|
||||
atol, rtol = {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)
|
||||
|
||||
INT_LOW = getenv("INT_LOW", 0)
|
||||
INT_HIGH = getenv("INT_HIGH", 10)
|
||||
|
||||
|
||||
@@ -8,19 +8,23 @@ 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]:
|
||||
for dtype in [torch.float32, torch.float16, torch.bfloat16]:
|
||||
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}")
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from tinygrad.runtime.support.system import System
|
||||
import argparse, glob, os, re, time, subprocess, sys
|
||||
import argparse, glob, os, 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 ""
|
||||
@@ -12,7 +11,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): System.pci_reset(pci_bus)
|
||||
def _do_reset_device(pci_bus): os.system(f"sudo sh -c 'echo 1 > /sys/bus/pci/devices/{pci_bus}/reset'")
|
||||
def _is_module_loaded(name: str) -> bool: return os.path.isdir(f"/sys/module/{name}")
|
||||
|
||||
def cmd_remove_module(args):
|
||||
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/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
|
||||
|
||||
+40
-22
@@ -7,31 +7,34 @@ import os
|
||||
NUM_WORKGROUPS = 96
|
||||
WAVE_SIZE = 32
|
||||
NUM_WAVES = 2
|
||||
FLOPS_PER_MATMUL = 16*16*16*2
|
||||
INTERNAL_LOOP = 1_000_000
|
||||
INSTRUCTIONS_PER_LOOP = 1_000
|
||||
FLOPS_PER_MATMUL = 16*16*16*2
|
||||
INTERNAL_LOOP = 1_000_00
|
||||
INSTRUCTIONS_PER_LOOP = 200
|
||||
DIRECTIVE = ".amdhsa_wavefront_size32 1"
|
||||
|
||||
assemblyTemplate = (pathlib.Path(__file__).parent / "template.s").read_text()
|
||||
|
||||
def launchBenchmark(instruction, vgprIndices, dense = True):
|
||||
if dense:
|
||||
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:
|
||||
instructions = "{} v[0:{}], v[{}:{}], v[{}:{}], 1\n".format(instruction, vgprIndices[0],
|
||||
vgprIndices[1], vgprIndices[2],
|
||||
vgprIndices[1], vgprIndices[2]) * INSTRUCTIONS_PER_LOOP
|
||||
vgprIndices[1], vgprIndices[2])
|
||||
else:
|
||||
instructions = "{} v[0:{}], v[{}:{}], v[{}:{}], v{}\n".format(instruction, vgprIndices[0],
|
||||
vgprIndices[1], vgprIndices[2],
|
||||
vgprIndices[3], vgprIndices[4],
|
||||
vgprIndices[5]) * INSTRUCTIONS_PER_LOOP
|
||||
src = assemblyTemplate.replace("INSTRUCTION", instructions)
|
||||
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)
|
||||
lib = COMPILER.compile(src)
|
||||
fxn = AMDProgram(DEV, "matmul", lib)
|
||||
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
|
||||
elapsed = fxn(global_size=(NUM_WORKGROUPS,1,1), local_size=(WAVE_SIZE*NUM_WAVES,1,1), wait=True)
|
||||
FLOPs = FLOPS_PER_MATMUL * NUM_WAVES * NUM_WORKGROUPS * INTERNAL_LOOP * INSTRUCTIONS_PER_LOOP
|
||||
print("{:<29} : {} T(FL)OPS".format(instruction, round(FLOPs/elapsed/10**12, 2)))
|
||||
print(f"{instruction:<29} : {FLOPs/elapsed/10**12:.2f} T(FL)OPS")
|
||||
|
||||
if __name__=="__main__":
|
||||
DEVICENUM = os.getenv("DEVICENUM", "0")
|
||||
@@ -40,18 +43,17 @@ if __name__=="__main__":
|
||||
except:
|
||||
raise RuntimeError("Error while initiating AMD device")
|
||||
|
||||
if (ARCH := DEV.arch) not in ['gfx1100', 'gfx1201']:
|
||||
raise RuntimeError("only gfx1100 and gfx1201 supported")
|
||||
COMPILER = HIPCompiler(ARCH)
|
||||
|
||||
if ARCH == 'gfx1100':
|
||||
COMPILER = HIPCompiler(DEV.arch)
|
||||
if DEV.arch in {'gfx1100', 'gfx1103'}:
|
||||
if DEV.arch == 'gfx1103':
|
||||
NUM_WORKGROUPS = 8
|
||||
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))
|
||||
if ARCH == 'gfx1201':
|
||||
elif DEV.arch == 'gfx1201':
|
||||
NUM_WORKGROUPS = 64
|
||||
launchBenchmark("v_wmma_bf16_16x16x16_bf16", (3,4,7))
|
||||
launchBenchmark("v_wmma_f16_16x16x16_f16", (3,4,7))
|
||||
@@ -76,4 +78,20 @@ 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)
|
||||
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.")
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
.text
|
||||
.globl matmul
|
||||
.p2align 8
|
||||
.p2align 8
|
||||
.type matmul,@function
|
||||
matmul:
|
||||
s_mov_b32 s1, 1000000
|
||||
s_mov_b32 s1, INTERNAL_LOOP
|
||||
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
|
||||
.amdhsa_wavefront_size32 1
|
||||
DIRECTIVE
|
||||
.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,6 +36,5 @@ amdhsa.kernels:
|
||||
.sgpr_count: 8
|
||||
.vgpr_count: 32
|
||||
.max_flat_workgroup_size: 1024
|
||||
.args:
|
||||
...
|
||||
.end_amdgpu_metadata
|
||||
+107
-4
@@ -3,8 +3,21 @@ 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:
|
||||
@@ -44,11 +57,9 @@ def get_example_inputs(graph_inputs:dict[str, OnnxValue], config={}):
|
||||
ret.update({name:value})
|
||||
return ret
|
||||
|
||||
def validate(onnx_file, inputs, rtol=1e-5, atol=1e-5):
|
||||
def _get_tinygrad_and_ort_np_outputs(onnx_file, inputs):
|
||||
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)
|
||||
@@ -56,9 +67,101 @@ def validate(onnx_file, inputs, rtol=1e-5, atol=1e-5):
|
||||
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.numpy(), onnx_v, rtol=rtol, atol=atol, err_msg=f"For tensor '{k}' in {tinygrad_out.keys()}")
|
||||
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
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<database xmlns="http://nouveau.freedesktop.org/"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.xsd">
|
||||
xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
|
||||
<import file="freedreno_copyright.xml"/>
|
||||
<import file="adreno/adreno_common.xml"/>
|
||||
|
||||
<enum name="vgt_event_type" varset="chip">
|
||||
@@ -20,9 +21,9 @@ xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.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"/>
|
||||
@@ -30,8 +31,8 @@ xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.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_COUNT_CONTROL.copy is true, writes OQ Z passed
|
||||
sample counts to RB_SAMPLE_COUNT_ADDR. This writes to main
|
||||
If A6XX_RB_SAMPLE_COUNTER_CNTL.copy is true, writes OQ Z passed
|
||||
sample counts to RB_SAMPLE_COUNTER_BASE. This writes to main
|
||||
memory, skipping UCHE.
|
||||
</doc>
|
||||
<value name="ZPASS_DONE" value="21"/>
|
||||
@@ -96,6 +97,13 @@ xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.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.
|
||||
@@ -112,11 +120,12 @@ xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.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="UNK_40" value="40" variants="A7XX"/>
|
||||
<value name="LRZ_CACHE_INVALIDATE" value="40" variants="A7XX-"/>
|
||||
<value name="LRZ_Q_CACHE_INVALIDATE" value="41" variants="A7XX-"/>
|
||||
<value name="BLIT_OP_SCALE_2D" value="42" variants="A5XX-"/>
|
||||
<value name="CONTEXT_DONE_2D" value="43" variants="A5XX-"/>
|
||||
<value name="UNK_2C" value="44" variants="A5XX-"/>
|
||||
<value name="UNK_2D" value="45" variants="A5XX-"/>
|
||||
<value name="VSC_BINNING_START" value="44" variants="A5XX-"/>
|
||||
<value name="VSC_BINNING_END" value="45" variants="A5XX-"/>
|
||||
|
||||
<!-- a6xx events -->
|
||||
<doc>
|
||||
@@ -129,21 +138,22 @@ xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.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="CACHE_FLUSH7" value="50" variants="A7XX-"/>
|
||||
<value name="CACHE_INVALIDATE7" value="51" variants="A7XX-"/>
|
||||
<value name="DEPTH_BUFFER_FLIP" value="0x3d" variants="A8XX-"/>
|
||||
</enum>
|
||||
|
||||
<enum name="pc_di_primtype">
|
||||
@@ -324,7 +334,7 @@ xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.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"/>
|
||||
<value name="CP_SET_CONSTANT" value="0x2d" variants="A2XX"/>
|
||||
<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>
|
||||
@@ -371,7 +381,7 @@ xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.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"/>
|
||||
<value name="CP_COND_INDIRECT_BUFFER_PFE" value="0x3a" variants="A3XX-A5XX"/>
|
||||
<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>
|
||||
@@ -514,7 +524,7 @@ xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.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
|
||||
-->
|
||||
@@ -537,7 +547,7 @@ xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.xsd">
|
||||
<value name="CP_LOAD_STATE6_GEOM" value="0x32" variants="A6XX-"/>
|
||||
<value name="CP_LOAD_STATE6_FRAG" value="0x34" variants="A6XX-"/>
|
||||
<!--
|
||||
Note: For IBO state (Image/SSBOs) which have shared state across
|
||||
Note: For UAV 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.
|
||||
@@ -566,20 +576,21 @@ xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.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"/>
|
||||
<!-- Note, kgsl calls this CP_SET_AMBLE: -->
|
||||
<value name="CP_SET_CTXSWITCH_IB" value="0x55" variants="A6XX-"/>
|
||||
<value name="CP_SET_AMBLE" value="0x55" variants="A6XX-"/>
|
||||
|
||||
<!--
|
||||
Seems to always have the payload:
|
||||
@@ -630,8 +641,7 @@ xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.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-"/>
|
||||
<!-- similar to CP_CONTEXT_REG_BUNCH, but discards first two dwords?? -->
|
||||
<value name="CP_CONTEXT_REG_BUNCH2" value="0x5d" variants="A7XX-"/>
|
||||
<value name="CP_NON_CONTEXT_REG_BUNCH" 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.
|
||||
@@ -648,6 +658,11 @@ xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.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>
|
||||
|
||||
|
||||
@@ -790,14 +805,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_IBO" value="0xe"/>
|
||||
<value name="SB6_CS_IBO" value="0xf"/>
|
||||
<value name="SB6_UAV" value="0xe"/>
|
||||
<value name="SB6_CS_UAV" 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_IBO" value="3"/>
|
||||
<value name="ST6_UAV" value="3"/>
|
||||
</enum>
|
||||
<enum name="a6xx_state_src">
|
||||
<value name="SS6_DIRECT" value="0"/>
|
||||
@@ -903,12 +918,6 @@ 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 -->
|
||||
@@ -1084,8 +1093,10 @@ 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"/>
|
||||
<bitfield name="GROUP_ID" low="24" high="28" type="uint"/>
|
||||
<!-- high bit is 28 until a750: -->
|
||||
<bitfield name="GROUP_ID" low="24" high="29" 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>
|
||||
@@ -1119,39 +1130,63 @@ 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>
|
||||
<!-- 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"/>
|
||||
<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>
|
||||
</domain>
|
||||
|
||||
<domain name="CP_SET_BIN_DATA5_OFFSET" width="32">
|
||||
@@ -1162,23 +1197,42 @@ 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>
|
||||
<!-- 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 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>
|
||||
</domain>
|
||||
|
||||
<domain name="CP_REG_RMW" width="32">
|
||||
@@ -1196,6 +1250,9 @@ 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"/>
|
||||
@@ -1209,7 +1266,7 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
|
||||
</reg32>
|
||||
</domain>
|
||||
|
||||
<domain name="CP_REG_TO_MEM" width="32">
|
||||
<domain name="CP_REG_TO_MEM" width="32" prefix="chip">
|
||||
<reg32 offset="0" name="0">
|
||||
<bitfield name="REG" low="0" high="17" type="hex"/>
|
||||
<!-- number of registers/dwords copied is max(CNT, 1). -->
|
||||
@@ -1217,12 +1274,12 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
|
||||
<bitfield name="64B" pos="30" type="boolean"/>
|
||||
<bitfield name="ACCUMULATE" pos="31" type="boolean"/>
|
||||
</reg32>
|
||||
<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>
|
||||
<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>
|
||||
</domain>
|
||||
|
||||
<domain name="CP_REG_TO_MEM_OFFSET_REG" width="32">
|
||||
@@ -1238,12 +1295,7 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
|
||||
<bitfield name="64B" pos="30" type="boolean"/>
|
||||
<bitfield name="ACCUMULATE" pos="31" type="boolean"/>
|
||||
</reg32>
|
||||
<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>
|
||||
<reg64 offset="1" name="DEST" type="waddress"/>
|
||||
<reg32 offset="3" name="3">
|
||||
<bitfield name="OFFSET0" low="0" high="17" type="hex"/>
|
||||
<bitfield name="OFFSET0_SCRATCH" pos="19" type="boolean"/>
|
||||
@@ -1263,18 +1315,8 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
|
||||
<bitfield name="64B" pos="30" type="boolean"/>
|
||||
<bitfield name="ACCUMULATE" pos="31" type="boolean"/>
|
||||
</reg32>
|
||||
<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>
|
||||
<reg64 offset="1" name="DEST" type="waddress"/>
|
||||
<reg64 offset="3" name="OFFSET" type="waddress"/>
|
||||
</domain>
|
||||
|
||||
<domain name="CP_MEM_TO_REG" width="32">
|
||||
@@ -1287,12 +1329,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>
|
||||
<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>
|
||||
<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>
|
||||
</domain>
|
||||
|
||||
<domain name="CP_MEM_TO_MEM" width="32">
|
||||
@@ -1312,6 +1354,10 @@ 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
|
||||
@@ -1346,6 +1392,8 @@ 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>
|
||||
|
||||
@@ -1368,12 +1416,12 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
|
||||
</domain>
|
||||
|
||||
<domain name="CP_MEM_WRITE" width="32">
|
||||
<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>
|
||||
<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>
|
||||
<!-- followed by the DWORDs to write -->
|
||||
</domain>
|
||||
|
||||
@@ -1425,24 +1473,14 @@ 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>
|
||||
<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>
|
||||
<reg64 offset="1" name="POLL_ADDR" type="address"/>
|
||||
<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>
|
||||
<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>
|
||||
<reg64 offset="5" name="WRITE_ADDR" type="waddress"/>
|
||||
<reg32 offset="7" name="7">
|
||||
<bitfield name="WRITE_DATA" low="0" high="31"/>
|
||||
</reg32>
|
||||
@@ -1457,12 +1495,7 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
|
||||
<!-- Reserved for flags, presumably? Unused in FW -->
|
||||
<bitfield name="RESERVED" low="0" high="31" type="hex"/>
|
||||
</reg32>
|
||||
<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>
|
||||
<reg64 offset="1" name="POLL_ADDR" type="address"/>
|
||||
<reg32 offset="3" name="3">
|
||||
<bitfield name="REF" low="0" high="31"/>
|
||||
</reg32>
|
||||
@@ -1480,12 +1513,7 @@ 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>
|
||||
<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>
|
||||
<reg64 offset="1" name="POLL_ADDR" type="address"/>
|
||||
<reg32 offset="3" name="3">
|
||||
<bitfield name="REF" low="0" high="31"/>
|
||||
</reg32>
|
||||
@@ -1619,12 +1647,7 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
|
||||
TODO what is gpuaddr for, seems to be all 0's.. maybe needed for
|
||||
context switch?
|
||||
-->
|
||||
<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>
|
||||
<reg64 offset="1" name="ADDR" type="waddress"/>
|
||||
<reg32 offset="3" name="3">
|
||||
<!-- ??? -->
|
||||
</reg32>
|
||||
@@ -1653,8 +1676,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_SAMPLE_COUNT_DIFF" pos="14" type="boolean"/>
|
||||
<!-- *(iova + 8) += *(iova + 16) - *iova -->
|
||||
<bitfield name="WRITE_ACCUM_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 -->
|
||||
@@ -1668,15 +1691,11 @@ 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">
|
||||
<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>
|
||||
<reg64 offset="1" name="1" type="waddress"/>
|
||||
<reg32 offset="3" name="3">
|
||||
<bitfield name="PAYLOAD_0" low="0" high="31"/>
|
||||
</reg32>
|
||||
@@ -1743,9 +1762,7 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
|
||||
<reg32 offset="0" name="0">
|
||||
</reg32>
|
||||
<stripe varset="chip" variants="A4XX">
|
||||
<reg32 offset="1" name="1">
|
||||
<bitfield name="ADDR" low="0" high="31"/>
|
||||
</reg32>
|
||||
<reg32 offset="1" name="ADDR" type="address"/>
|
||||
<reg32 offset="2" name="2">
|
||||
<!-- localsize is value minus one: -->
|
||||
<bitfield name="LOCALSIZEX" low="2" high="11" type="uint"/>
|
||||
@@ -1754,12 +1771,7 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
|
||||
</reg32>
|
||||
</stripe>
|
||||
<stripe varset="chip" variants="A5XX-">
|
||||
<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>
|
||||
<reg64 offset="1" name="ADDR" type="address"/>
|
||||
<reg32 offset="3" name="3">
|
||||
<!-- localsize is value minus one: -->
|
||||
<bitfield name="LOCALSIZEX" low="2" high="11" type="uint"/>
|
||||
@@ -1771,40 +1783,88 @@ 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_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="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="8" name="RM6_COMPUTE"/>
|
||||
<value value="0xc" name="RM6_BLIT2DSCALE"/> <!-- no-op (at least on current sqe fw) -->
|
||||
<value value="12" 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="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"/>
|
||||
<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"/>
|
||||
</enum>
|
||||
<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>
|
||||
<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>
|
||||
</domain>
|
||||
|
||||
<domain name="CP_SET_PSEUDO_REG" width="32" varset="chip" prefix="chip" variants="A6XX-">
|
||||
@@ -1830,9 +1890,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="DRAW_STRM_ADDRESS"/>
|
||||
<value value="9" name="DRAW_STRM_SIZE_ADDRESS"/>
|
||||
<value value="10" name="PRIM_STRM_ADDRESS"/>
|
||||
<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="11" name="UNK_STRM_ADDRESS"/>
|
||||
<value value="12" name="UNK_STRM_SIZE_ADDRESS"/>
|
||||
|
||||
@@ -1933,11 +1993,11 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
|
||||
a bitmask of which modes pass the test.
|
||||
-->
|
||||
|
||||
<!-- RM6_BINNING -->
|
||||
<!-- RM6_BIN_VISIBILITY -->
|
||||
<bitfield name="BINNING" pos="25" variants="RENDER_MODE" type="boolean"/>
|
||||
<!-- all others -->
|
||||
<bitfield name="GMEM" pos="26" variants="RENDER_MODE" type="boolean"/>
|
||||
<!-- RM6_BYPASS -->
|
||||
<!-- RM6_DIRECT_RENDER -->
|
||||
<bitfield name="SYSMEM" pos="27" variants="RENDER_MODE" type="boolean"/>
|
||||
|
||||
<bitfield name="BV" pos="25" variants="THREAD_MODE" type="boolean"/>
|
||||
@@ -2010,54 +2070,45 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
|
||||
</reg32>
|
||||
</domain>
|
||||
|
||||
<domain name="CP_SET_CTXSWITCH_IB" width="32">
|
||||
<domain name="CP_SET_AMBLE" width="32">
|
||||
<doc>
|
||||
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.
|
||||
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.
|
||||
</doc>
|
||||
<enum name="ctxswitch_ib">
|
||||
<value name="RESTORE_IB" value="0">
|
||||
<enum name="amble_type">
|
||||
<value name="PREAMBLE_AMBLE_TYPE" value="0">
|
||||
<doc>Executed unconditionally when switching back to the context.</doc>
|
||||
</value>
|
||||
<value name="YIELD_RESTORE_IB" value="1">
|
||||
<value name="BIN_PREAMBLE_AMBLE_TYPE" value="1">
|
||||
<doc>
|
||||
Executed when switching back after switching
|
||||
away during execution of
|
||||
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.
|
||||
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.
|
||||
</doc>
|
||||
</value>
|
||||
<value name="SAVE_IB" value="2">
|
||||
<value name="POSTAMBLE_AMBLE_TYPE" value="2">
|
||||
<doc>
|
||||
Executed when switching away from the context,
|
||||
except for context switches initiated via
|
||||
CP_YIELD.
|
||||
</doc>
|
||||
</value>
|
||||
<value name="RB_SAVE_IB" value="3">
|
||||
<value name="KMD_AMBLE_TYPE" 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 SAVE_IB.
|
||||
|
||||
Note, kgsl calls this CP_KMD_AMBLE_TYPE
|
||||
is otherwise similar to POSTAMBLE_AMBLE_TYPE.
|
||||
</doc>
|
||||
</value>
|
||||
</enum>
|
||||
<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>
|
||||
<reg64 offset="0" name="ADDR" type="address"/>
|
||||
<reg32 offset="2" name="2">
|
||||
<bitfield name="DWORDS" low="0" high="19" type="uint"/>
|
||||
<bitfield name="TYPE" low="20" high="21" type="ctxswitch_ib"/>
|
||||
<bitfield name="TYPE" low="20" high="21" type="amble_type"/>
|
||||
</reg32>
|
||||
</domain>
|
||||
|
||||
@@ -2089,12 +2140,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_DEPTH_VIEW with previous values, and if one of
|
||||
GRAS_LRZ_VIEW_INFO 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_DEPTH_VIEW has changed
|
||||
- GRAS_LRZ_VIEW_INFO has changed
|
||||
then it does a LRZ_FLUSH with GRAS_LRZ_CNTL::ENABLE
|
||||
forced to 1.
|
||||
Only exists in a650_sqe.fw.
|
||||
@@ -2209,7 +2260,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_STATE registers
|
||||
Best guess is that it is a faster way to fetch all the VSC_CHANNEL_VISIBILITY registers
|
||||
and keep them in a local scratch memory instead of fetching every time
|
||||
when skipping IBs.
|
||||
</doc>
|
||||
@@ -2257,7 +2308,25 @@ 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_GLOBAL_LOCAL_TS" pos="2" 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"/>
|
||||
</reg32>
|
||||
</domain>
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ import ctypes
|
||||
|
||||
|
||||
class AsDictMixin:
|
||||
import sys
|
||||
if sys.version_info >= (3, 14): _layout_ = 'ms'
|
||||
@classmethod
|
||||
def as_dict(cls, self):
|
||||
result = {}
|
||||
|
||||
@@ -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 > 2:
|
||||
if state_type == ST6_SHADER and IOCTL > 3:
|
||||
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,25 +106,30 @@ def parse_cmd_buf(dat):
|
||||
print('constants')
|
||||
hexdump(x)
|
||||
if state_type == ST6_IBO:
|
||||
ibos_bytes = get_mem((vals[2] << 32) | vals[1], num_unit * 16 * 4)
|
||||
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)
|
||||
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:
|
||||
samplers_bytes = get_mem((vals[2] << 32) | vals[1], num_unit * 4 * 4)
|
||||
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)
|
||||
CAPTURED_STATE['samplers'] = samplers_bytes[:]
|
||||
if IOCTL > 1:
|
||||
print('texture samplers')
|
||||
hexdump(samplers_bytes)
|
||||
if state_type == ST6_CONSTANTS:
|
||||
descriptors_bytes = get_mem((vals[2] << 32) | vals[1], 1600)
|
||||
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)
|
||||
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)
|
||||
@@ -152,6 +157,10 @@ 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]))
|
||||
@@ -171,8 +180,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": pass
|
||||
# mmaped[s.gpuaddr] = mmap.mmap(fd, s.size, offset=s.id*0x1000)
|
||||
if name == "IOCTL_KGSL_GPUOBJ_INFO":
|
||||
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)
|
||||
|
||||
@@ -882,6 +882,11 @@ 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 {
|
||||
@@ -930,7 +935,7 @@ impl<'a> Thread<'a> {
|
||||
|
||||
let op = ((instr >> 16) & 0x3ff) as u32;
|
||||
match op {
|
||||
764 | 765 | 288 | 289 | 290 | 766 | 767 | 768 | 769 => {
|
||||
764 | 765 | 288 | 289 | 290 | 766 | 767 | 768 | 769 | 770 => {
|
||||
let vdst = (instr & 0xff) as usize;
|
||||
let sdst = ((instr >> 8) & 0x7f) as usize;
|
||||
let f = |i: u32| -> usize { ((instr >> i) & 0x1ff) as usize };
|
||||
@@ -996,6 +1001,10 @@ 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() {
|
||||
|
||||
+64
-119
@@ -1,98 +1,32 @@
|
||||
import numpy as np
|
||||
import unittest
|
||||
import subprocess, struct, math
|
||||
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
|
||||
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
|
||||
|
||||
@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 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()
|
||||
|
||||
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]
|
||||
@@ -105,54 +39,57 @@ class TestHW(unittest.TestCase):
|
||||
|
||||
def test_simple(self):
|
||||
out = get_output("""
|
||||
v_mov_b32_e32 v10 42
|
||||
v_mov_b32_e32 v1 v10
|
||||
""", n_threads=2)
|
||||
v_mov_b32_e32 %1 42
|
||||
v_mov_b32_e32 %2 %1
|
||||
""")[0]
|
||||
np.testing.assert_equal(out, 42)
|
||||
|
||||
def test_exec_mov(self):
|
||||
out = get_output("""
|
||||
v_mov_b32_e32 v10 42
|
||||
v_mov_b32_e32 %1 42
|
||||
s_mov_b32_e32 exec_lo 0b10
|
||||
v_mov_b32_e32 v10 10
|
||||
v_mov_b32_e32 %1 10
|
||||
s_mov_b32_e32 exec_lo 0b11
|
||||
v_mov_b32_e32 v1 v10
|
||||
v_mov_b32_e32 %2 %1
|
||||
""", 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 v10 42
|
||||
v_mov_b32_e32 v11 10
|
||||
v_mov_b32_e32 %1 42
|
||||
v_mov_b32_e32 %2 10
|
||||
s_mov_b32_e32 exec_lo 0b01
|
||||
v_cmp_ne_u32 v10 v11
|
||||
v_cmp_ne_u32 %1 %2
|
||||
s_mov_b32_e32 exec_lo 0b11
|
||||
v_mov_b32_e32 v1 vcc_lo
|
||||
v_mov_b32_e32 %2 vcc_lo
|
||||
""", n_threads=2)
|
||||
np.testing.assert_equal(out, 0b01)
|
||||
|
||||
def test_exec_cmpx_vop3(self):
|
||||
out = get_output("""
|
||||
v_mov_b32_e32 v10 42
|
||||
v_mov_b32_e32 v11 10
|
||||
s_mov_b32_e32 exec_lo 0b11
|
||||
v_mov_b32_e32 %1 42
|
||||
v_mov_b32_e32 %2 10
|
||||
s_mov_b32_e32 exec_lo 0b01
|
||||
v_cmpx_ne_u32 v10 v11
|
||||
v_cmpx_ne_u32 %1 %2
|
||||
s_mov_b32_e32 s10 exec_lo
|
||||
s_mov_b32_e32 exec_lo 0b11
|
||||
v_mov_b32_e32 v1 s10
|
||||
""", n_threads=2)
|
||||
np.testing.assert_equal(out, 0b01)
|
||||
v_mov_b32_e32 %2 s10
|
||||
""", n_threads=2)[0]
|
||||
np.testing.assert_equal(out & 0b11, 0b01)
|
||||
|
||||
def test_fmac_vop3_modifier(self):
|
||||
init_state = f"""
|
||||
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)}
|
||||
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));
|
||||
"""
|
||||
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.))
|
||||
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.))
|
||||
|
||||
def test_s_abs_i32(self):
|
||||
def s_abs_i32(x, y, dst="s10", scc=0):
|
||||
@@ -160,7 +97,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 v1 {reg}
|
||||
v_mov_b32_e32 %2 {reg}
|
||||
""")[0], val)
|
||||
s_abs_i32(0x00000001, 0x00000001, scc=1)
|
||||
s_abs_i32(0x7fffffff, 0x7fffffff, scc=1)
|
||||
@@ -173,8 +110,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 v1 {f32_to_bits(x)}
|
||||
v_rcp_f32_e64 v1, -v1
|
||||
v_mov_b32_e32 %2 {f32_to_bits(x)}
|
||||
v_rcp_f32_e64 %2, -%2
|
||||
""")[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)
|
||||
@@ -186,10 +123,11 @@ 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 v1 {f32_to_bits(x)}
|
||||
s_mov_b32_e32 s10 1 // always pick -v1
|
||||
v_cndmask_b32 v1, v1, -v1 s10
|
||||
v_mov_b32_e32 %2 {f32_to_bits(x)}
|
||||
s_mov_b32_e32 s10 1
|
||||
v_cndmask_b32 %2, %2, -%2 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)
|
||||
@@ -198,5 +136,12 @@ 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,8 +2,6 @@
|
||||
|
||||
## 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.
|
||||
|
||||
+25
-7
@@ -1,9 +1,9 @@
|
||||
import ctypes, pathlib, argparse, pickle, re, functools, dataclasses
|
||||
import ctypes, pathlib, argparse, pickle, re, functools, dataclasses, itertools
|
||||
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
|
||||
from tinygrad.runtime.ops_amd import ProfileSQTTEvent, ProfilePMCEvent
|
||||
|
||||
@dataclasses.dataclass
|
||||
class InstInfo:
|
||||
@@ -27,14 +27,18 @@ class _ROCParseCtx:
|
||||
self.disasms[prog.base + addr] = info
|
||||
self.addr2prg[prog.base + addr] = prog
|
||||
|
||||
def next_sqtt(self): return next(self.sqtt_evs, None)
|
||||
def next_sqtt(self):
|
||||
x = next(self.sqtt_evs, None)
|
||||
self.active_se = x.se if x is not None else None
|
||||
return x
|
||||
|
||||
def find_program(self, addr): return self.addr2prg[addr]
|
||||
|
||||
def on_occupancy_ev(self, ev):
|
||||
if DEBUG >= 4: print("OCC", ev.time, ev.cu, ev.simd, ev.wave_id, ev.start)
|
||||
if DEBUG >= 4: print("OCC", ev.time, self.active_se, ev.cu, ev.simd, ev.wave_id, ev.start)
|
||||
|
||||
def on_wave_ev(self, ev):
|
||||
if DEBUG >= 4: print("WAVE", ev.wave_id, ev.cu, ev.simd, ev.contexts, ev.begin_time, ev.end_time)
|
||||
if DEBUG >= 4: print("WAVE", ev.wave_id, self.active_se, ev.cu, ev.simd, ev.contexts, ev.begin_time, ev.end_time)
|
||||
|
||||
asm = {}
|
||||
for j in range(ev.instructions_size):
|
||||
@@ -52,9 +56,11 @@ if __name__ == "__main__":
|
||||
|
||||
with args.profile.open("rb") as f: profile = pickle.load(f)
|
||||
sqtt_events:list[ProfileSQTTEvent] = []
|
||||
pmc_events:list[ProfilePMCEvent] = []
|
||||
prog_events:list[ProfileProgramEvent] = []
|
||||
for e in profile:
|
||||
if isinstance(e, ProfileSQTTEvent): sqtt_events.append(e)
|
||||
if isinstance(e, ProfilePMCEvent): pmc_events.append(e)
|
||||
if isinstance(e, ProfileProgramEvent) and e.device.startswith("AMD"): prog_events.append(e)
|
||||
|
||||
ROCParseCtx = _ROCParseCtx(sqtt_events, prog_events)
|
||||
@@ -92,5 +98,17 @@ if __name__ == "__main__":
|
||||
|
||||
return rocprof.ROCPROFILER_THREAD_TRACE_DECODER_STATUS_SUCCESS
|
||||
|
||||
rocprof.rocprof_trace_decoder_parse_data(copy_cb, trace_cb, isa_cb, None)
|
||||
print(ROCParseCtx.wave_events.keys())
|
||||
try:
|
||||
rocprof.rocprof_trace_decoder_parse_data(copy_cb, trace_cb, isa_cb, None)
|
||||
print('SQTT:', ROCParseCtx.wave_events.keys())
|
||||
except Exception as e: print("Error in sqtt decoder:", e)
|
||||
|
||||
for ev in pmc_events:
|
||||
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
|
||||
|
||||
@@ -11,6 +11,8 @@ import ctypes, ctypes.util
|
||||
|
||||
|
||||
class AsDictMixin:
|
||||
import sys
|
||||
if sys.version_info >= (3, 14): _layout_ = 'ms'
|
||||
@classmethod
|
||||
def as_dict(cls, self):
|
||||
result = {}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
#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*(128/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});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
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 * (128 // 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__ descriptor_dict() {}
|
||||
template<typename T> __host__ descriptor_dict(T _, int b, int d, int r, int c) {}
|
||||
__host__ __device__ descriptor_dict() {}
|
||||
template<typename T> __host__ __device__ 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__ 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) {
|
||||
__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) {
|
||||
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__ inline gl(T *_data,
|
||||
__host__ __device__ 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
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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});
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
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())
|
||||
@@ -0,0 +1,105 @@
|
||||
#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});
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
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()
|
||||
@@ -0,0 +1,39 @@
|
||||
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
|
||||
@@ -0,0 +1,31 @@
|
||||
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)}")
|
||||
@@ -155,16 +155,14 @@ def index_tensor(x, y):
|
||||
def zero_(x):
|
||||
if TORCH_DEBUG: print(f"zero_ {x.shape}")
|
||||
tt = unwrap(x)
|
||||
# NOTE: unconditional contiguous covers if x is contiguous (match it) or if x is view (realize for inplace)
|
||||
# TODO: consolidate
|
||||
tt.assign(tt.zeros_like().contiguous())
|
||||
tt.assign(tt.zeros_like())
|
||||
|
||||
@torch.library.impl("aten::fill_.Scalar", "privateuseone")
|
||||
@inplace_fn("x")
|
||||
def fill_scalar(x, y):
|
||||
if TORCH_DEBUG: print(f"fill_.Scalar {x.shape} {y}")
|
||||
tt = unwrap(x)
|
||||
tt.assign(tt.full_like(y).contiguous())
|
||||
tt.assign(tt.full_like(y))
|
||||
|
||||
@torch.library.impl("aten::_local_scalar_dense", "privateuseone")
|
||||
def _local_scalar_dense(tensor): return unwrap(tensor).item()
|
||||
@@ -644,10 +642,11 @@ def get_real_tinygrad_buffers():
|
||||
torch.nn.modules.module.register_module_buffer_registration_hook(register_torch_buffer)
|
||||
|
||||
from torch.nn.modules import Module
|
||||
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)
|
||||
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)
|
||||
torch.nn.modules.module.register_module_module_registration_hook(module_hook)
|
||||
|
||||
def realize_optimizer_step(optimizer: torch.optim.Optimizer, *args, **kwargs):
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
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
|
||||
@@ -0,0 +1,13 @@
|
||||
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
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
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)
|
||||
|
||||
// Add appropriate logic here to determine whether to replace the extension
|
||||
// with the new extension. Common things to check for include
|
||||
// testing whether the new extension's version number is newer than
|
||||
// the current version number, or whether the bundleIdentifier is different.
|
||||
// For simplicity, this sample always replaces the current extension
|
||||
// with the new one.
|
||||
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
|
||||
@@ -0,0 +1,589 @@
|
||||
// !$*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 */;
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?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>
|
||||
@@ -0,0 +1,37 @@
|
||||
<?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>
|
||||
@@ -0,0 +1,259 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?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&0x0000FFFF</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IOPCIMatch</key>
|
||||
<string>0x000010de&0x0000FFFF</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>com.apple.developer.driverkit.allow-any-userclient-access</key>
|
||||
<true/>
|
||||
<key>com.apple.developer.driverkit</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,38 @@
|
||||
#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 */
|
||||
@@ -0,0 +1,127 @@
|
||||
#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;
|
||||
};
|
||||
|
||||
bool TinyGPUDriverUserClient::init()
|
||||
{
|
||||
auto theAnswer = super::init();
|
||||
if (!theAnswer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ivars = IONewZero(TinyGPUDriverUserClient_IVars, 1);
|
||||
if (ivars == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TinyGPUDriverUserClient::free()
|
||||
{
|
||||
if (ivars != nullptr) {
|
||||
ivars->provider.reset();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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() == nullptr) {
|
||||
return kIOReturnNotAttached;
|
||||
}
|
||||
|
||||
if (type < 6) {
|
||||
uint32_t bar = (uint32_t)type;
|
||||
return ivars->provider->MapBar(bar, memory);
|
||||
}
|
||||
|
||||
// dma page buffer
|
||||
TinyGPUCreateDMAResp buf;
|
||||
kern_return_t err = ivars->provider->CreateDMA(type, &buf);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
*memory = buf.sharedBuf;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#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 */
|
||||
@@ -0,0 +1,12 @@
|
||||
<?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>
|
||||
@@ -0,0 +1,39 @@
|
||||
#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 it’s 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;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
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)
|
||||
@@ -25,8 +25,6 @@ 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
|
||||
|
||||
@@ -9,10 +9,11 @@ with open(directory / 'README.md', encoding='utf-8') as f:
|
||||
|
||||
testing_minimal = [
|
||||
"numpy",
|
||||
"torch==2.8.0",
|
||||
"torch==2.9.0",
|
||||
"pytest",
|
||||
"pytest-xdist",
|
||||
"pytest-timeout",
|
||||
"pytest-split",
|
||||
"hypothesis",
|
||||
"z3-solver",
|
||||
]
|
||||
@@ -42,7 +43,6 @@ setup(name='tinygrad',
|
||||
'tinygrad.runtime.support.am',
|
||||
'tinygrad.runtime.support.nv',
|
||||
'tinygrad.schedule',
|
||||
'tinygrad.shape',
|
||||
'tinygrad.uop',
|
||||
'tinygrad.viz',
|
||||
],
|
||||
@@ -52,7 +52,7 @@ setup(name='tinygrad',
|
||||
"License :: OSI Approved :: MIT License"
|
||||
],
|
||||
install_requires=[],
|
||||
python_requires='>=3.10',
|
||||
python_requires='>=3.11',
|
||||
extras_require={
|
||||
'arm': ["unicorn"],
|
||||
'triton': ["triton-nightly>=2.1.0.dev20231014192330"],
|
||||
|
||||
@@ -54,6 +54,8 @@ 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"]
|
||||
@@ -76,9 +78,12 @@ 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]):
|
||||
print(f"{dir_name:30s} : {sum([x[1] for x in group]):6d}")
|
||||
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])}")
|
||||
total_lines = sum([x[1] for x in table])
|
||||
print(f"\ntotal line count: {total_lines}")
|
||||
print(f"total 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"
|
||||
|
||||
+2
-3
@@ -1,4 +1,4 @@
|
||||
from tinygrad import Tensor, dtypes, Context, GlobalCounters
|
||||
from tinygrad import Tensor, dtypes, GlobalCounters
|
||||
dtypes.default_float = dtypes.float16
|
||||
from tinygrad.dtype import to_dtype
|
||||
from tinygrad.helpers import getenv
|
||||
@@ -13,6 +13,5 @@ if __name__ == "__main__":
|
||||
|
||||
# test single kernel softmax
|
||||
GlobalCounters.reset()
|
||||
with Context(DONT_GROUP_REDUCES=1):
|
||||
single_kernel_softmax(t, -1, acc_dtype).realize()
|
||||
single_kernel_softmax(t, -1, acc_dtype).realize()
|
||||
|
||||
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
# ruff: noqa: E501
|
||||
from tinygrad import dtypes, Device
|
||||
from tinygrad.uop.ops import UOp, AxisType, Ops
|
||||
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
|
||||
|
||||
# PYTHONPATH="." DEBUG=5 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
|
||||
# kernel 672
|
||||
# faster on d59d4cd, 50% slower with the new linearizer
|
||||
|
||||
""" d59d4cd
|
||||
c0 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((32, 1024, 4)), arg=0, src=())
|
||||
c1 = UOp.range(UOp.const(dtypes.index, 64), 3, AxisType.LOOP)
|
||||
c2 = UOp.range(UOp.const(dtypes.index, 64), 4, AxisType.LOOP)
|
||||
c3 = UOp.range(UOp.const(dtypes.index, 32), 2, AxisType.LOOP)
|
||||
c4 = (((c1*UOp.const(dtypes.index, 64))+c2)+(c3*UOp.const(dtypes.index, 4096)))
|
||||
c5 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((32, 1024, 4)), arg=1, src=())
|
||||
c6 = c5.index(c4).load()
|
||||
c7 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((32, 3072, 4)), arg=2, src=())
|
||||
c8 = UOp.range(UOp.const(dtypes.index, 48), 0, AxisType.REDUCE)
|
||||
c9 = UOp.range(UOp.const(dtypes.index, 4), 1, AxisType.REDUCE)
|
||||
c10 = c7.index(((((c8*UOp.const(dtypes.index, 4))+c9)+(c1*UOp.const(dtypes.index, 192)))+(c3*UOp.const(dtypes.index, 12288)))).load()
|
||||
c11 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((16, 192, 4)), arg=3, src=())
|
||||
c12 = c11.index(((((c9*UOp.const(dtypes.index, 4))+(c2%UOp.const(dtypes.index, 4)))+(c8*UOp.const(dtypes.index, 16)))+((c2//UOp.const(dtypes.index, 4))*UOp.const(dtypes.index, 768)))).load()
|
||||
c13 = UOp(Ops.DEFINE_GLOBAL, dtypes.half.ptr(64), arg=4, src=())
|
||||
c14 = c13.index(c2).load()
|
||||
c15 = UOp(Ops.DEFINE_GLOBAL, dtypes.half.ptr(64), arg=5, src=())
|
||||
c16 = c15.index(c2).load()
|
||||
c17 = (c6+(((c10*c12.cast(dtypes.float)).cast(dtypes.float).reduce(c8, c9, arg=Ops.ADD)+c14.cast(dtypes.float))*c16.cast(dtypes.float)))
|
||||
c18 = c0.index(c4).store(c17, c3, c1, c2)
|
||||
ast = c18.sink()
|
||||
more upcast axis : [(3, 320, 0, 4)]
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
__kernel void r_512_16_4_4_48_4(write_only image2d_t data0_131072, read_only image2d_t data1_131072, read_only image2d_t data2_393216, read_only image2d_t data3_12288, __global half* data4_64, __global half* data5_64) {
|
||||
const sampler_t smp = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST;
|
||||
float acc0[16];
|
||||
int idx0 = get_global_id(0); /* 16 */
|
||||
int idx1 = get_global_id(1); /* 512 */
|
||||
int alu0 = (idx1>>4);
|
||||
*(acc0+0) = 0.0f;
|
||||
*(acc0+1) = 0.0f;
|
||||
*(acc0+2) = 0.0f;
|
||||
*(acc0+3) = 0.0f;
|
||||
*(acc0+4) = 0.0f;
|
||||
*(acc0+5) = 0.0f;
|
||||
*(acc0+6) = 0.0f;
|
||||
*(acc0+7) = 0.0f;
|
||||
*(acc0+8) = 0.0f;
|
||||
*(acc0+9) = 0.0f;
|
||||
*(acc0+10) = 0.0f;
|
||||
*(acc0+11) = 0.0f;
|
||||
*(acc0+12) = 0.0f;
|
||||
*(acc0+13) = 0.0f;
|
||||
*(acc0+14) = 0.0f;
|
||||
*(acc0+15) = 0.0f;
|
||||
for (int Ridx0 = 0; Ridx0 < 48; Ridx0++) {
|
||||
int alu17 = ((idx1*192)+Ridx0);
|
||||
int alu18 = (alu17+48);
|
||||
int alu19 = (alu17+96);
|
||||
int alu20 = (alu17+144);
|
||||
int alu21 = (Ridx0<<2);
|
||||
float4 val0 = read_imagef(data3_12288, smp, (int2)(alu21,idx0));
|
||||
float4 val1 = read_imagef(data3_12288, smp, (int2)((alu21+1),idx0));
|
||||
float4 val2 = read_imagef(data3_12288, smp, (int2)((alu21+2),idx0));
|
||||
float4 val3 = read_imagef(data3_12288, smp, (int2)((alu21+3),idx0));
|
||||
float4 val4 = read_imagef(data2_393216, smp, (int2)((alu18-(3072*(((alu18>>10)*43)>>7))),alu0));
|
||||
float4 val5 = read_imagef(data2_393216, smp, (int2)((alu19-(3072*(((alu19>>10)*43)>>7))),alu0));
|
||||
float4 val6 = read_imagef(data2_393216, smp, (int2)((alu20-(3072*(((alu20>>10)*43)>>7))),alu0));
|
||||
float4 val7 = read_imagef(data2_393216, smp, (int2)((alu17-(3072*(((alu17>>10)*43)>>7))),alu0));
|
||||
*(acc0+1) = ((*(acc0+1))+(val4.x*val0.x)+(val4.y*val1.x)+(val4.z*val2.x)+(val4.w*val3.x));
|
||||
*(acc0+5) = ((*(acc0+5))+(val4.x*val0.y)+(val4.y*val1.y)+(val4.z*val2.y)+(val4.w*val3.y));
|
||||
*(acc0+9) = ((*(acc0+9))+(val4.x*val0.z)+(val4.y*val1.z)+(val4.z*val2.z)+(val4.w*val3.z));
|
||||
*(acc0+13) = ((*(acc0+13))+(val4.x*val0.w)+(val4.y*val1.w)+(val4.z*val2.w)+(val4.w*val3.w));
|
||||
*(acc0+2) = ((*(acc0+2))+(val5.x*val0.x)+(val5.y*val1.x)+(val5.z*val2.x)+(val5.w*val3.x));
|
||||
*(acc0+6) = ((*(acc0+6))+(val5.x*val0.y)+(val5.y*val1.y)+(val5.z*val2.y)+(val5.w*val3.y));
|
||||
*(acc0+10) = ((*(acc0+10))+(val5.x*val0.z)+(val5.y*val1.z)+(val5.z*val2.z)+(val5.w*val3.z));
|
||||
*(acc0+14) = ((*(acc0+14))+(val5.x*val0.w)+(val5.y*val1.w)+(val5.z*val2.w)+(val5.w*val3.w));
|
||||
*(acc0+3) = ((*(acc0+3))+(val6.x*val0.x)+(val6.y*val1.x)+(val6.z*val2.x)+(val6.w*val3.x));
|
||||
*(acc0+7) = ((*(acc0+7))+(val6.x*val0.y)+(val6.y*val1.y)+(val6.z*val2.y)+(val6.w*val3.y));
|
||||
*(acc0+11) = ((*(acc0+11))+(val6.x*val0.z)+(val6.y*val1.z)+(val6.z*val2.z)+(val6.w*val3.z));
|
||||
*(acc0+15) = ((*(acc0+15))+(val6.x*val0.w)+(val6.y*val1.w)+(val6.z*val2.w)+(val6.w*val3.w));
|
||||
*(acc0+0) = ((*(acc0+0))+(val7.x*val0.x)+(val7.y*val1.x)+(val7.z*val2.x)+(val7.w*val3.x));
|
||||
*(acc0+4) = ((*(acc0+4))+(val7.x*val0.y)+(val7.y*val1.y)+(val7.z*val2.y)+(val7.w*val3.y));
|
||||
*(acc0+8) = ((*(acc0+8))+(val7.x*val0.z)+(val7.y*val1.z)+(val7.z*val2.z)+(val7.w*val3.z));
|
||||
*(acc0+12) = ((*(acc0+12))+(val7.x*val0.w)+(val7.y*val1.w)+(val7.z*val2.w)+(val7.w*val3.w));
|
||||
}
|
||||
int alu39 = (idx0<<2);
|
||||
half4 val8 = (*((__global half4*)((data4_64+alu39))));
|
||||
half4 val9 = (*((__global half4*)((data5_64+alu39))));
|
||||
int alu40 = (idx0+(idx1<<6));
|
||||
int2 cast0 = (int2)((alu40&1023),alu0);
|
||||
float4 val10 = read_imagef(data1_131072, smp, cast0);
|
||||
int2 cast1 = (int2)(((alu40+16)&1023),alu0);
|
||||
float4 val11 = read_imagef(data1_131072, smp, cast1);
|
||||
int2 cast2 = (int2)(((alu40+32)&1023),alu0);
|
||||
float4 val12 = read_imagef(data1_131072, smp, cast2);
|
||||
int2 cast3 = (int2)(((alu40+48)&1023),alu0);
|
||||
float4 val13 = read_imagef(data1_131072, smp, cast3);
|
||||
float cast4 = ((float)(val8.x));
|
||||
float cast5 = ((float)(val9.x));
|
||||
float cast6 = ((float)(val8.y));
|
||||
float cast7 = ((float)(val9.y));
|
||||
float cast8 = ((float)(val8.z));
|
||||
float cast9 = ((float)(val9.z));
|
||||
float cast10 = ((float)(val8.w));
|
||||
float cast11 = ((float)(val9.w));
|
||||
write_imagef(data0_131072, cast0, (float4)((val10.x+(((*(acc0+0))+cast4)*cast5)),(val10.y+(((*(acc0+4))+cast6)*cast7)),(val10.z+(((*(acc0+8))+cast8)*cast9)),(val10.w+(((*(acc0+12))+cast10)*cast11))));
|
||||
write_imagef(data0_131072, cast1, (float4)((val11.x+(((*(acc0+1))+cast4)*cast5)),(val11.y+(((*(acc0+5))+cast6)*cast7)),(val11.z+(((*(acc0+9))+cast8)*cast9)),(val11.w+(((*(acc0+13))+cast10)*cast11))));
|
||||
write_imagef(data0_131072, cast2, (float4)((val12.x+(((*(acc0+2))+cast4)*cast5)),(val12.y+(((*(acc0+6))+cast6)*cast7)),(val12.z+(((*(acc0+10))+cast8)*cast9)),(val12.w+(((*(acc0+14))+cast10)*cast11))));
|
||||
write_imagef(data0_131072, cast3, (float4)((val13.x+(((*(acc0+3))+cast4)*cast5)),(val13.y+(((*(acc0+7))+cast6)*cast7)),(val13.z+(((*(acc0+11))+cast8)*cast9)),(val13.w+(((*(acc0+15))+cast10)*cast11))));
|
||||
}
|
||||
*** QCOM 672 r_512_16_4_4_48_4 arg 6 mem 0.10 GB tm 322.55us/ 77.83ms ( 157 GFLOPS 4|160 GB/s) ['mul', '__add__', 'conv2d']
|
||||
"""
|
||||
|
||||
""" master 99e76f33a0f4ec84c79c1271dbc955fe6b5a7778
|
||||
c0 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((32, 1024, 4)), (), 0)
|
||||
c2 = UOp.range(64, 3, AxisType.LOOP)
|
||||
c4 = UOp.range(64, 4, AxisType.LOOP)
|
||||
c7 = UOp.range(32, 2, AxisType.LOOP)
|
||||
c10 = (((c2*64)+c4)+(c7*4096))
|
||||
c12 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((32, 1024, 4)), (), 1)
|
||||
c14 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((32, 3072, 4)), (), 2)
|
||||
c16 = UOp.range(48, 0, AxisType.REDUCE)
|
||||
c19 = UOp.range(4, 1, AxisType.REDUCE)
|
||||
c28 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((16, 192, 4)), (), 3)
|
||||
c40 = (c14.index(((((c16*4)+c19)+(c2*192))+(c7*12288)))*c28.index(((((c19*4)+(c4%4))+(c16*16))+((c4//4)*768))))
|
||||
c42 = UOp(Ops.DEFINE_GLOBAL, dtypes.half.ptr(64), (), 4)
|
||||
c46 = UOp(Ops.DEFINE_GLOBAL, dtypes.half.ptr(64), (), 5)
|
||||
c50 = (c12.index(c10)+((c40.reduce(c16, c19, arg=Ops.ADD)+c42.index(c4).cast(dtypes.float))*c46.index(c4).cast(dtypes.float)))
|
||||
c52 = c0.index(c10, ptr=True).store(c50).end(c7, c2, c4)
|
||||
ast = c52.sink()
|
||||
more upcast axis : [(3, 320, 0, 4)]
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
__kernel void r_512_16_4_4_48_4(write_only image2d_t data0_131072, read_only image2d_t data1_131072, read_only image2d_t data2_393216, read_only image2d_t data3_12288, __global half* data4_64, __global half* data5_64) {
|
||||
const sampler_t smp = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST;
|
||||
float acc0[16];
|
||||
int idx0 = get_global_id(0); /* 16 */
|
||||
int idx1 = get_global_id(1); /* 512 */
|
||||
*(acc0+0) = 0.0f;
|
||||
*(acc0+1) = 0.0f;
|
||||
*(acc0+2) = 0.0f;
|
||||
*(acc0+3) = 0.0f;
|
||||
*(acc0+4) = 0.0f;
|
||||
*(acc0+5) = 0.0f;
|
||||
*(acc0+6) = 0.0f;
|
||||
*(acc0+7) = 0.0f;
|
||||
*(acc0+8) = 0.0f;
|
||||
*(acc0+9) = 0.0f;
|
||||
*(acc0+10) = 0.0f;
|
||||
*(acc0+11) = 0.0f;
|
||||
*(acc0+12) = 0.0f;
|
||||
*(acc0+13) = 0.0f;
|
||||
*(acc0+14) = 0.0f;
|
||||
*(acc0+15) = 0.0f;
|
||||
int alu16 = (idx0<<2);
|
||||
half4 val0 = (*((__global half4*)((data4_64+alu16))));
|
||||
half4 val1 = (*((__global half4*)((data5_64+alu16))));
|
||||
int alu17 = (idx0+(idx1<<6));
|
||||
int alu18 = (idx1>>4);
|
||||
int2 cast0 = (int2)((alu17&1023),alu18);
|
||||
float4 val2 = read_imagef(data1_131072, smp, cast0);
|
||||
int2 cast1 = (int2)(((alu17+16)&1023),alu18);
|
||||
float4 val3 = read_imagef(data1_131072, smp, cast1);
|
||||
int2 cast2 = (int2)(((alu17+32)&1023),alu18);
|
||||
float4 val4 = read_imagef(data1_131072, smp, cast2);
|
||||
int2 cast3 = (int2)(((alu17+48)&1023),alu18);
|
||||
float4 val5 = read_imagef(data1_131072, smp, cast3);
|
||||
for (int Ridx0 = 0; Ridx0 < 48; Ridx0++) {
|
||||
int alu19 = ((idx1*192)+Ridx0);
|
||||
int alu20 = (alu19+48);
|
||||
int alu21 = (alu19+96);
|
||||
int alu22 = (alu19+144);
|
||||
int alu23 = (Ridx0<<2);
|
||||
float4 val6 = read_imagef(data3_12288, smp, (int2)(alu23,idx0));
|
||||
float4 val7 = read_imagef(data3_12288, smp, (int2)((alu23+1),idx0));
|
||||
float4 val8 = read_imagef(data3_12288, smp, (int2)((alu23+2),idx0));
|
||||
float4 val9 = read_imagef(data3_12288, smp, (int2)((alu23+3),idx0));
|
||||
float4 val10 = read_imagef(data2_393216, smp, (int2)((alu20-(3072*(((alu20>>10)*43)>>7))),alu18));
|
||||
*(acc0+1) = ((*(acc0+1))+(val10.x*val6.x)+(val10.y*val7.x)+(val10.z*val8.x)+(val10.w*val9.x));
|
||||
*(acc0+5) = ((*(acc0+5))+(val10.x*val6.y)+(val10.y*val7.y)+(val10.z*val8.y)+(val10.w*val9.y));
|
||||
*(acc0+9) = ((*(acc0+9))+(val10.x*val6.z)+(val10.y*val7.z)+(val10.z*val8.z)+(val10.w*val9.z));
|
||||
*(acc0+13) = ((*(acc0+13))+(val10.x*val6.w)+(val10.y*val7.w)+(val10.z*val8.w)+(val10.w*val9.w));
|
||||
float4 val11 = read_imagef(data2_393216, smp, (int2)((alu21-(3072*(((alu21>>10)*43)>>7))),alu18));
|
||||
*(acc0+2) = ((*(acc0+2))+(val11.x*val6.x)+(val11.y*val7.x)+(val11.z*val8.x)+(val11.w*val9.x));
|
||||
*(acc0+6) = ((*(acc0+6))+(val11.x*val6.y)+(val11.y*val7.y)+(val11.z*val8.y)+(val11.w*val9.y));
|
||||
*(acc0+10) = ((*(acc0+10))+(val11.x*val6.z)+(val11.y*val7.z)+(val11.z*val8.z)+(val11.w*val9.z));
|
||||
*(acc0+14) = ((*(acc0+14))+(val11.x*val6.w)+(val11.y*val7.w)+(val11.z*val8.w)+(val11.w*val9.w));
|
||||
float4 val12 = read_imagef(data2_393216, smp, (int2)((alu22-(3072*(((alu22>>10)*43)>>7))),alu18));
|
||||
*(acc0+3) = ((*(acc0+3))+(val12.x*val6.x)+(val12.y*val7.x)+(val12.z*val8.x)+(val12.w*val9.x));
|
||||
*(acc0+7) = ((*(acc0+7))+(val12.x*val6.y)+(val12.y*val7.y)+(val12.z*val8.y)+(val12.w*val9.y));
|
||||
*(acc0+11) = ((*(acc0+11))+(val12.x*val6.z)+(val12.y*val7.z)+(val12.z*val8.z)+(val12.w*val9.z));
|
||||
*(acc0+15) = ((*(acc0+15))+(val12.x*val6.w)+(val12.y*val7.w)+(val12.z*val8.w)+(val12.w*val9.w));
|
||||
float4 val13 = read_imagef(data2_393216, smp, (int2)((alu19-(3072*(((alu19>>10)*43)>>7))),alu18));
|
||||
*(acc0+0) = ((*(acc0+0))+(val13.x*val6.x)+(val13.y*val7.x)+(val13.z*val8.x)+(val13.w*val9.x));
|
||||
*(acc0+4) = ((*(acc0+4))+(val13.x*val6.y)+(val13.y*val7.y)+(val13.z*val8.y)+(val13.w*val9.y));
|
||||
*(acc0+8) = ((*(acc0+8))+(val13.x*val6.z)+(val13.y*val7.z)+(val13.z*val8.z)+(val13.w*val9.z));
|
||||
*(acc0+12) = ((*(acc0+12))+(val13.x*val6.w)+(val13.y*val7.w)+(val13.z*val8.w)+(val13.w*val9.w));
|
||||
}
|
||||
float cast4 = ((float)(val0.x));
|
||||
float cast5 = ((float)(val1.x));
|
||||
float cast6 = ((float)(val0.y));
|
||||
float cast7 = ((float)(val1.y));
|
||||
float cast8 = ((float)(val0.z));
|
||||
float cast9 = ((float)(val1.z));
|
||||
float cast10 = ((float)(val0.w));
|
||||
float cast11 = ((float)(val1.w));
|
||||
write_imagef(data0_131072, cast0, (float4)((val2.x+(((*(acc0+0))+cast4)*cast5)),(val2.y+(((*(acc0+4))+cast6)*cast7)),(val2.z+(((*(acc0+8))+cast8)*cast9)),(val2.w+(((*(acc0+12))+cast10)*cast11))));
|
||||
write_imagef(data0_131072, cast1, (float4)((val3.x+(((*(acc0+1))+cast4)*cast5)),(val3.y+(((*(acc0+5))+cast6)*cast7)),(val3.z+(((*(acc0+9))+cast8)*cast9)),(val3.w+(((*(acc0+13))+cast10)*cast11))));
|
||||
write_imagef(data0_131072, cast2, (float4)((val4.x+(((*(acc0+2))+cast4)*cast5)),(val4.y+(((*(acc0+6))+cast6)*cast7)),(val4.z+(((*(acc0+10))+cast8)*cast9)),(val4.w+(((*(acc0+14))+cast10)*cast11))));
|
||||
write_imagef(data0_131072, cast3, (float4)((val5.x+(((*(acc0+3))+cast4)*cast5)),(val5.y+(((*(acc0+7))+cast6)*cast7)),(val5.z+(((*(acc0+11))+cast8)*cast9)),(val5.w+(((*(acc0+15))+cast10)*cast11))));
|
||||
}
|
||||
*** QCOM 672 r_512_16_4_4_48_4 arg 6 mem 0.10 GB tm 527.97us/ 78.94ms ( 96 GFLOPS 3|98 GB/s) ['conv2d', 'mul', '__add__']
|
||||
"""
|
||||
|
||||
c0 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((32, 1024, 4)), (), 0)
|
||||
c2 = UOp.range(64, 3, AxisType.LOOP)
|
||||
c4 = UOp.range(64, 4, AxisType.LOOP)
|
||||
c7 = UOp.range(32, 2, AxisType.LOOP)
|
||||
c10 = (((c2*64)+c4)+(c7*4096))
|
||||
c12 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((32, 1024, 4)), (), 1)
|
||||
c14 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((32, 3072, 4)), (), 2)
|
||||
c16 = UOp.range(48, 0, AxisType.REDUCE)
|
||||
c19 = UOp.range(4, 1, AxisType.REDUCE)
|
||||
c28 = UOp(Ops.DEFINE_GLOBAL, dtypes.imageh((16, 192, 4)), (), 3)
|
||||
c40 = (c14.index(((((c16*4)+c19)+(c2*192))+(c7*12288)))*c28.index(((((c19*4)+(c4%4))+(c16*16))+((c4//4)*768))))
|
||||
c42 = UOp(Ops.DEFINE_GLOBAL, dtypes.half.ptr(64), (), 4)
|
||||
c46 = UOp(Ops.DEFINE_GLOBAL, dtypes.half.ptr(64), (), 5)
|
||||
c50 = (c12.index(c10)+((c40.reduce(c16, c19, arg=Ops.ADD)+c42.index(c4).cast(dtypes.float))*c46.index(c4).cast(dtypes.float)))
|
||||
c52 = c0.index(c10, ptr=True).store(c50).end(c7, c2, c4)
|
||||
ast = c52.sink()
|
||||
|
||||
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)
|
||||
# r_64_8_16_4_4_48_4
|
||||
# NOLOCALS: r_512_16_4_4_48_4
|
||||
ps = ProgramSpec("r_512_16_4_4_48_4", src, Device.DEFAULT, ast, uops)
|
||||
print(ps.src)
|
||||
print(ps.applied_opts)
|
||||
# (Opt(op=OptOps.UNROLL, axis=0, arg=4), Opt(op=OptOps.UPCAST, axis=1, arg=4), Opt(op=OptOps.UPCAST, axis=0, 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")
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
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
@@ -2,8 +2,9 @@ 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 get_rewrites_for_renderer, apply_rewrites, rewrites_for_linearizer
|
||||
from tinygrad.uop.spec import type_verify
|
||||
from tinygrad.codegen import full_rewrite_to_sink
|
||||
from tinygrad.codegen.late.linearizer import linearize
|
||||
from tinygrad.uop.spec import type_verify, program_spec
|
||||
|
||||
if __name__ == "__main__":
|
||||
mdl = ResNet50()
|
||||
@@ -28,18 +29,17 @@ 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(apply_rewrites(u, rewrites))
|
||||
rewritten_uops.append(full_rewrite_to_sink(u, ren=Device.default.renderer))
|
||||
|
||||
if LINEARIZE:
|
||||
with Timing("***** model linearize in "):
|
||||
uops_line = []
|
||||
for u in rewritten_uops:
|
||||
uops_line.append(apply_rewrites(u, rewrites_for_linearizer))
|
||||
uops_line.append(linearize(u))
|
||||
with Timing("***** model verify in "):
|
||||
for u in uops_line: type_verify(u.arg.lst)
|
||||
print(sum(len(u.arg.lst) for u in uops_line))
|
||||
for u in uops_line: type_verify(u, program_spec)
|
||||
print(sum(len(u) for u in uops_line))
|
||||
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
# 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
|
||||
Vendored
+38
@@ -0,0 +1,38 @@
|
||||
from tinygrad import Tensor, nn, Context, GlobalCounters
|
||||
|
||||
if __name__ == "__main__":
|
||||
conv = nn.Conv2d(64, 128, 3)
|
||||
img = Tensor.randn((1,64,128,128))
|
||||
with Context(DEBUG=0, BEAM=0):
|
||||
Tensor.realize(img, conv.weight, conv.bias)
|
||||
|
||||
tst = conv(img).permute(0,2,3,1).realize()
|
||||
print(tst.shape)
|
||||
|
||||
print("NEW")
|
||||
img_perm = img.permute(0,2,3,1).contiguous()
|
||||
print(img_perm.shape)
|
||||
pp = img_perm.permute(0,3,1,2)._pool((3,3)).permute(0,2,3,4,5,1)
|
||||
|
||||
def hwio(pp, conv):
|
||||
pp = pp.unsqueeze(-1)
|
||||
weight = conv.weight.permute(2,3,1,0).contiguous()
|
||||
print(pp.shape, weight.shape, (pp*weight).shape)
|
||||
return (pp * weight).sum([-4,-3, -2])
|
||||
|
||||
def ohwi(pp, conv):
|
||||
pp = pp.unsqueeze(-4)
|
||||
weight = conv.weight.permute(0,2,3,1).contiguous()
|
||||
print(pp.shape, weight.shape, (pp*weight).shape)
|
||||
return (pp * weight).sum([-3,-2,-1])
|
||||
|
||||
for f in [hwio, ohwi]:
|
||||
GlobalCounters.reset()
|
||||
print("\n**************", f.__name__, "**************")
|
||||
out = f(pp, conv)
|
||||
out.realize()
|
||||
print(out.shape)
|
||||
|
||||
with Context(DEBUG=0, BEAM=0):
|
||||
err = (tst-out).square()
|
||||
print(err.mean().item(), err.max().item())
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import subprocess, unittest, os, sys
|
||||
from tinygrad.device import Device
|
||||
|
||||
class TestTinygradSlow(unittest.TestCase):
|
||||
def test_env_overwrite_default_device(self):
|
||||
subprocess.run([f'{Device.DEFAULT}=1 python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
||||
shell=True, check=True)
|
||||
subprocess.run([f'DISK=1 {Device.DEFAULT}=1 python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
||||
shell=True, check=True)
|
||||
subprocess.run([f'NPY=1 {Device.DEFAULT}=1 python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
||||
shell=True, check=True)
|
||||
|
||||
if Device.DEFAULT != "CPU":
|
||||
# setting multiple devices fail
|
||||
with self.assertRaises(subprocess.CalledProcessError):
|
||||
subprocess.run([f'{Device.DEFAULT}=1 CPU=1 python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
||||
shell=True, check=True)
|
||||
|
||||
# setting device via DEV
|
||||
subprocess.run([f'DEV={Device.DEFAULT.capitalize()} python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
||||
shell=True, check=True)
|
||||
subprocess.run([f'DEV={Device.DEFAULT.lower()} python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
||||
shell=True, check=True)
|
||||
subprocess.run([f'DEV={Device.DEFAULT.upper()} python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
||||
shell=True, check=True)
|
||||
|
||||
with self.assertRaises(subprocess.CalledProcessError):
|
||||
subprocess.run([f'DEV={Device.DEFAULT} CPU=1 python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
||||
shell=True, check=True)
|
||||
|
||||
class TestRunAsModule(unittest.TestCase):
|
||||
def test_module_runs(self):
|
||||
p = subprocess.run([sys.executable, "-m", "tinygrad.device"],stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
env={**os.environ, "DEBUG": "1"}, timeout=40,)
|
||||
out = (p.stdout + p.stderr).decode()
|
||||
self.assertEqual(p.returncode, 0, msg=out)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
+2
-2
@@ -58,8 +58,8 @@ class TestExample(unittest.TestCase):
|
||||
print(f"WARNING: {device} test isn't running")
|
||||
return
|
||||
|
||||
x = Tensor.eye(64, device=device, requires_grad=True)
|
||||
y = Tensor.eye(64, device=device, requires_grad=True)
|
||||
x = Tensor.eye(8, device=device, requires_grad=True)
|
||||
y = Tensor.eye(8, device=device, requires_grad=True)
|
||||
z = y.matmul(x).sum()
|
||||
z.backward()
|
||||
|
||||
|
||||
+14
-9
@@ -272,6 +272,10 @@ class TestMainOnnxOps(TestOnnxOps):
|
||||
def test_qlinearmatmul_2D_int8_float32(self): self._run_qlinearmatmul_test(np.int8, np.float32, 2)
|
||||
def test_qlinearmatmul_3D_int8_float32(self): self._run_qlinearmatmul_test(np.int8, np.float32, 3)
|
||||
|
||||
def test_reduce_l2_half(self):
|
||||
inputs = {"data": np.random.randn(1, 1, 32, 32, 32).astype(np.half)*100}
|
||||
self.helper_test_single_op("ReduceL2", inputs, {}, ["reduced"])
|
||||
|
||||
class TestTrainingOnnxOps(TestOnnxOps):
|
||||
# NOTE: ORT doesn't actually support training ops on cpu so we test using functions provided by onnx
|
||||
DOMAIN = AI_ONNX_PREVIEW_TRAINING_DOMAIN
|
||||
@@ -282,11 +286,11 @@ class TestTrainingOnnxOps(TestOnnxOps):
|
||||
tiny_out = runner(inps)
|
||||
onnx_out = onnx_fxn(**inps, **opts)
|
||||
for (nm, t_out), o_out in zip(tiny_out.items(), onnx_out):
|
||||
np.testing.assert_allclose(t_out.numpy(), o_out, rtol=1e-3, atol=1e-6, err_msg=f"{nm} failed")
|
||||
np.testing.assert_allclose(t_out.numpy(), o_out, rtol=1e-6, atol=1e-6, err_msg=f"{nm} failed")
|
||||
|
||||
def test_adagrad_t_greater_than_zero(self):
|
||||
def test_adagrad_t(self):
|
||||
from onnx.backend.test.case.node.adagrad import apply_adagrad
|
||||
for t in [1, 3, 100]:
|
||||
for t in [0, 1, 3, 100]:
|
||||
inputs = {
|
||||
"r": np.array(0.01, dtype=np.float32),
|
||||
"t": np.array(t, dtype=np.int32),
|
||||
@@ -298,10 +302,10 @@ class TestTrainingOnnxOps(TestOnnxOps):
|
||||
outputs = ["X_out", "H_out"]
|
||||
self._validate_training("Adagrad", apply_adagrad, inputs, attributes, outputs)
|
||||
|
||||
def test_momentum_t_greater_than_zero(self):
|
||||
def test_momentum(self):
|
||||
from onnx.backend.test.case.node.momentum import apply_momentum, apply_nesterov
|
||||
for onnx_fxn, mode in ((apply_momentum, "standard"), (apply_nesterov, "nesterov")):
|
||||
for t in [1, 3, 100]:
|
||||
for t in [0, 1, 3, 100]:
|
||||
inputs = {
|
||||
"r": np.array(0.01, dtype=np.float32),
|
||||
"t": np.array(t, dtype=np.int32),
|
||||
@@ -313,9 +317,9 @@ class TestTrainingOnnxOps(TestOnnxOps):
|
||||
outputs = ["X_out", "V_out"]
|
||||
self._validate_training("Momentum", onnx_fxn, inputs, attributes, outputs)
|
||||
|
||||
def test_adam_t_greater_than_zero(self):
|
||||
def test_adam(self):
|
||||
from onnx.backend.test.case.node.adam import apply_adam
|
||||
for t in [1, 3, 100]:
|
||||
for t in [0, 1, 3, 100]:
|
||||
inputs = {
|
||||
"r": np.array(0.01, dtype=np.float32),
|
||||
"t": np.array(t, dtype=np.int32),
|
||||
@@ -422,6 +426,7 @@ class TestContribOnnxOps(TestOnnxOps):
|
||||
outputs = ["C"]
|
||||
self.helper_test_single_op("QLinearAdd", inputs, attributes, outputs, atol=1) # TODO: look into why this is inaccurate
|
||||
|
||||
def test_qlinear_add_round_half_to_even(self):
|
||||
with self.subTest(test_case="round_half_to_even"):
|
||||
inputs = {
|
||||
"A": np.array([1, 1, 1, 1], dtype=np.int8),
|
||||
@@ -435,7 +440,7 @@ class TestContribOnnxOps(TestOnnxOps):
|
||||
}
|
||||
attributes = {}
|
||||
outputs = ["C"]
|
||||
self.helper_test_single_op("QLinearAdd", inputs, attributes, outputs)
|
||||
self.helper_test_single_op("QLinearAdd", inputs, attributes, outputs, atol=1) # TODO: look into why this is inaccurate
|
||||
|
||||
def test_qlinear_mul(self):
|
||||
for dtype, zero_point in [(np.uint8, 128), (np.int8, 0)]:
|
||||
@@ -486,4 +491,4 @@ class TestContribOnnxOps(TestOnnxOps):
|
||||
self.helper_test_single_op("QLinearGlobalAveragePool", inputs, attributes, outputs)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
unittest.main()
|
||||
|
||||
+41
-32
@@ -1,41 +1,50 @@
|
||||
import functools, multiprocessing
|
||||
from transformers import AutoTokenizer
|
||||
from datasets import load_dataset
|
||||
from tinygrad.apps.llm import SimpleTokenizer, gpt2_decode_vocab, get_llama_re
|
||||
from tinygrad.apps.llm import SimpleTokenizer
|
||||
from tinygrad.helpers import tqdm, getenv, partition
|
||||
|
||||
@functools.cache
|
||||
def get_tokenizers():
|
||||
print("getting tokenizers")
|
||||
base_tokenizer = AutoTokenizer.from_pretrained("NousResearch/Meta-Llama-3-8B-Instruct")
|
||||
special_tokens, normal_tokens = partition(((t, tid) for t, tid in base_tokenizer.vocab.items()), lambda e: e[1] in base_tokenizer.all_special_ids)
|
||||
simple_tokenizer = SimpleTokenizer(dict(normal_tokens), dict(special_tokens))
|
||||
return base_tokenizer, simple_tokenizer
|
||||
|
||||
def test_tokenize(samp) -> bool:
|
||||
base_tokenizer, simple_tokenizer = get_tokenizers()
|
||||
idx, txt = samp
|
||||
try: simple_tokens = tuple(simple_tokenizer.encode(txt))
|
||||
except RuntimeError: simple_tokens = ()
|
||||
base_tokens = tuple(base_tokenizer.encode(txt, add_special_tokens=False))
|
||||
if simple_tokens != base_tokens:
|
||||
print(f"tokens mismatch at index: {idx}.\n")
|
||||
color_codes = [91, 92, 94, 93, 95]
|
||||
def color_tokens(tids):
|
||||
return "".join(f"\033[{color_codes[i%len(color_codes)]}m{base_tokenizer.decode([t])}" for i, t in enumerate(tids)) + "\033[0m"
|
||||
print("simple: ", color_tokens(simple_tokens))
|
||||
print("official:", color_tokens(base_tokens) + "\n")
|
||||
return False
|
||||
if simple_tokenizer.decode(simple_tokens) != txt:
|
||||
print(f"decode mismatch at {idx}")
|
||||
return False
|
||||
return True
|
||||
|
||||
# use ALLOW_FAILED=-1 to go over the entire dataset without printing.
|
||||
if __name__ == "__main__":
|
||||
base_tokenizer = AutoTokenizer.from_pretrained("NousResearch/Meta-Llama-3-8B-Instruct")
|
||||
special_tokens, normal_tokens = partition(((t, tid) for t, tid in base_tokenizer.vocab.items()),
|
||||
lambda e: e[1] in base_tokenizer.all_special_ids)
|
||||
inv_vocab = { tid: word for word, tid in base_tokenizer.get_vocab().items() }
|
||||
simple_tokenizer = SimpleTokenizer(get_llama_re(), gpt2_decode_vocab(dict(normal_tokens)), dict(special_tokens))
|
||||
|
||||
color_codes = [ 91, 92, 94, 93, 95 ]
|
||||
def color_tokens(tids):
|
||||
return "".join(f"\033[{color_codes[i%len(color_codes)]}m{base_tokenizer.decode([t])}" for i, t in enumerate(tids)) + "\033[0m"
|
||||
|
||||
print("loading datasets")
|
||||
ds = load_dataset("OpenAssistant/oasst1")
|
||||
loaded_ds = [(idx, el["text"]) for idx, el in enumerate(ds["train"])]
|
||||
print(f"loaded {len(loaded_ds)}")
|
||||
|
||||
allow_failed = getenv("ALLOW_FAILED", 10)
|
||||
|
||||
fail_count, total = 0, 0
|
||||
|
||||
for idx, el in enumerate(tqdm(ds["train"])):
|
||||
total += 1
|
||||
|
||||
try: simple_tokens = tuple(simple_tokenizer.encode(el["text"]))
|
||||
except RuntimeError: simple_tokens = ()
|
||||
base_tokens = tuple(base_tokenizer.encode(el["text"], add_special_tokens=False))
|
||||
|
||||
if simple_tokens != base_tokens:
|
||||
fail_count += 1
|
||||
allow_failed -= 1
|
||||
|
||||
if allow_failed >= 0:
|
||||
print(f"tokens mismatch at index: {idx}.\n")
|
||||
|
||||
print("simple: ", color_tokens(simple_tokens))
|
||||
print("official:", color_tokens(base_tokens) + "\n")
|
||||
|
||||
if allow_failed == 0: break
|
||||
print(f"{fail_count}/{total} samples are inconsistent with the official tokenizer.")
|
||||
with multiprocessing.Pool(16) as pool:
|
||||
for good in tqdm(pool.imap_unordered(test_tokenize, loaded_ds), total=len(loaded_ds)):
|
||||
total += 1
|
||||
if not good:
|
||||
fail_count += 1
|
||||
allow_failed -= 1
|
||||
if allow_failed == 0: break
|
||||
print(f"{fail_count}/{total} samples are inconsistent with the official tokenizer.")
|
||||
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
# ruff: noqa: E501
|
||||
import unittest
|
||||
|
||||
from tinygrad.uop.ops import UOp, Ops
|
||||
from .search import Opt, OptOps
|
||||
from tinygrad.dtype import dtypes
|
||||
from tinygrad.shape.shapetracker import ShapeTracker
|
||||
from tinygrad.shape.view import View
|
||||
from tinygrad.codegen.opt.kernel import Kernel
|
||||
|
||||
from test.external.fuzz_linearizer import run_linearizer
|
||||
|
||||
class TestTrainGpt2Kernel(unittest.TestCase):
|
||||
def test_1(self):
|
||||
# kernel 244
|
||||
ast = UOp(Ops.SINK, dtypes.void, arg=None, src=(
|
||||
UOp(Ops.STORE, dtypes.void, arg=None, src=(
|
||||
UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(206045184), arg=0, src=()),
|
||||
UOp(Ops.VIEW, dtypes.void, arg=ShapeTracker(views=(View(shape=(4, 1024, 50304, 1), strides=(51511296, 50304, 1, 0), offset=0, mask=None, contiguous=True),)), src=()),
|
||||
UOp(Ops.REDUCE_AXIS, dtypes.float, arg=(Ops.ADD, (3,)), src=(
|
||||
UOp(Ops.MUL, dtypes.float, arg=None, src=(
|
||||
UOp(Ops.LOAD, dtypes.float, arg=None, src=(
|
||||
UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(3145728), arg=1, src=()),
|
||||
UOp(Ops.VIEW, dtypes.void, arg=ShapeTracker(views=(View(shape=(4, 1024, 50304, 768), strides=(786432, 768, 0, 1), offset=0, mask=None, contiguous=False),)), src=()),)),
|
||||
UOp(Ops.LOAD, dtypes.float, arg=None, src=(
|
||||
UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(38633472), arg=2, src=()),
|
||||
UOp(Ops.VIEW, dtypes.void, arg=ShapeTracker(views=(View(shape=(4, 1024, 50304, 768), strides=(0, 0, 768, 1), offset=0, mask=None, contiguous=False),)), src=()),)),)),)),)),))
|
||||
|
||||
opts = [Opt(op=OptOps.LOCAL, axis=0, arg=16), Opt(op=OptOps.UPCAST, axis=1, arg=3), Opt(op=OptOps.LOCAL, axis=0, arg=2)]
|
||||
kernel = Kernel(ast)
|
||||
kernel.apply_opts(opts)
|
||||
run_linearizer(kernel)
|
||||
|
||||
def test_2(self):
|
||||
# kernel 254
|
||||
ast = UOp(Ops.SINK, dtypes.void, arg=None, src=(
|
||||
UOp(Ops.STORE, dtypes.void, arg=None, src=(
|
||||
UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(3145728), arg=0, src=()),
|
||||
UOp(Ops.VIEW, dtypes.void, arg=ShapeTracker(views=(View(shape=(4, 1024, 1, 768), strides=(786432, 768, 0, 1), offset=0, mask=None, contiguous=True),)), src=()),
|
||||
UOp(Ops.REDUCE_AXIS, dtypes.float, arg=(Ops.ADD, (2,)), src=(
|
||||
UOp(Ops.MUL, dtypes.float, arg=None, src=(
|
||||
UOp(Ops.LOAD, dtypes.float, arg=None, src=(
|
||||
UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(38633472), arg=1, src=()),
|
||||
UOp(Ops.VIEW, dtypes.void, arg=ShapeTracker(views=(View(shape=(4, 1024, 50304, 768), strides=(0, 0, 768, 1), offset=0, mask=None, contiguous=False),)), src=()),)),
|
||||
UOp(Ops.LOAD, dtypes.float, arg=None, src=(
|
||||
UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(205852672), arg=2, src=()),
|
||||
UOp(Ops.VIEW, dtypes.void, arg=ShapeTracker(views=(View(shape=(4, 1024, 50304, 768), strides=(51463168, 50257, 1, 0), offset=0, mask=((0, 4), (0, 1024), (0, 50257), (0, 768)), contiguous=False),)), src=()),)),)),)),)),))
|
||||
|
||||
opts = [Opt(op=OptOps.LOCAL, axis=1, arg=16), Opt(op=OptOps.LOCAL, axis=0, arg=8), Opt(op=OptOps.UPCAST, axis=2, arg=4), Opt(op=OptOps.UPCAST, axis=1, arg=4), Opt(op=OptOps.LOCAL, axis=1, arg=4), Opt(op=OptOps.UPCAST, axis=3, arg=4)]
|
||||
kernel = Kernel(ast)
|
||||
kernel.apply_opts(opts)
|
||||
run_linearizer(kernel)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Vendored
-2
@@ -1,6 +1,5 @@
|
||||
import gc
|
||||
from tinygrad import Tensor, UOp, Device, nn
|
||||
from tinygrad.shape.shapetracker import views_to_valid_uop
|
||||
from tinygrad.engine.realize import method_cache, get_program
|
||||
from tinygrad.schedule.indexing import apply_movement_op
|
||||
from test.test_tiny import TestTiny
|
||||
@@ -69,7 +68,6 @@ if __name__ == "__main__":
|
||||
|
||||
# these caches will keep uops alive
|
||||
method_cache.clear()
|
||||
views_to_valid_uop.cache_clear()
|
||||
apply_movement_op.cache_clear()
|
||||
Tensor._device_seeds.clear()
|
||||
Tensor._device_rng_counters.clear()
|
||||
|
||||
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
||||
import random
|
||||
import z3
|
||||
from tinygrad import dtypes
|
||||
from tinygrad.uop.spec import uops_to_z3, z3_cdiv
|
||||
from tinygrad.uop.validate import uops_to_z3, z3_cdiv
|
||||
from tinygrad.uop.ops import UOp
|
||||
from tinygrad.uop.decompositions import fast_idiv
|
||||
random.seed(42)
|
||||
|
||||
Vendored
+2
-2
@@ -207,7 +207,7 @@ def fuzz_linearizer(lin: Kernel, rtol=1e-2, atol=1e-2, opts_list=None):
|
||||
if not FUZZ_ALL_ACTIONS and test_lin.applied_opts: print(f"applied opts: {test_lin.applied_opts}")
|
||||
|
||||
# stop if kernel uops repeat
|
||||
try: tuops = tuplize_uops(get_program(test_lin.get_optimized_ast(), test_lin.opts).uops)
|
||||
try: tuops = tuplize_uops(get_program(test_lin.get_optimized_ast(), test_lin.ren).uops)
|
||||
except KeyboardInterrupt: raise
|
||||
except BaseException as e:
|
||||
print(test_lin.ast)
|
||||
@@ -224,7 +224,7 @@ def fuzz_linearizer(lin: Kernel, rtol=1e-2, atol=1e-2, opts_list=None):
|
||||
(msg, rawbufs, var_vals, ground_truth, state1) = compare_linearizer(test_lin, rawbufs, var_vals, ground_truth, rtol=rtol, atol=atol)
|
||||
if state1 is not None and validate_device is not None:
|
||||
validate_lin = test_lin.copy()
|
||||
validate_lin.opts = validate_device.renderer
|
||||
validate_lin.ren = validate_device.renderer
|
||||
if validate_rawbufs is None:
|
||||
validate_rawbufs = [get_fuzz_rawbuf_like(x, copy=True, force_device=validate_device.device) for x in rawbufs]
|
||||
(_msg, _, _, _, state2) = compare_linearizer(validate_lin, validate_rawbufs, var_vals, ground_truth, rtol=rtol, atol=atol)
|
||||
|
||||
Vendored
+1
-1
@@ -2,7 +2,7 @@ import random, operator
|
||||
import z3
|
||||
from tinygrad import Variable, dtypes
|
||||
from tinygrad.uop.ops import UOp
|
||||
from tinygrad.uop.spec import uops_to_z3
|
||||
from tinygrad.uop.validate import uops_to_z3
|
||||
from tinygrad.helpers import DEBUG, Context
|
||||
|
||||
seed = random.randint(0, 100)
|
||||
|
||||
+3
-3
@@ -13,7 +13,7 @@ try:
|
||||
from tinygrad.engine.realize import get_program
|
||||
from tinygrad.uop.ops import UOp, Ops, KernelInfo
|
||||
from tinygrad.codegen.opt import Opt
|
||||
from tinygrad.helpers import VERSION, Context, ContextVar, colored, db_connection, getenv, tqdm
|
||||
from tinygrad.helpers import VERSION, Context, ContextVar, colored, db_connection, getenv, tqdm, BEAM
|
||||
from tinygrad.device import Device
|
||||
except ImportError as e:
|
||||
print(repr(e))
|
||||
@@ -51,8 +51,8 @@ def replay_get_rangeify_map(ret:dict[UOp, UOp], big_sink:UOp) -> tuple[str, str,
|
||||
return to_str(new_sink), to_str(big_sink.substitute(ret)), (big_sink,)
|
||||
|
||||
def replay_get_program(p:ProgramSpec, ast:UOp, renderer:Renderer|None=None, opts:list[Opt]|None=None) -> tuple[str, str, tuple[Any, ...]]:
|
||||
# NOTE: this always uses the opts_to_apply path
|
||||
sink_arg = ast.arg or KernelInfo(opts_to_apply=p.applied_opts)
|
||||
# the ast.arg is non None if we are inside of search.py
|
||||
sink_arg = ast.arg or KernelInfo(opts_to_apply=tuple(opts) if opts is not None else p.applied_opts if BEAM>=1 else None)
|
||||
input_ast = ast.replace(arg=replace(sink_arg, name=p.name))
|
||||
# if no renderer was provided, open the device to get it
|
||||
if renderer is None: renderer = Device[p.device].renderer
|
||||
|
||||
Vendored
+2
-2
@@ -91,11 +91,11 @@ class TestKernelSpeed(unittest.TestCase):
|
||||
|
||||
# theoretical is nv_tflops=165, amd_tflops=123
|
||||
def test_gemm_4096(self): self._test_matmul(4096, nv_tflops=115, amd_tflops=65)
|
||||
def test_gemm_8192(self): self._test_matmul(8192, nv_tflops=125, amd_tflops=60)
|
||||
def test_gemm_8192(self): self._test_matmul(8192, nv_tflops=115, amd_tflops=60)
|
||||
|
||||
# theoretical is nv_gbs=1008, amd_gbs=960
|
||||
def test_gemv_16384_4096(self): self._test_matmul(16384, 4096, 1, nv_gbs=840, amd_gbs=750)
|
||||
def test_gemv_4096_16384(self): self._test_matmul(4096, 16384, 1, nv_gbs=830, amd_gbs=750)
|
||||
def test_gemv_4096_16384(self): self._test_matmul(4096, 16384, 1, nv_gbs=820, amd_gbs=750)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -85,6 +85,8 @@ class AMDDriver(VirtDriver):
|
||||
VirtFile(f'/sys/devices/virtual/kfd/kfd/topology/nodes/{gpu_id}/gpu_id', functools.partial(TextFileDesc, text=f"{gpu_id}")),
|
||||
VirtFile(f'/sys/devices/virtual/kfd/kfd/topology/nodes/{gpu_id}/properties',
|
||||
functools.partial(TextFileDesc, text=gpu_props.format(drm_render_minor=gpu_id))),
|
||||
VirtFile(f'/sys/class/drm/renderD{gpu_id}/device/power_dpm_force_performance_level',
|
||||
functools.partial(TextFileDesc, text='profile_standard\n')),
|
||||
VirtFile(f'/sys/class/drm/renderD{gpu_id}/device/ip_discovery/die/0',
|
||||
functools.partial(DirFileDesc, child_names=[str(am.GC_HWID), str(am.SDMA0_HWID), str(am.NBIF_HWID)])),
|
||||
VirtFile(f'/sys/class/drm/renderD{gpu_id}/device/ip_discovery/die/0/{am.GC_HWID}', functools.partial(DirFileDesc, child_names=['0'])),
|
||||
|
||||
@@ -14,6 +14,9 @@ regSQ_THREAD_TRACE_BUF0_BASE = 0x39e8 + amd_gpu.GC_BASE__INST0_SEG1
|
||||
regSQ_THREAD_TRACE_BUF0_SIZE = 0x39e9 + amd_gpu.GC_BASE__INST0_SEG1
|
||||
regSQ_THREAD_TRACE_WPTR = 0x39ef + amd_gpu.GC_BASE__INST0_SEG1
|
||||
regSQ_THREAD_TRACE_STATUS = 0x39f4 + amd_gpu.GC_BASE__INST0_SEG1
|
||||
regCP_PERFMON_CNTL = 0x3808 + amd_gpu.GC_BASE__INST0_SEG1
|
||||
regCPG_PERFCOUNTER1_LO = 0x3000 + amd_gpu.GC_BASE__INST0_SEG1
|
||||
regGUS_PERFCOUNTER_HI = 0x3643 + amd_gpu.GC_BASE__INST0_SEG1
|
||||
|
||||
class SQTT_EVENTS:
|
||||
THREAD_TRACE_FINISH = 0x00000037
|
||||
@@ -130,7 +133,7 @@ class PM4Executor(AMDQueue):
|
||||
_src_addr_hi = self._next_dword()
|
||||
dst_addr_lo = self._next_dword()
|
||||
dst_addr_hi = self._next_dword()
|
||||
assert copy_data_flags == 0x100204, hex(copy_data_flags) # better fail than silently do the wrong thing
|
||||
assert copy_data_flags in {0x100204, 0x000204}, hex(copy_data_flags) # better fail than silently do the wrong thing
|
||||
to_mv(dst_addr_hi<<32|dst_addr_lo, 4).cast('I')[0] = self.gpu.regs[src_addr_lo]
|
||||
|
||||
def _exec_wait_reg_mem(self, n):
|
||||
@@ -280,6 +283,9 @@ class AMDGPURegisters:
|
||||
self.regs: dict[tuple[int, int], int] = {}
|
||||
def __getitem__(self, addr:int) -> int:
|
||||
if addr == regGRBM_GFX_INDEX: return self.grbm_index
|
||||
if regCPG_PERFCOUNTER1_LO < addr < regGUS_PERFCOUNTER_HI:
|
||||
assert self.regs[(regCP_PERFMON_CNTL, 0)] == 0x401, "read mode should be enabled"
|
||||
return addr << 16 | self.grbm_index
|
||||
return self.regs[(addr, getbits(self.grbm_index, 16, 23))]
|
||||
def __setitem__(self, addr:int, val:int):
|
||||
if addr == regGRBM_GFX_INDEX: self.grbm_index = val
|
||||
|
||||
@@ -112,7 +112,7 @@ class TestRealWorld(unittest.TestCase):
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
|
||||
helper_test("train_mnist", lambda: (Tensor.randn(BS, 1, 28, 28),), train, 0.07, 102)
|
||||
helper_test("train_mnist", lambda: (Tensor.randn(BS, 1, 28, 28),), train, 0.07, 103)
|
||||
|
||||
@unittest.skipIf(CI and Device.DEFAULT in {"CPU", "CL"}, "slow")
|
||||
def test_forward_cifar(self):
|
||||
@@ -176,7 +176,7 @@ class TestRealWorld(unittest.TestCase):
|
||||
for v in data.values(): v.to_(Device.DEFAULT)
|
||||
|
||||
helper_test("train_bert", lambda: (data["input_ids"], data["segment_ids"], data["input_mask"], data["masked_lm_positions"], \
|
||||
data["masked_lm_ids"], data["masked_lm_weights"], data["next_sentence_labels"]), train, 0.31, 358)
|
||||
data["masked_lm_ids"], data["masked_lm_weights"], data["next_sentence_labels"]), train, 0.31, 427)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -2,7 +2,6 @@ import unittest
|
||||
from tinygrad import Device, Tensor, dtypes
|
||||
from tinygrad.uop.ops import UOp, Ops
|
||||
from tinygrad.codegen.opt import Opt, OptOps
|
||||
from tinygrad.shape.shapetracker import ShapeTracker, View
|
||||
from tinygrad.engine.realize import get_program
|
||||
from tinygrad.helpers import AMX
|
||||
|
||||
@@ -149,33 +148,5 @@ class TestFloat4(unittest.TestCase):
|
||||
|
||||
assert TestFloat4.count_float4(uops) == (1, 1)
|
||||
|
||||
@unittest.skip("Ops.VIEW no longer exists")
|
||||
def test_half4_load_unrolled(self):
|
||||
# from llama 7B shard 4 gpus
|
||||
ast = UOp(Ops.SINK, dtypes.void, arg=None, src=(
|
||||
UOp(Ops.STORE, dtypes.void, arg=None, src=(
|
||||
UOp(Ops.VIEW, dtypes.float.ptr(96000), arg=ShapeTracker(views=(View(shape=(1, 3, 32000, 1), strides=(0, 32000, 1, 0), offset=0, mask=None, contiguous=True),)), src=( # noqa: E501
|
||||
UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(96000), arg=0, src=()),)),
|
||||
UOp(Ops.REDUCE_AXIS, dtypes.float, arg=(Ops.ADD, (3,)), 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.VIEW, dtypes.half.ptr(9216), arg=ShapeTracker(views=(View(shape=(1, 3, 32000, 1024), strides=(0, 4096, 0, 1), offset=0, mask=None, contiguous=False),)), src=( # noqa: E501
|
||||
UOp(Ops.DEFINE_GLOBAL, dtypes.half.ptr(9216), arg=1, src=()),)),)),
|
||||
UOp(Ops.LOAD, dtypes.half, arg=None, src=(
|
||||
UOp(Ops.VIEW, dtypes.half.ptr(32768000), arg=ShapeTracker(views=(View(shape=(1, 3, 32000, 1024), strides=(0, 0, 1024, 1), offset=0, mask=None, contiguous=False),)), src=( # noqa: E501
|
||||
UOp(Ops.DEFINE_GLOBAL, dtypes.half.ptr(32768000), arg=2, src=()),)),)),)),)),)),)),))
|
||||
|
||||
# TODO: fix this, expected might change but should be positive
|
||||
for expected, opts in [
|
||||
((7, 0), [Opt(op=OptOps.UPCAST, axis=1, arg=4), Opt(op=OptOps.UPCAST, axis=0, arg=3), Opt(op=OptOps.UNROLL, axis=0, arg=4)]),
|
||||
((5, 0), [Opt(op=OptOps.UPCAST, axis=1, arg=4), Opt(op=OptOps.UNROLL, axis=0, arg=4)]),
|
||||
((2, 0), [Opt(op=OptOps.UNROLL, axis=0, arg=4)]),
|
||||
]:
|
||||
program = get_program(ast, Device[Device.DEFAULT].renderer, opts=opts)
|
||||
|
||||
count = TestFloat4.count_half4(program.uops)
|
||||
assert count == expected, f"{count=}, {expected=}"
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import unittest
|
||||
from tinygrad import Device, Tensor, dtypes
|
||||
from tinygrad.helpers import CI
|
||||
from tinygrad.codegen.opt import Opt, OptOps, KernelOptError
|
||||
|
||||
# TODO: write a clean version of this
|
||||
@@ -177,9 +176,7 @@ class TestKernelOpts(unittest.TestCase):
|
||||
], apply_tc=True, atol=atol, rtol=rtol)
|
||||
|
||||
def test_padto_matmul(self):
|
||||
if (CI and Device.DEFAULT in ["AMD", "NV", "CUDA"]):
|
||||
self.skipTest("super slow on CUDA and AMD because of the big grid dims")
|
||||
N = 17 * 17
|
||||
N = 17
|
||||
Tensor.manual_seed(289)
|
||||
a = Tensor.rand(N, N)
|
||||
b = Tensor.rand(N, N)
|
||||
@@ -213,7 +210,7 @@ class TestKernelOpts(unittest.TestCase):
|
||||
helper_linearizer_opt(a@b, [[Opt(OptOps.UNROLL, 0, 0), Opt(OptOps.PADTO, 2, 8)]])
|
||||
|
||||
def test_padto_sum_ok(self):
|
||||
N = 18 * 18
|
||||
N = 18
|
||||
# NOTE: this setup prevents 17 * 17 contiguous merged into one dimension
|
||||
a = Tensor.rand(N, N).realize().shrink(((0, 17), (0, 17))) * 100
|
||||
b = (Tensor.rand(N, N) < 0.5).realize().shrink(((0, 17), (0, 17)))
|
||||
@@ -244,7 +241,7 @@ class TestKernelOpts(unittest.TestCase):
|
||||
helper_linearizer_opt(a.sum(0).exp(), [[Opt(OptOps.PADTO, 1, 32)],])
|
||||
|
||||
def test_padto_sum_not_ok(self):
|
||||
N = 18 * 18
|
||||
N = 18
|
||||
# NOTE: this setup prevents 17 * 17 contiguous merged into one dimension
|
||||
a = Tensor.rand(N, N).shrink(((0, 17), (0, 17))).exp()
|
||||
# exp is not safe to pad
|
||||
@@ -261,7 +258,7 @@ class TestKernelOpts(unittest.TestCase):
|
||||
helper_linearizer_opt(b.sum(0), [[Opt(OptOps.PADTO, 1, 32)],])
|
||||
|
||||
def test_padto_max(self):
|
||||
N = 18 * 18
|
||||
N = 18
|
||||
# NOTE: this setup prevents 17 * 17 contiguous merged into one axis
|
||||
a = -Tensor.rand(N, N).shrink(((0, 17), (0, 17))) * 100
|
||||
|
||||
@@ -282,7 +279,7 @@ class TestKernelOpts(unittest.TestCase):
|
||||
|
||||
def test_padto_where(self):
|
||||
Tensor.manual_seed(0)
|
||||
N = 17 * 17
|
||||
N = 17
|
||||
a = (Tensor.randn(N, N).realize().max(axis=0, keepdim=True) > 1).where(1, 0)
|
||||
helper_linearizer_opt(a.max(0), [
|
||||
[Opt(OptOps.PADTO, 0, 32)],
|
||||
@@ -291,7 +288,7 @@ class TestKernelOpts(unittest.TestCase):
|
||||
|
||||
def test_padto_where_multioutput(self):
|
||||
Tensor.manual_seed(0)
|
||||
N = 17 * 17
|
||||
N = 17
|
||||
r = Tensor.randn(N, N).realize().max(axis=0, keepdim=True) > 1
|
||||
a0 = r.where(1, 0)
|
||||
a1 = r.where(2, 0)
|
||||
|
||||
@@ -14,6 +14,8 @@ from tinygrad.codegen.opt import Opt, OptOps, KernelOptError
|
||||
# TODO: write a clean version of this
|
||||
from test.test_linearizer import helper_realized_ast, helper_linearizer_opt
|
||||
|
||||
# NOTE: get_program always passes in Device[Device.DEFAULT].renderer explicitly for process_replay!!!
|
||||
|
||||
def helper_tc_ensure_uops_and_opts_count(N: int, M:int, K:int, dtype_in:DType, dtype_out:DType, axis:int=0, tc_select:int=-1, tc_opt:int=0,
|
||||
ensure_triggered:bool=True):
|
||||
a, b = Tensor.rand(M, K, dtype=dtype_in), Tensor.rand(K, N, dtype=dtype_in)
|
||||
@@ -41,7 +43,7 @@ def helper_tc_allclose(N:int, M:int, K:int, dtype_in:DType, dtype_out:DType, axi
|
||||
if dtype_in == dtypes.bfloat16: r = r.float()
|
||||
realized_ast, bufs = helper_realized_ast(r)
|
||||
opts = [Opt(op=OptOps.TC, axis=axis, arg=(tc_select, tc_opt, use_tensor_cores))]
|
||||
prg = CompiledRunner(replace(get_program(realized_ast, opts=opts), device=Device.DEFAULT))
|
||||
prg = CompiledRunner(replace(get_program(realized_ast, Device[Device.DEFAULT].renderer, opts=opts), device=Device.DEFAULT))
|
||||
if use_tensor_cores == 1: assert len([uop for uop in prg.p.uops if uop.op is Ops.WMMA]) > 0, "wmma not triggered"
|
||||
assert len([x for x in prg.p.uops[-1].arg.applied_opts if x.op is OptOps.TC]) == 1, "tensor core opt not included"
|
||||
prg.exec(bufs)
|
||||
@@ -68,7 +70,7 @@ class TestTensorCores(unittest.TestCase):
|
||||
n, m, k = tc.dims[0], tc.dims[1], 2 if AMX else tc.dims[2]
|
||||
a, b = Tensor.rand(m, k, dtype=tc.dtype_in), Tensor.rand(k, n, dtype=tc.dtype_in)
|
||||
r = a.matmul(b, dtype=tc.dtype_out)
|
||||
prg = get_program(r.schedule()[-1].ast, opts=[Opt(op=OptOps.TC, axis=0, arg=(-1, 2, 1))])
|
||||
prg = get_program(r.schedule()[-1].ast, Device[Device.DEFAULT].renderer, opts=[Opt(op=OptOps.TC, axis=0, arg=(-1, 2, 1))])
|
||||
if Device.DEFAULT == "CPU" and CPU_LLVM:
|
||||
assert "0x201000" in prg.src
|
||||
elif Device.DEFAULT == "AMD" and AMD_LLVM:
|
||||
@@ -154,7 +156,7 @@ class TestTensorCores(unittest.TestCase):
|
||||
r = x.matmul(y, dtype=tc.dtype_out)
|
||||
opts = [Opt(OptOps.UNROLL, 0, 4)]
|
||||
ast = helper_linearizer_opt(r, [opts], apply_tc=True, atol=3e-2, rtol=1e-3)
|
||||
for u in get_program(ast, opts=opts).uops:
|
||||
for u in get_program(ast, Device[Device.DEFAULT].renderer, opts=opts).uops:
|
||||
if u.op is Ops.WMMA:
|
||||
assert u.src[-1].src[0].op != Ops.STORE
|
||||
|
||||
@@ -167,7 +169,7 @@ class TestTensorCores(unittest.TestCase):
|
||||
r = x.matmul(y, dtype=tc.dtype_out)
|
||||
opts = [Opt(OptOps.UNROLL, 0, 4)]
|
||||
ast = helper_linearizer_opt(r, [opts], apply_tc=True, atol=3e-2, rtol=1e-3)
|
||||
for u in get_program(ast, opts=opts).uops:
|
||||
for u in get_program(ast, Device[Device.DEFAULT].renderer, opts=opts).uops:
|
||||
if u.op is Ops.WMMA:
|
||||
#assert u.src[-1].dtype == dtypes.float.vec(prod(tc.thread_local_sizes[2]))
|
||||
assert u.src[-1].src[0].op != Ops.STORE
|
||||
@@ -182,7 +184,7 @@ class TestTensorCores(unittest.TestCase):
|
||||
r = x.matmul(y, dtype=tc.dtype_out).relu()
|
||||
opts = [Opt(OptOps.UNROLL, 0, 4)]
|
||||
ast = helper_linearizer_opt(r, [opts], apply_tc=True, atol=3e-2, rtol=1e-3)
|
||||
for u in get_program(ast, opts=opts).uops:
|
||||
for u in get_program(ast, Device[Device.DEFAULT].renderer, opts=opts).uops:
|
||||
if u.op is Ops.WMMA:
|
||||
#assert u.src[-1].dtype == dtypes.float.vec(prod(tc.thread_local_sizes[2]))
|
||||
assert u.src[-1].src[0].op != Ops.STORE
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user