Compare commits

...
176 Commits
Author SHA1 Message Date
geohot fbc7f4c12a only floats 2025-10-25 13:17:33 +08:00
geohot 2fec7ed6df lil symbolic pattern for relu 2025-10-25 13:12:21 +08:00
George HotzandGitHub 6415e3e8a7 use Ops.GROUP instead of Ops.NOOP for merging stores (#12912)
* use Ops.GROUP instead of Ops.NOOP for merging stores

* fs noop
2025-10-25 12:26:12 +08:00
George HotzandGitHub b4f6a2c7a3 add kernel spec (#12911)
* add kernel spec

* fix kernel spec
2025-10-25 11:49:20 +08:00
George HotzandGitHub 8a941d95a4 SPEC=2 is full spec, SPEC=1 is default (#12910)
* SPEC=1 passes all tests

* just use SPEC, not __debug__
2025-10-25 11:10:43 +08:00
wozeparrotandGitHub 456560c1ff stateless tinyfs copyin (#12908) 2025-10-24 19:18:38 -07:00
wozeparrotandGitHub a5b0f57067 clean: cleanup tinyfs copyout (#12907) 2025-10-24 18:32:55 -07:00
chenyuandGitHub 4b7329001d clean up test_avg_pool3d (#12905) 2025-10-24 14:31:36 -04:00
George HotzandGitHub 6b35467f53 stores don't end ranges (#12902)
* early endrange

* bugfixes
2025-10-24 23:05:03 +08:00
nimlgenandGitHub 5b5ba31a86 amd: make sqtt bufs uc (#12898) 2025-10-24 18:55:14 +08:00
Sieds LyklesandGitHub e1f8c82938 Onnx Layer/Group/RMS/Batch-Norm ReduceL2 fp32 intermediates for fp16 (#12109)
* match onnx spec

* use least_upper_dtype

* promote the square

* just cast before the square
2025-10-24 12:26:11 +02:00
George HotzandGitHub 0bde87d8d7 cleanups from flash attention branch (#12897) 2025-10-24 14:14:56 +08:00
wozeparrotandGitHub 9dac505565 variable bs keccak (#10731) 2025-10-23 14:10:21 -07:00
chenyuandGitHub 154b4f9f40 test FUSE_OPTIM=1 test/test_optim.py (#12895) 2025-10-23 15:54:27 -04:00
chenyuandGitHub 6e4ee8deea small heuristic cleanup [pr] (#12892) 2025-10-23 10:50:15 -04:00
nimlgenandGitHub f835566e27 sqtt: correct header (#12891)
* sqtt: correct header

* f
2025-10-23 22:37:17 +08:00
Sieds LyklesandGitHub c1db62ff7c move reduce collapse to rangeify (#12845) 2025-10-23 15:44:17 +02:00
Sieds LyklesandGitHub 04b3e51f1b remove old reduce collapse rule (#12889)
* comment this out

* remove
2025-10-23 13:51:49 +02:00
qazalandGitHub cdfb8e31ae hotfix: correct viz rewrite step counter reset (#12890) 2025-10-23 19:47:16 +08:00
George HotzandGitHub 6df19a4ac6 lil qol improvements to viz (#12887) 2025-10-23 18:41:07 +08:00
George HotzandGitHub ff68a6263b move locals into codegen (dedup works) (#12885)
* move locals into codegen (dedup works)

* move in optimize
2025-10-23 17:07:39 +08:00
George HotzandGitHub ddb53d1d48 PCONTIG=3 both saves ram and flops (#12884)
* PCONTIG=3 both saves ram and flops

* group

* gate locals

* should be correct
2025-10-23 16:37:26 +08:00
qazalandGitHub 2a5c22436e remove outdated docs (#12881) 2025-10-23 12:52:36 +08:00
qazalandGitHub bcc30e5e10 viz: add linearized UOp list view (#12883)
* viz: add linearized UOp list view

* lang
2025-10-23 12:52:14 +08:00
George HotzandGitHub e85cee0aad flip Ops.END srcs (#12882)
* flip Ops.END srcs

* backward

* late end split
2025-10-23 12:47:50 +08:00
George HotzandGitHub 74b4cfe44b Ops.GROUP + range check (#12880)
* simpler

* fix that

* Ops.GROUP + range check

* fix bugs

* fix linter

* fix test
2025-10-23 12:05:21 +08:00
Sieds LyklesandGitHub 914defd55d give endrange priority (#12870)
* uncomment line

* try giving endrange priority
2025-10-23 05:19:13 +02:00
qazalandGitHub 2f95c10702 remu new instructions / use volatile in emulator tests (#12862)
* remu new instructions

* start moving to volatile

* test_simple works

* test_exec_mov works and lid is still here

* test_exec_cmp_vopc

* clang did s_mov_b32 exec_lo, 1

* don't hardcode v1

* support volatile in tests

* hw_test passes

* only the volatile version

* subrev saturating behavior
2025-10-23 11:13:43 +08:00
George HotzandGitHub e718254004 simpler end (#12879)
* simpler

* fix that
2025-10-23 10:35:58 +08:00
wozeparrotandGitHub 6e00dec95d feat: pin openpilot 0.10.1 models (#12878) 2025-10-22 14:57:54 -07:00
wozeparrotandGitHub 3a9aa05359 feat: extra nvcc options (#12876) 2025-10-22 13:21:11 -07:00
chenyuandGitHub f0831c8c30 add 0.10.0 to comma benchmark (#12875)
* add 0.10.0 to comma benchmark

disabled the 0.10.1 ones which are pinned to master. it does not work because benchmark uses the cached old version

* that's pinned
2025-10-22 15:18:21 -04:00
nimlgenandGitHub e7e535cd53 amd: sqtt for gfx9 (#12844)
* amd: start sqtt for gfx9

* writes something, but sometimes zeroes

* HEADER!

* w

* tiny

* mypy
2025-10-23 02:31:07 +08:00
b1tgandGitHub 81108f91ee amd tc: 16x16x32 (#12874)
* amd tc: 16x16x32

* test

* clean, test amd_cdna4
2025-10-22 13:48:01 -04:00
George HotzandGitHub bf173c0a37 we don't support multi end yet (#12869) 2025-10-22 23:43:32 +08:00
nimlgenandGitHub a7bc0104c2 amd: clean up sqtt_stop (#12872) 2025-10-22 22:17:03 +08:00
nimlgenandGitHub b6eb9172ea amd: fix ip offsets (#12867) 2025-10-22 20:50:18 +08:00
geohot 174811fc0f hotfix: slightly looser load spec for AMD bfloat16 2025-10-22 19:55:59 +08:00
George HotzandGitHub 7762b3558b clean up the spec (#12868)
* tighten up the spec

* move validate into a different file

* that moved to validate

* after(barr)
2025-10-22 19:50:42 +08:00
George HotzandGitHub 726988fa4b late ifs try 2 (#12865)
* late ifs try 2

* fix image

* fix that test

* panic

* ptx fixups

* preserve toposort

* those pass locally

* Revert "those pass locally"

This reverts commit 063409f828.

* no ls

* make that explicit
2025-10-22 18:49:27 +08:00
George HotzandGitHub 6abe90fb7c fix linearizer non-determinism (#12866) 2025-10-22 17:51:35 +08:00
qazalandGitHub cebc2b5721 cleanup viz profiler metadata ui (#12860)
* cleanup viz profiler metadata ui

* text

* select over .args

* space
2025-10-22 17:31:12 +08:00
Sieds LyklesandGitHub 8d0256c46b Move gate to load for loaded index (#12861)
* change condition

* change test to better represent how the uop looks irl
2025-10-22 09:53:07 +02:00
chenyuandGitHub 6d86e962c7 update ASSERT_MIN_STEP_TIME (#12857)
0.10.1 driving_policy is good now, still need driving_vision and dmonitoring to be fast
2025-10-21 22:46:07 -04:00
George HotzandGitHub 92778c7a8b rename opts to ren, add store ranges back (#12856)
* rename opts to ren

* fix docs and bring store back
2025-10-22 09:15:38 +08:00
chenyuandGitHub c5cee74706 remove BLOCK_REORDER (#12854)
not used
2025-10-21 19:10:14 -04:00
chenyuandGitHub 0b673eddec simpler newton_schulz transpose (#12853) 2025-10-21 17:21:45 -04:00
b1tgandGitHub 60d7e232f2 cuda fp8 (#12782)
* cuda fp8

* tensor core

* tc test

* clean

* clean pm
2025-10-21 15:05:25 -04:00
Harald SchäferandGitHub 587ccc0e5c compile3: make selftests opt-in (#12851) 2025-10-21 11:32:27 -07:00
wozeparrotandGitHub c3149c618a feat: nvcc compiler (#12852) 2025-10-21 11:31:23 -07:00
chenyuandGitHub 8baa61bd67 use torch 2.9 and its Muon in test (#12773)
* use torch 2.9 and its Muon in test

* relax and disable
2025-10-21 13:35:17 -04:00
chenyuandGitHub f51f9aaa16 muon ns_params -> ns_coefficients (#12850)
match the official torch one
2025-10-21 12:35:52 -04:00
wozeparrotandGitHub 62e7b8b870 feat: just use compile3 (#12849) 2025-10-21 07:56:50 -07:00
nimlgenandGitHub c7336c3e31 amd: sqtt for aql (#12846) 2025-10-21 22:35:01 +08:00
George HotzandGitHub 8960ac54f3 remove RewriteStep premature optimization (#12840)
* remove RewriteStep premature optimization

* fix ebs

* core line count
2025-10-21 21:45:20 +08:00
Sieds LyklesandGitHub 7f798a9630 Cleanup const buffers (#12829)
* split pm_cleanups

* update test_schedule

* shrink when we remove bufferize

* dont do shrink if shape is empty

* update tests

* remove *1 from metadata

* deal with the noop bufferize

* only noop on cvar

* cleanup

* fix if

* rename
2025-10-21 14:53:49 +02:00
nimlgenandGitHub 1ad6598963 amd: trace all instructions (#12831) 2025-10-21 20:52:24 +08:00
sirhcmandGitHub cdc72556a1 no more brew (#12839) 2025-10-21 08:12:46 -04:00
George HotzandGitHub 20a232f1c5 bugfixes from multioutput + PCONTIG=3 for fa bw memory fix (#12837)
* bugfixes from multioutput

* PCONTIG=3 fixes fa memory usage

* that's base
2025-10-21 19:21:02 +08:00
qazalandGitHub 0435d31f1c viz: generic back button functionality (#12838) 2025-10-21 18:52:00 +08:00
George HotzandGitHub 7d9551ce2e move to late/control_flow.py (#12835) 2025-10-21 18:15:06 +08:00
George HotzandGitHub d711a4b933 delete old linearizer (#12834)
* new linearizer with early endrange

* cleanups

* second stage removal

* not store

* do that later

* end cleanup

* fix globals

* end

* multi end

* fix ends earlier

* work

* do_merge_ends

* mini change

* range_gate

* fix cpu

* test fixups

* ranges on index

* not for ptx

* delete linearizer

* remove more junk

* delete that test

* we insert endif

* all ends
2025-10-21 17:52:18 +08:00
qazalandGitHub 40633ab34d list buffer args to kernel in profiler (#12826)
* list buffer args to kernel in profiler

* stable order

* back button works

* deselect also works
2025-10-21 17:51:36 +08:00
George HotzandGitHub c780cd9abb new linearizer with early endrange (#12823)
* new linearizer with early endrange

* cleanups

* second stage removal

* not store

* do that later

* end cleanup

* fix globals

* end

* multi end

* fix ends earlier

* work

* do_merge_ends

* mini change

* range_gate

* fix cpu

* test fixups

* ranges on index

* not for ptx
2025-10-21 17:37:48 +08:00
geohot d59d4cdbe4 lil less is okay 2025-10-21 17:09:44 +08:00
qazalandGitHub 32af1ff84b viz graph drawing small cleanups (#12830)
* viz graph drawing small cleanups

* str literal
2025-10-21 15:51:32 +08:00
Sieds LyklesandGitHub 367fbabc30 remove Ops.SUBSTITUTE (#12827)
* remove Ops.SUBSTITUTE

* remove from viz
2025-10-21 08:19:42 +02:00
qazalandGitHub 57f6b6f229 style view codegen like a link in profiler (#12825) 2025-10-21 12:15:13 +08:00
qazalandGitHub 154cdfe46d viz state cleanups (#12821)
* viz state cleanups

* more generic
2025-10-21 11:44:51 +08:00
George HotzandGitHub a71a41f6d1 rename Ops.ENDRANGE -> Ops.END (#12824) 2025-10-21 11:32:18 +08:00
qazalandGitHub 8521fd5263 viz: hierarchical rewrites (#12805)
* viz: hierarchical rewrites

* count of subrewrites

* arrows

* better keyboard things

* add select and deselect utils

* works

* diff

* event stopPropagation

* work

* don't change the rewrite

* walk tree back
2025-10-21 10:55:41 +08:00
George HotzandGitHub df2f8b9295 use after on locals (#12815)
* use after on locals

* fix estimates

* too much compute

* correct for both ptx and normal

* err, that

* tighter spec

* keep that
2025-10-21 10:29:12 +08:00
sirhcmandGitHub 68c045bf0a NIR: Check for brew packages tinymesa and tinymesa_cpu (#12739)
* brew install tinymesa_cpu

* brew --prefix tinygrad_cpu too

* fix brew paths

* check both brew paths

* better errors

* handle failure
2025-10-21 09:38:43 +08:00
wozeparrotandGitHub 990e8b97ee feat: log openpilot 0.10.1 times (#12816) 2025-10-20 18:30:34 -07:00
George HotzandGitHub 565a7a6218 num_batches_tracked has shape () (#12820) 2025-10-21 09:22:39 +08:00
geohot 25beea5769 hotfix: suppress_finalizing on device __del__ 2025-10-21 09:04:36 +08:00
chenyuandGitHub c7c59e6dd7 unused UPat.or_broadcasted and GroupOp.Block [pr] (#12819) 2025-10-20 12:24:58 -04:00
nimlgenandGitHub e284f6325a llvm: fix compile key for different processors (#12812) 2025-10-20 19:46:48 +08:00
George HotzandGitHub 203a93363c Revert "after clean up of locals (#12813)" (#12814)
This reverts commit 5d0d3d7aac.
2025-10-20 19:33:35 +08:00
George HotzandGitHub 5d0d3d7aac after clean up of locals (#12813) 2025-10-20 19:24:24 +08:00
George HotzandGitHub d1e2c393f8 after in sym, axis_letters in range (#12811)
* after in sym, axis_letters in range

* this is better

* this work?
2025-10-20 18:54:37 +08:00
Sieds LyklesandGitHub a8e4614436 remove REAL_SUBSTITUTE=0 and make it fast (#12809)
* fast REAL_substitute

* remove REAL_SUBSTITUTE=0
2025-10-20 12:44:20 +02:00
Sieds LyklesandGitHub 1e93d19ee3 stable diffusion --fakeweights (#12810) 2025-10-20 12:41:06 +02:00
nimlgenandGitHub b5e36e3c6c nv: check if jitlink is avail (#12808)
* nv: check if jitlink is avail

* why

* fix

* fix
2025-10-20 18:13:16 +08:00
George HotzandGitHub b8a9cce783 replace NOOP with AFTER in reg init (#12804)
* after op

* fix tests

* replace NOOP with AFTER in reg init

* closer

* or_after there

* fix device

* fix all renderers

* better spec for after
2025-10-20 15:34:32 +08:00
qazalandGitHub 12fd2c9c7b explicitly set ignore_indexing for schedule only (#12803) 2025-10-20 13:11:57 +08:00
qazalandGitHub 734c99f722 viz: show indexing rewrites during run_rangeify (#12802)
* viz: show indexing rewrites during run_rangeify

* sinking index
2025-10-20 12:37:03 +08:00
George HotzandGitHub 2e9082e0bc after op (#12801)
* after op

* fix tests
2025-10-20 12:27:56 +08:00
qazalandGitHub 339e6edb7d viz: ui prereqs for hierarchical rewrites (#12799) 2025-10-20 12:15:15 +08:00
wozeparrotandGitHub 357dac8425 feat: allow tuple indexing on uops (#12797) 2025-10-19 19:11:05 -07:00
ba593f7b98 don't render index (#12796)
* don't render index

* update to ignore_indexing

---------

Co-authored-by: qazal <[email protected]>
2025-10-20 09:48:36 +08:00
geohot cad3ada909 tinygpu: build with SIP off works 2025-10-20 09:11:09 +08:00
nimlgenandGitHub 9cd35deae7 amd: fix alignment + pointers for aql over usb (#12793) 2025-10-19 23:55:57 +08:00
nimlgenandGitHub 59784a5972 amd: ensure ts is written (#12794) 2025-10-19 23:55:49 +08:00
chenyuandGitHub 63a23dfe80 test step 0 in TestTrainingOnnxOps (#12790)
and tighter rtol
2025-10-19 09:15:49 -04:00
chenyuandGitHub e8158afd4b update test_qlinear_add_round_half_to_even (#12789)
this does not pass locally
2025-10-19 08:47:27 -04:00
Sieds LyklesandGitHub 1df9c7d7e7 reduce_collapse uses symbolic_flat (#12766)
* sym->symbolic_flat

* cast invalid drops invalid
2025-10-19 12:27:47 +02:00
Sieds LyklesandGitHub fd6ef4801c rangeify uses symbolic_flat (#12786)
* symbolic_simple -> symbolic_flat

* remove expected failures
2025-10-19 12:27:14 +02:00
geohot 89e7f2fa00 mmapeak: gfx1103 support 2025-10-19 16:57:28 +08:00
George HotzandGitHub 617614beb7 add mi350x support to mmapeak (#12784) 2025-10-19 16:11:07 +08:00
qazalandGitHub c8ef4b60f6 viz: share match tracing and TINY device profiler (#12783)
* set a default name for the traces

* set profile_matches + renames

* profile_matches test

* traces 4 steps total
2025-10-19 14:30:07 +08:00
350a4754a9 Update openpilot models (#12780)
* Update openpilot models

* Update slower model

* fix that

---------

Co-authored-by: Bruce Wayne <[email protected]>
2025-10-18 20:32:35 -04:00
chenyuandGitHub 30ff84d050 update test_conv2d_ceildiv_edge_case (#12779) 2025-10-18 16:43:32 -04:00
nimlgenandGitHub 442218266d qcom: fix profiler (#12778)
* qcom: fix profiler

* this way
2025-10-19 01:27:59 +08:00
Harald SchäferandGitHub addc54b96c Simplify openpilot compile3.py (#12748)
* Simpler compile3

* tests

* remove default args

* onnx file is still fp16

* self-test FP16 too

* allow test disable

* absurd tolerance

* Just do latest

* Try simplest

* use later models

* kernel count not relevant if speed is good

* dead improts

* Revert "dead improts"

This reverts commit f68c2cd15d.

* Revert "kernel count not relevant if speed is good"

This reverts commit 0955ca4ee0.

* add back kernal count check on latest model
2025-10-18 10:12:22 -04:00
nimlgenandGitHub 037f6e8fa0 qcom: ioctl for 7xx (#12777) 2025-10-18 20:33:14 +08:00
wozeparrotandGitHub 82f10cfe2e feat: assert on bufferview math (#12772) 2025-10-17 14:20:08 -07:00
chenyuandGitHub fcdf4ab37e remove a contiguous in LARS (#12770) 2025-10-17 17:07:30 -04:00
nimlgenandGitHub 910d698b78 system: cleanup page sizes (#12771)
* system: cleanup page sizes

* ooops
2025-10-18 02:06:42 +08:00
George HotzandGitHub 062a6d68d7 test flash attention backward (#12762)
* test flash attention backward

* TODO: fix pcontig

* end ranges

* render colors

* very big

* multiout at every level

* reset ending ranges

* fix tests

* ugh
2025-10-17 23:15:59 +08:00
George HotzandGitHub 33025b99f6 small changes from fa backward (#12769) 2025-10-17 22:41:18 +08:00
chenyuandGitHub e0d0d4372d fix shape of m and v in onnx Adam with FUSE_OPTIM (#12768)
value is still slightly off but that's not onnx specific
2025-10-17 10:32:41 -04:00
qazalandGitHub bd662bea67 viz: light up program runs (#12764)
* basics work

* fix the color

* light up program events

* swap a with p

* better
2025-10-17 19:33:18 +08:00
George HotzandGitHub c9a3464f76 those decimals never mattered (#12760)
* those decimals never mattered

* this

* improve debug

* real substitute fixes pcontig

* locals are different buffers
2025-10-17 17:16:24 +08:00
qazalandGitHub 0160f034d6 viz: show display name for copy runners (#12761)
* viz: show display name for copy runners

* more u32
2025-10-17 16:59:51 +08:00
qazalandGitHub 253d32b065 viz: add metadata to buffer user list (#12758)
* simple failing test

* encodings

* test passing

* key is deduped
2025-10-17 16:28:54 +08:00
George HotzandGitHub 935a60db72 bring back partial contig and flash attention (#12756)
* bring back partial contig and flash attention

* why not 2

* work

* that

* fix pcontig
2025-10-17 16:19:05 +08:00
Sieds LyklesandGitHub f6bc620169 UOp.prod and UOp.sum methods (#12755) 2025-10-17 10:02:01 +02:00
Sieds LyklesandGitHub d1bb5c0426 slightly flatter symbolic (#12757) 2025-10-17 09:58:45 +02:00
qazalandGitHub 5417e4b099 viz helper cleanups (#12754) 2025-10-17 15:20:24 +08:00
qazalandGitHub 3196a7aae3 viz: pre reqs for lighting up programs (#12753) 2025-10-17 15:03:21 +08:00
qazalandGitHub dfb8f9fc9e viz: annotate buffer mutability in the memory graph (#12750) 2025-10-17 11:53:02 +08:00
Sieds LyklesandGitHub 79c2f1ae26 remove reduce_rangless and replace with reduce_unparented (#12749) 2025-10-17 04:46:05 +02:00
chenyuandGitHub 9561803cb0 fix assert in test_schedule (#12745)
* fix assert in test_schedule

updated kernel counts and some old tests

* fix
2025-10-16 15:39:50 -04:00
chenyuandGitHub 285534ce64 delete DONT_REALIZE_EXPAND and DONT_GROUP_REDUCES (#12744)
does nothing now
2025-10-16 14:11:33 -04:00
chenyuandGitHub 98239f1156 few shapetracker cleanups (#12741) 2025-10-16 12:43:27 -04:00
chenyuandGitHub 53478c741d relax ASSERT_MIN_STEP_TIME for space lab policy (#12742) 2025-10-16 11:40:36 -04:00
geohotstanandGitHub 5d209ee7ec onnx helper intermediate node output validation (#12740)
* start

* update comments

* good

* add comments and better printing

* done
2025-10-16 11:17:47 -04:00
sirhcmandGitHub bce2bc0465 Revert "use RTLD_GLOBAL on macos" (#12738)
This reverts commit 89fe3e574d.
2025-10-16 10:07:21 -04:00
chenyuandGitHub f34f26bca0 fix gpt2 with benchmark (#12736)
`CPU=1 python3 examples/gpt2.py --benchmark 128` works now
2025-10-16 09:55:20 -04:00
Sieds LyklesandGitHub 55db1b0e0e reduce where that is cut from two sides (#12733)
* better rule

* correct pattern

* shorten line
2025-10-16 15:25:15 +02:00
nimlgenandGitHub cf9baeea61 Revert "nv: check if jitlink is avail (#12731)" (#12735)
This reverts commit a069a45d14.
2025-10-16 20:41:49 +08:00
George HotzandGitHub 8be7844b2e use apply uop for assign to fix assign metadata (#12732)
* use apply uop for assign

* fix metadata for assign

* fix backward metadata

* those aren't real tests
2025-10-16 20:34:12 +08:00
nimlgenandGitHub 3aa2277b8f nv: usb4 (#12696)
* hackish

* prog

* match

* l

* simpler

* refactor

* not osx

* apple things

* tiny changes

* fix mask

* match fix

* nn
2025-10-16 20:11:19 +08:00
nimlgenandGitHub a069a45d14 nv: check if jitlink is avail (#12731) 2025-10-16 19:58:50 +08:00
George HotzandGitHub a498ec9c18 cleanup names of postrange + fast FUSE_OPTIM (#12730)
* cleanup names of postrange

* make FUSE_OPTIM not slow

* delete junk in def r
2025-10-16 19:38:31 +08:00
Sieds LyklesandGitHub 8f740e07ff no broadcasting/vectors in reduce collapse (#12729) 2025-10-16 13:22:57 +02:00
qazalandGitHub 533f18b22c viz: add trace data for inflight buffers (#12728)
* viz: add trace data for inflight buffers

* add test_inflight_buf

* temp stores the keys

* update tests / use Tensor.ones
2025-10-16 19:15:03 +08:00
George HotzandGitHub af4479c169 faster stable diffusion load (#12725)
* faster stable diffusion load

* failing tests
2025-10-16 18:31:59 +08:00
nimlgenandGitHub e7c057d5dc system: alloc_sysmem return view (#12724)
* system: alloc_sysmem return view

* e
2025-10-16 17:55:01 +08:00
nimlgenandGitHub b86a33a312 ptx: support bw (#12722) 2025-10-16 15:38:08 +08:00
nimlgenandGitHub b8cd66c7a2 nv: support all gb20x and small bar (#12721) 2025-10-16 15:37:54 +08:00
George HotzandGitHub 1d1e1d9d88 delete the ShapeTracker (#12720)
* delete the ShapeTracker

* fix tests

* fix more

* fix gc test
2025-10-16 15:36:22 +08:00
George HotzandGitHub 592e86f6f5 remove UOp.st (#12716)
* remove UOp.st

* fix tests

* torch backend disable
2025-10-16 14:44:09 +08:00
wozeparrotandGitHub cc2dfe22f5 tinyfs: fetch file utility (#12719) 2025-10-15 23:38:56 -07:00
nimlgenandGitHub 3ed543f956 system: reorder funcs + barrier on macos (#12714) 2025-10-16 14:38:01 +08:00
qazalandGitHub b77bdbbc62 viz: count unpickle in server startup time (#12715)
* viz: count unpickle in server startup time

* type checking
2025-10-16 13:07:46 +08:00
George HotzandGitHub 7c19db00f1 remove st from jit/split_reduceop (#12713)
* remove st from jit

* fix by merging reshapes

* no st usage in rangeify

* hmm, stop early works

* fix speed regressions
2025-10-16 12:50:58 +08:00
qazalandGitHub 069177c1be trace buffer producer and consumers (#12639)
* trace buffer producer and consumers

* work

* generic colored util

* fix batched

* basic clicking works

* generic javascript that works for producer and consumers

* keep focused shape

* idle time

* timings for producer and consumers dedup

* from sd test

* tiny cleanups

* timeline

* work

* up to here

* assert

* list it

* work
2025-10-16 11:11:31 +08:00
George HotzandGitHub 4a151e7533 make xcode signing happy, waiting for entitlement (#12712) 2025-10-16 10:20:34 +08:00
chenyuandGitHub c3278e5622 clean up old tests (#12708) 2025-10-15 17:53:17 -04:00
chenyuandGitHub b8cf35fb77 print macOS version in CI (#12705) 2025-10-15 15:05:33 -04:00
d65bd669f8 update tiny torch backend hook (#12575)
* update the backend to fix torch deprecation warning

* use param_hook to avoid full backward hook needlessly firing on inputs which do not require gradients

* fix indentation

---------

Co-authored-by: chenyu <[email protected]>
2025-10-15 14:02:33 -04:00
nimlgenandGitHub db5ae846aa nv: do not use va_addr for cpu accesses (#12697)
* nv: do not use va_addr for cpu accesses

* mypy
2025-10-15 22:48:12 +08:00
nimlgenandGitHub 3ab23af829 nv: copy prog with copyin (#12701)
* nv: copy prog with copyin

* to bytes

* fix test
2025-10-15 22:48:01 +08:00
nimlgenandGitHub fafbf3daea memory: reserve ptable (#12702) 2025-10-15 22:47:50 +08:00
geohot 85a907605c hotfix: only 20 steps of beautiful_mnist_torch, some CI machines are slow 2025-10-15 22:29:34 +08:00
sirhcmandGitHub e1996d358c use RTLD_GLOBAL on macos (#12699) 2025-10-15 22:24:50 +08:00
chenyuandGitHub 312c622d35 support None in pad_to and shrink_to (#12700) 2025-10-15 09:25:31 -04:00
George HotzandGitHub 612e3d6143 replace mop arg with vectorized index (#12695)
* replace mop arg with vectorized index

* tests passing

* better viz

* no compile4
2025-10-15 20:50:06 +08:00
wozeparrotandGitHub 9ec4c06d7d feat: one request per device (#12698) 2025-10-15 05:22:07 -07:00
Sieds LyklesandGitHub 99aa3bd5f9 reduce collapse reduce only the cut range (#12687) 2025-10-15 13:57:41 +02:00
Sieds LyklesandGitHub 91ac4f1f92 late merging of where and load (#12694) 2025-10-15 13:33:06 +02:00
qazalandGitHub 768dc952de viz ui cleanups / renaming (#12691)
* better viz names

* delete unused

* don't use opacity, it's multiplicative

* keep styles

* scrollbar coloring

* pyrender doesn't work here

beautiful_mnist r_64_16_32_36@lower all index dtypes
2025-10-15 18:40:22 +08:00
chenyuandGitHub 2e50ed0767 increase timeout of resnet cron (#12693)
does not finish in 6 hours now
2025-10-15 06:08:58 -04:00
0aabc1e938 Mesa NIR backend (NAK/LLVMpipe) (#12089)
* nak works

* TestOps::test_add works

* testop has no crashes

* fix bool casts

* fix typo

* add disassemble

* RANGE and locals/regs

* simplify NAKCompiler

* disass cleanup

* cleanup nir codegen

* almost all tests passing

* cleanup notes in extra/

* old notes

* only import nak if NIR=1

* fix new SPECIAL syntax

* fix local/shared memory

* more tests passing

* add DEFINE_VAR support

* llvmpipe kinda works

* diskcache

* some mypy stuff

* lvp passing test_ops.py

* fix imports

* actually fix imports

* remove 'stdout'

* fix llvm import

* fix mypy issues

* nicer errors

* simpler test_dtype skips

* test lvp in CI

* fix github action syntax

* fix more actions typos

* switch to mesa 25.1.0

* diskcache_put

* better generation for lvp nir_options

* b64encode shader blobs

* Revert diskcache changes

This reverts commits 930fa3de8a and 8428c694b3.

* general cleanup

* better error messages

* fix llvm import

* fix windows tests

* link with libm and libgcc_s

* fix some errors

* dont check for 'float4'

* NIR uses pointer arithmetic

* use tinymesa

* bump tinymesa

* bump tinymesa again

* update lvp nir_options

* print nir shader with DEBUG

* simplify LVPCompiler

* more tests

* "gated" STORE

* NAK is cacheable

* more tests

* all tests pass locally for NAK

* test autogen in CI

* autogen deps

* more deps

* fix uop_gc

* fix macos

* mypy

* save 2 lines

* save two more lines

* save 1 line

* save 4 lines

* save more lines

* Revert "save more lines"

This reverts commit dd3a720c5a.

* save more lines

* fix LVP on windows

* refactor

* reorganize some code

* refactor lib_gpu

* move LVP check

* out of order loads

* remove support.mesa

* bump tinymesa version

* simplify LVP jit

* macos

* macos ci

* shell: bash

* testing

* more testing

* compute brew prefix

* stupid typo

* actually fix

* lib

* stdout on macos

* inline gallivm_compile_module

* Revert "inline gallivm_compile_module"

This reverts commit b65983b151.

* elf macos

* semicolon

* inherit from CPULLVMCompiler

* ruff

* disas test

* fix libm linking

* default is fine actually

* arm works

* add elf loader link test

* fix NAK beam

* pylint is too smart by half

---------

Co-authored-by: George Hotz <[email protected]>
Co-authored-by: nimlgen <[email protected]>
2025-10-15 17:38:33 +08:00
qazalandGitHub f0268d13f6 cleanup viz server (#12688) 2025-10-15 15:58:36 +08:00
nimlgenandGitHub aa81bde150 amd: usb4/thunderbolt on macs (#12641)
* tbgpu

* works

* cleaner

* this

* zero size

* h

* fix

* simpler

* prio over usb

* c

* not needed

* linter

* this way

* mappings

* mypy

* mypy

* mypy 2

* nn
2025-10-15 13:02:01 +08:00
George HotzandGitHub 236c4590c3 use margs as intermediate for new style mops (#12686)
* use marg to prepare for movement op change

* clean up forced reshape

* move marg

* more marg

* more
2025-10-15 12:43:00 +08:00
qazalandGitHub 7597e1dcac pyrender in viz (#12682)
* pyrender in viz

* keep profile still print_tree

* keep special in render
2025-10-15 11:53:30 +08:00
qazalandGitHub 60e03eec37 viz: add View Program option (#12683) 2025-10-15 11:37:51 +08:00
George HotzandGitHub a59439d013 use UOp.shape property instead of UOp.st (#12664)
* work on shape property

* reshape causing issues

* more mops

* all mops

* need to cache it

* _shape is like _device

* mostly works

* shape is good

* const uses _shape

* fix tests

* size doesn't use st

* close

* test is broken

* one less st

* hack for 3 op assign

* oops, i didn't mean to change that

* support emulate in the NullDevice

* reproed failure in emulation

* fix wmma
2025-10-15 10:01:34 +08:00
chenyuandGitHub 89df6f611d reenable sdxl mac benchmark (#12680)
also updated faster sd step times
2025-10-14 17:36:17 -04:00
chenyuandGitHub d25ceffe8d update padto opts tests (#12679) 2025-10-14 17:00:42 -04:00
chenyuandGitHub e8380968f2 add venv_sd_mlperf to gitignore (#12676)
training stable diffusion stuff
2025-10-14 12:51:36 -04:00
wozeparrotandGitHub f228c03f9f fetch raid from cloud (#10799)
* feat: initial tinyfs device

* feat: don't allow compute on tinyfs device

* feat: tensor helpers to load and store

* feat: bufferview for tinyfs

* fix: keep copy sizes correct

* fix: recv large

* clean: unneeded

* feat: comment

* clean: unneeded

* clean: remove

* clean: remove

* feat: get request tag

* feat: rename to cloud

* feat: send request_id

* feat: start computing tree

* feat: compute store tree on this side

* feat: jank chunked load

* feat: more debugging

* feat: rename to just load and store

* feat: correct chunk count

* fix: fix load for < 1mb

* feat: comments

* feat: don't truncate on block devices

* feat: better way of testing block device

* feat: don't need to pad that much

* feat: connect to nodes directly on load

* feat: cache connections

* feat: don't hard code chunk size

* feat: close mmap when closing file handle

* feat: don't overwrite stuff on disk if storing from disk

* clean: debug print

* fix: close mmap

* feat: await workers

* feat: fast copy from tinyfs to disk

* feat: don't copy to device on last

* feat: use single socket per device

* feat: raid in tinyfs

* clean: remove import

* clean: type

* feat: maintain single event loop

* feat: lower worker count

* feat: use connection pool

* feat: fetch mapping in its own process

* fix: release lock

* feat: don't fetch if exists

* feat: req id only on stores

* feat: always fetch

* fix: rangeify

* feat: allow specifying raid root

* fix: dealloc buffer

* feat: start support non 0 offset

* clean: use cleaner

* feat: don't pass to threadpool

* clean: typing
2025-10-14 07:53:55 -07:00
173 changed files with 25269 additions and 4621 deletions
+14
View File
@@ -41,6 +41,10 @@ inputs:
description: "Install LLVM?" description: "Install LLVM?"
required: false required: false
default: 'false' default: 'false'
mesa:
description: "Install mesa"
required: false
default: 'false'
runs: runs:
using: "composite" using: "composite"
steps: steps:
@@ -289,3 +293,13 @@ runs:
if: inputs.llvm == 'true' && runner.os == 'macOS' if: inputs.llvm == 'true' && runner.os == 'macOS'
shell: bash shell: bash
run: brew install llvm@20 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
+7 -1
View File
@@ -36,8 +36,9 @@ jobs:
cuda: 'true' cuda: 'true'
webgpu: 'true' webgpu: 'true'
llvm: 'true' llvm: 'true'
pydeps: 'pyyaml mako'
- name: Install autogen support packages - 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 - name: Verify OpenCL autogen
run: | run: |
cp tinygrad/runtime/autogen/opencl.py /tmp/opencl.py.bak 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 cp tinygrad/runtime/autogen/llvm.py /tmp/llvm.py.bak
./autogen_stubs.sh llvm ./autogen_stubs.sh llvm
diff /tmp/llvm.py.bak tinygrad/runtime/autogen/llvm.py 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
+26 -33
View File
@@ -51,17 +51,18 @@ jobs:
rm -f /tmp/staging.db /tmp/staging.db-shm /tmp/staging.db-wal rm -f /tmp/staging.db /tmp/staging.db-shm /tmp/staging.db-wal
- name: reset process replay - name: reset process replay
run: python3.11 test/external/process_replay/reset.py run: python3.11 test/external/process_replay/reset.py
- name: Print macOS version
run: sw_vers
- name: Run Stable Diffusion - 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 - 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 - name: Run Stable Diffusion v2
# TODO: very slow step time # 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 # process replay can't capture this, the graph is too large
# TODO: too slow - name: Run SDXL
# - 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
# 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 - name: Run model inference benchmark
run: METAL=1 python3.11 test/external/external_model_benchmark.py run: METAL=1 python3.11 test/external/external_model_benchmark.py
- name: Test speed vs torch - name: Test speed vs torch
@@ -130,7 +131,7 @@ jobs:
- name: UsbGPU copy speeds - name: UsbGPU copy speeds
run: sudo -E PYTHONPATH=. AMD=1 AMD_IFACE=USB python3.11 test/external/external_test_usb_asm24.py TestDevCopySpeeds run: sudo -E PYTHONPATH=. AMD=1 AMD_IFACE=USB python3.11 test/external/external_test_usb_asm24.py TestDevCopySpeeds
#- name: UsbGPU openpilot test #- 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 - uses: actions/upload-artifact@v4
with: with:
name: Speed (Mac) name: Speed (Mac)
@@ -318,9 +319,9 @@ jobs:
- name: Run 10 CIFAR training steps - 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 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 - 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 - 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 # TODO: too slow
# - name: Run 10 CIFAR training steps w winograd # - 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 # 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
@@ -618,22 +619,24 @@ jobs:
rm -f /tmp/staging.db /tmp/staging.db-shm /tmp/staging.db-wal rm -f /tmp/staging.db /tmp/staging.db-shm /tmp/staging.db-wal
- name: reset process replay - name: reset process replay
run: test/external/process_replay/reset.py 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 - 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 - 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 - 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 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 Space Lab policy + vision - name: openpilot compile3 0.10.0 driving_policy
run: | 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
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 - name: openpilot compile3 0.10.0 dmonitoring
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_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 - name: benchmark MobileNetV2 on DSP
run: | run: |
# generate quantized weights # generate quantized weights
@@ -641,19 +644,9 @@ jobs:
ln -s /data/home/tiny/tinygrad/testsig-*.so . 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 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 # 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 - 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 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: testreddriverbenchmark:
name: AM Benchmark name: AM Benchmark
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
run_script_job: run_script_job:
runs-on: [self-hosted, Linux, tinybox] runs-on: [self-hosted, Linux, tinybox]
if: github.repository_owner == 'tinygrad' if: github.repository_owner == 'tinygrad'
timeout-minutes: 360 timeout-minutes: 720
steps: steps:
- name: Checkout Code - name: Checkout Code
+82 -85
View File
@@ -89,64 +89,65 @@ jobs:
clang -O2 recognize.c -lm -o recognize clang -O2 recognize.c -lm -o recognize
cat test/models/efficientnet/Chicken.jpg | ./recognize | grep cock cat test/models/efficientnet/Chicken.jpg | ./recognize | grep cock
torchbackend: # TODO: fix the torch backend and reenable
name: Torch Backend Tests # torchbackend:
runs-on: ubuntu-latest # name: Torch Backend Tests
timeout-minutes: 15 # runs-on: ubuntu-latest
steps: # timeout-minutes: 15
- name: Checkout Code # steps:
uses: actions/checkout@v4 # - name: Checkout Code
- name: Setup Environment # uses: actions/checkout@v4
uses: ./.github/actions/setup-tinygrad # - name: Setup Environment
with: # uses: ./.github/actions/setup-tinygrad
key: torch-backend-pillow-torchvision-et-pt # with:
deps: testing_minimal # key: torch-backend-pillow-torchvision-et-pt
pydeps: "pillow torchvision expecttest" # deps: testing_minimal
llvm: 'true' # pydeps: "pillow torchvision expecttest"
- name: Install ninja # llvm: 'true'
run: | # - name: Install ninja
sudo apt update || true # run: |
sudo apt install -y --no-install-recommends ninja-build # sudo apt update || true
- name: Lint with ruff # sudo apt install -y --no-install-recommends ninja-build
run: | # - name: Lint with ruff
pip3 install --upgrade --force-reinstall ruff==0.11.0 # run: |
python3 -m ruff check extra/torch_backend/backend.py # pip3 install --upgrade --force-reinstall ruff==0.11.0
- name: Test one op # python3 -m ruff check extra/torch_backend/backend.py
run: FORWARD_ONLY=1 TINY_BACKEND=1 python3 test/test_ops.py TestOps.test_add # - name: Test one op
- name: Test ResNet-18 # run: FORWARD_ONLY=1 TINY_BACKEND=1 python3 test/test_ops.py TestOps.test_add
run: DEBUG=2 python3 extra/torch_backend/example.py # - name: Test ResNet-18
- name: My (custom) tests # run: DEBUG=2 python3 extra/torch_backend/example.py
run: python3 extra/torch_backend/test.py # - name: My (custom) tests
- name: Test one op in torch tests # run: python3 extra/torch_backend/test.py
run: DEBUG=2 python3 extra/torch_backend/torch_tests.py TestTinyBackendPRIVATEUSE1.test_unary_log_tiny_float32 # - name: Test one op in torch tests
- name: Test Ops with TINY_BACKEND # run: DEBUG=2 python3 extra/torch_backend/torch_tests.py TestTinyBackendPRIVATEUSE1.test_unary_log_tiny_float32
run: CPU=1 CPU_LLVM=1 LLVMOPT=0 TINY_BACKEND=1 python3 -m pytest -n auto test/test_ops.py --durations=20 # - name: Test Ops with TINY_BACKEND
- name: Test in-place operations on views # run: CPU=1 CPU_LLVM=1 LLVMOPT=0 TINY_BACKEND=1 python3 -m pytest -n auto test/test_ops.py --durations=20
run: TORCH_DEBUG=1 python3 extra/torch_backend/test_inplace.py # - name: Test in-place operations on views
- name: Test multi-gpu # run: TORCH_DEBUG=1 python3 extra/torch_backend/test_inplace.py
run: CPU=1 CPU_LLVM=1 GPUS=4 TORCH_DEBUG=1 python3 extra/torch_backend/test_multigpu.py # - name: Test multi-gpu
# run: CPU=1 CPU_LLVM=1 GPUS=4 TORCH_DEBUG=1 python3 extra/torch_backend/test_multigpu.py
torchbackendmore: # torchbackendmore:
name: Torch Backend Tests More # name: Torch Backend Tests More
runs-on: ubuntu-latest # runs-on: ubuntu-latest
timeout-minutes: 15 # timeout-minutes: 15
steps: # steps:
- name: Checkout Code # - name: Checkout Code
uses: actions/checkout@v4 # uses: actions/checkout@v4
- name: Setup Environment # - name: Setup Environment
uses: ./.github/actions/setup-tinygrad # uses: ./.github/actions/setup-tinygrad
with: # with:
key: torch-backend-pillow-torchvision-et-pt # key: torch-backend-pillow-torchvision-et-pt
deps: testing_minimal # deps: testing_minimal
llvm: 'true' # llvm: 'true'
- name: Install ninja # - name: Install ninja
run: | # run: |
sudo apt update || true # sudo apt update || true
sudo apt install -y --no-install-recommends ninja-build # sudo apt install -y --no-install-recommends ninja-build
- name: Test beautiful_mnist in torch with TINY_BACKEND # - 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 # 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) # - name: Test some torch tests (expect failure)
run: python3 -m pytest extra/torch_backend/torch_tests.py -v --tb=no || true # run: python3 -m pytest extra/torch_backend/torch_tests.py -v --tb=no || true
bepython: bepython:
name: Python Backend 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 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 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_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 - 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 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 - name: Test emulated AMX tensor cores
@@ -263,8 +264,8 @@ jobs:
run: python -c "from tinygrad import Device; assert Device.DEFAULT == 'CPU', Device.DEFAULT" run: python -c "from tinygrad import Device; assert Device.DEFAULT == 'CPU', Device.DEFAULT"
- name: Run unit tests - name: Run unit tests
run: CPU=1 python -m pytest -n=auto test/unit/ --durations=20 run: CPU=1 python -m pytest -n=auto test/unit/ --durations=20
- name: Check SPEC=1 - name: Check SPEC=2
run: SPEC=1 python3 test/test_tiny.py run: SPEC=2 python3 test/test_tiny.py
- name: Run targetted tests on NULL backend - 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 run: NULL=1 python3 -m unittest test.test_multitensor.TestMultiTensor.test_data_parallel_resnet_train_step test/device/test_null.py
# TODO: too slow # TODO: too slow
@@ -272,6 +273,8 @@ jobs:
# run: NULL=1 DEBUG=1 python3 examples/sdxl.py --seed 0 --noshow --timing --fakeweights # run: NULL=1 DEBUG=1 python3 examples/sdxl.py --seed 0 --noshow --timing --fakeweights
- name: Run Clip tests for SD MLPerf on NULL backend - 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 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 # TODO: support fake weights
#- name: Run LLaMA 7B on 4 fake devices #- 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 # run: NULL=1 python3 examples/llama.py --gen 1 --size 7B --shard 4 --prompt "Hello." --count 3 --temperature 0 --timing
@@ -307,10 +310,6 @@ jobs:
run: python test/external/fuzz_symbolic.py run: python test/external/fuzz_symbolic.py
- name: Fuzz Test fast idiv - name: Fuzz Test fast idiv
run: python test/external/fuzz_fast_idiv.py 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 - name: Fuzz Test shape ops
run: python test/external/fuzz_shape_ops.py run: python test/external/fuzz_shape_ops.py
@@ -352,7 +351,7 @@ jobs:
- name: Run Kernel Count Test - name: Run Kernel Count Test
run: CL=1 python -m pytest -n=auto test/external/external_test_opt.py run: CL=1 python -m pytest -n=auto test/external/external_test_opt.py
- name: Run fused optimizer tests - 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 - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
@@ -375,17 +374,13 @@ jobs:
llvm: 'true' llvm: 'true'
- name: Test openpilot model kernel count and gate usage - name: Test openpilot model kernel count and gate usage
run: | 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 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 alt model correctness (float32) - name: Test openpilot CL compile fp16
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 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 fastvits model correctness (float32) - name: Test openpilot CL compile fp32 (test correctness)
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 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 simple_plan vision model correctness (float32) - name: Test openpilot LLVM compile fp16
# 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 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: Test openpilot LLVM compile
run: CPU=1 CPU_LLVM=1 LLVMOPT=1 JIT=2 BEAM=0 IMAGE=0 python examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/9118973ed03c1ae1d40cf69a29507ec2cc78efd7/selfdrive/modeld/models/supercombo.onnx
- name: Test openpilot compile4
run: NOLOCALS=1 CL=1 IMAGE=2 FLOAT16=1 DEBUG=2 python3 examples/openpilot/compile4.py
- name: Run process replay tests - name: Run process replay tests
uses: ./.github/actions/process-replay uses: ./.github/actions/process-replay
@@ -527,11 +522,11 @@ jobs:
pydeps: "pillow" pydeps: "pillow"
llvm: "true" llvm: "true"
- name: Test LLVM=1 DEVECTORIZE=0 - 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 - name: Test LLVM=1 DEVECTORIZE=0 for model
run: CPU=1 CPU_LLVM=1 DEVECTORIZE=0 python3 test/models/test_efficientnet.py run: CPU=1 CPU_LLVM=1 DEVECTORIZE=0 python3 test/models/test_efficientnet.py
- name: Test CPU=1 DEVECTORIZE=0 - 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: testdsp:
name: Linux (DSP) name: Linux (DSP)
@@ -675,7 +670,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
backend: [llvm, cpu, opencl] backend: [llvm, cpu, opencl, lvp]
name: Linux (${{ matrix.backend }}) name: Linux (${{ matrix.backend }})
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
@@ -689,9 +684,10 @@ jobs:
key: ${{ matrix.backend }}-minimal key: ${{ matrix.backend }}-minimal
deps: testing_minimal deps: testing_minimal
opencl: ${{ matrix.backend == 'opencl' && 'true' }} 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 - 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 - name: Check Device.DEFAULT and print some source
run: | run: |
python3 -c "from tinygrad import Device; assert Device.DEFAULT in ['CPU','CL'], Device.DEFAULT" python3 -c "from tinygrad import Device; assert Device.DEFAULT in ['CPU','CL'], Device.DEFAULT"
@@ -893,7 +889,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
backend: [metal, llvm, cpu] backend: [metal, llvm, cpu, lvp]
name: MacOS (${{ matrix.backend }}) name: MacOS (${{ matrix.backend }})
runs-on: macos-15 runs-on: macos-15
timeout-minutes: 20 timeout-minutes: 20
@@ -906,12 +902,13 @@ jobs:
key: macos-${{ matrix.backend }}-minimal key: macos-${{ matrix.backend }}-minimal
deps: testing_minimal deps: testing_minimal
pydeps: "capstone" pydeps: "capstone"
llvm: ${{ matrix.backend == 'llvm' && 'true' }} llvm: ${{ matrix.backend == 'llvm' || matrix.backend == 'lvp' }}
mesa: ${{ matrix.backend == 'lvp' && 'true' }}
- name: Set env - 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 - name: Check Device.DEFAULT and print some source
run: | 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 DEBUG=4 python3 test/test_tiny.py TestTiny.test_plus
- name: Run pytest (${{ matrix.backend }}) - name: Run pytest (${{ matrix.backend }})
run: python3 -m pytest -n=auto test/ --ignore=test/models --ignore=test/unit --durations=20 run: python3 -m pytest -n=auto test/ --ignore=test/models --ignore=test/unit --durations=20
+1
View File
@@ -38,6 +38,7 @@ extra/huggingface_onnx/models/*
extra/huggingface_onnx/*.yaml extra/huggingface_onnx/*.yaml
extra/weights extra/weights
venv venv
venv_sd_mlperf
examples/**/net.*[js,json] examples/**/net.*[js,json]
examples/**/*.safetensors examples/**/*.safetensors
node_modules node_modules
+76 -1
View File
@@ -461,6 +461,80 @@ generate_libusb() {
python3 -c "import tinygrad.runtime.autogen.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 "s/\(struct_nir_builder._pack_\) = 1/\1 = 0/" $BASE/mesa.py
python3 -c "import tinygrad.runtime.autogen.mesa"
}
if [ "$1" == "opencl" ]; then generate_opencl if [ "$1" == "opencl" ]; then generate_opencl
elif [ "$1" == "hip" ]; then generate_hip elif [ "$1" == "hip" ]; then generate_hip
elif [ "$1" == "comgr" ]; then generate_comgr elif [ "$1" == "comgr" ]; then generate_comgr
@@ -484,6 +558,7 @@ elif [ "$1" == "pci" ]; then generate_pci
elif [ "$1" == "vfio" ]; then generate_vfio elif [ "$1" == "vfio" ]; then generate_vfio
elif [ "$1" == "webgpu" ]; then generate_webgpu elif [ "$1" == "webgpu" ]; then generate_webgpu
elif [ "$1" == "libusb" ]; then generate_libusb 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>" else echo "usage: $0 <type>"
fi fi
-109
View File
@@ -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
View File
@@ -232,7 +232,7 @@ if __name__ == "__main__":
gpt2 = GPT2.build_gguf(args.model_size) if args.model_size.startswith("gpt2_gguf_") else GPT2.build(args.model_size) gpt2 = GPT2.build_gguf(args.model_size) if args.model_size.startswith("gpt2_gguf_") else GPT2.build(args.model_size)
if args.benchmark != -1: 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: else:
texts = gpt2.generate(args.prompt, args.count, args.temperature, timing=args.timing, batch_size=args.batch_size) texts = gpt2.generate(args.prompt, args.count, args.temperature, timing=args.timing, batch_size=args.batch_size)
if not args.noshow: if not args.noshow:
+44 -63
View File
@@ -1,9 +1,5 @@
import os, sys, pickle, time, re import os, sys, pickle, time, re
import numpy as np 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 import fetch, Tensor, TinyJit, Context, GlobalCounters, Device, dtypes
from tinygrad.helpers import DEBUG, getenv 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_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()} 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 # 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()} input_types = {k:(dtypes.float32 if v is dtypes.float16 else v) for k,v in input_types.items()}
Tensor.manual_seed(100) 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())} inputs = {k:Tensor(Tensor.randn(*shp, dtype=input_types[k]).mul(8).realize().numpy(), device='NPY') for k,shp in sorted(input_shapes.items())}
new_inputs_numpy = {k:v.numpy() for k,v in new_inputs.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") print("created tensors")
run_onnx_jit = TinyJit(lambda **kwargs: run_onnx_jit = TinyJit(lambda **kwargs:
@@ -33,8 +32,6 @@ def compile(onnx_file):
for i in range(3): for i in range(3):
GlobalCounters.reset() GlobalCounters.reset()
print(f"run {i}") 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)): with Context(DEBUG=max(DEBUG.value, 2 if i == 2 else 1)):
ret = run_onnx_jit(**inputs).numpy() ret = run_onnx_jit(**inputs).numpy()
# copy i == 1 so use of JITBEAM is okay # 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"mdl size is {mdl_sz/1e6:.2f}M")
print(f"pkl size is {pkl_sz/1e6:.2f}M") print(f"pkl size is {pkl_sz/1e6:.2f}M")
print("**** compile done ****") print("**** compile done ****")
return test_val return inputs, test_val
def test_vs_compile(run, new_inputs, test_val=None): def test_vs_compile(run, inputs, test_val=None):
new_inputs_numpy = {k:v.numpy() for k,v in new_inputs.items()}
# create fake "from_blob" tensors for the inputs, and wrapped NPY tensors for the numpy inputs (these have the same underlying memory)
inputs = {**{k:v for k,v in new_inputs.items() if 'img' in k},
**{k:Tensor(v, device="NPY").realize() for k,v in new_inputs_numpy.items() if 'img' not in k}}
# run 20 times # run 20 times
step_times = [] step_times = []
@@ -93,68 +85,57 @@ def test_vs_compile(run, new_inputs, test_val=None):
min_time = min(step_times) 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" 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) if test_val is not None: np.testing.assert_equal(test_val, val)
print("**** test done ****") print("**** test done ****")
# test that changing the numpy changes the model outputs # test that changing the numpy changes the model outputs
if any([x.device == 'NPY' for x in inputs.values()]): inputs_2x = {k: Tensor(v.numpy()*2, device=v.device) for k,v in inputs.items()}
for v in new_inputs_numpy.values(): v *= 2 out = run(**inputs_2x)
out = run(**inputs) changed_val = out.numpy()
changed_val = out.numpy() np.testing.assert_raises(AssertionError, np.testing.assert_array_equal, val, changed_val)
np.testing.assert_raises(AssertionError, np.testing.assert_array_equal, val, changed_val)
return val return val
def test_vs_onnx(new_inputs, test_val, onnx_file, ort=False): def test_vs_onnx(new_inputs, test_val, onnx_file, tol):
new_inputs_numpy = {k:v.numpy() for k,v in new_inputs.items()} import onnxruntime as ort
onnx_inputs = {k:v.numpy() for k,v in new_inputs.items()}
onnx_model = onnx.load(onnx_file) onnx_model = onnx.load(onnx_file)
timings = [] ORT_TO_NP_DTYPES: dict[str, np.dtype] = {
if ort: 'tensor(float)': np.dtype('float32'),
# test with onnxruntime 'tensor(float16)': np.dtype('float16'),
import onnxruntime as ort 'tensor(uint8)': np.dtype('uint8'),
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()
if test_val is not None: timings = []
np.testing.assert_allclose(new_torch_out.reshape(test_val.shape), test_val, atol=1e-4, rtol=1e-2) onnx_session = ort.InferenceSession(onnx_file)
print("test vs onnx passed") 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 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__": if __name__ == "__main__":
onnx_file = fetch(OPENPILOT_MODEL) 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) with open(OUTPUT, "rb") as f: pickle_loaded = pickle.load(f)
# same randomness as compile test_vs_compile(pickle_loaded, inputs, outputs)
Tensor.manual_seed(100) if getenv("SELFTEST"):
new_inputs = {nm:Tensor.randn(*st.shape, dtype=dtype).mul(8).realize() for nm, (st, _, dtype, _) in test_vs_onnx(inputs, outputs, onnx_file, 1e-4)
sorted(zip(pickle_loaded.captured.expected_names, pickle_loaded.captured.expected_st_vars_dtype_device))}
test_val = test_vs_compile(pickle_loaded, new_inputs, test_val)
if getenv("BENCHMARK"):
for be in ["torch", "ort"]:
try:
timings = test_vs_onnx(new_inputs, None, onnx_file, be=="ort")
print(f"timing {be}: {min(timings)*1000:.2f} ms")
except Exception as e:
print(f"{be} fail with {e}")
if not getenv("FLOAT16"): test_vs_onnx(new_inputs, test_val, onnx_file, getenv("ORT"))
if getenv("BENCHMARK_LOG", ""):
bench(pickle_loaded, inputs)
-47
View File
@@ -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
View File
@@ -99,6 +99,7 @@ if __name__ == "__main__":
parser.add_argument('--timing', action='store_true', help="Print timing per step") 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('--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('--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() args = parser.parse_args()
N = 1 N = 1
@@ -112,19 +113,22 @@ if __name__ == "__main__":
model = StableDiffusionV2(**params) 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): 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: if args.fp16:
for k,v in get_state_dict(model).items(): for k,v in get_state_dict(model).items():
if k.startswith("model"): 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) } c = { "crossattn": model.cond_stage_model(args.prompt) }
uc = { "crossattn": model.cond_stage_model("") } uc = { "crossattn": model.cond_stage_model("") }
+4 -1
View File
@@ -263,13 +263,16 @@ if __name__ == "__main__":
parser.add_argument('--timing', action='store_true', help="Print timing per step") 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('--seed', type=int, help="Set the random latent seed")
parser.add_argument('--guidance', type=float, default=7.5, help="Prompt strength") 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() args = parser.parse_args()
model = StableDiffusion() model = StableDiffusion()
# load in weights # load in weights
with WallTimeEvent(BenchEvent.LOAD_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: if args.fp16:
for k,v in get_state_dict(model).items(): for k,v in get_state_dict(model).items():
+2 -2
View File
@@ -19,8 +19,8 @@ from tinygrad.helpers import fetch, getenv
# QUANT=1 python3 examples/test_onnx_imagenet.py # 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 # 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 # 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 # VIZ=1 python3 examples/benchmark_onnx.py /tmp/model.quant.onnx
def imagenet_dataloader(cnt=0): def imagenet_dataloader(cnt=0):
input_mean = Tensor([0.485, 0.456, 0.406]).reshape(1, -1, 1, 1) input_mean = Tensor([0.485, 0.456, 0.406]).reshape(1, -1, 1, 1)
+1 -2
View File
@@ -328,8 +328,7 @@ if __name__ == "__main__":
elif HL == 1: hprg = hl_spec_kernel3() elif HL == 1: hprg = hl_spec_kernel3()
else: hprg = hand_spec_kernel3() else: hprg = hand_spec_kernel3()
if HL == 3: if HL == 3:
with Context(BLOCK_REORDER=0): prg = get_program(hprg, Device.default.renderer)
prg = get_program(hprg, Device.default.renderer)
else: else:
prg = get_program(hprg, Device.default.renderer) prg = get_program(hprg, Device.default.renderer)
print(prg.src) print(prg.src)
+8 -4
View File
@@ -5,8 +5,10 @@ from tinygrad.dtype import _to_np_dtype
from tinygrad.codegen.opt import OptOps from tinygrad.codegen.opt import OptOps
from tinygrad.engine.realize import lower_schedule from tinygrad.engine.realize import lower_schedule
dtype_in = dtypes.half if getenv("HALF") else dtypes.bfloat16 if getenv("BFLOAT16") else dtypes.float dtype_in = (dtypes.half if getenv("HALF") else dtypes.bfloat16 if getenv("BFLOAT16") else
acc_dtype = dtypes.half if getenv("ACC_HALF") else dtypes.bfloat16 if getenv("ACC_BFLOAT16") else None 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("INT"): dtype_in, acc_dtype = dtypes.int8, dtypes.int32
if getenv("UINT"): dtype_in, acc_dtype = dtypes.uint8, 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) M = getenv("M", N)
K = getenv("K", N) K = getenv("K", N)
CNT = getenv("CNT", 10) 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_LOW = getenv("INT_LOW", 0)
INT_HIGH = getenv("INT_HIGH", 10) INT_HIGH = getenv("INT_HIGH", 10)
+23
View File
@@ -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
+38 -22
View File
@@ -7,31 +7,34 @@ import os
NUM_WORKGROUPS = 96 NUM_WORKGROUPS = 96
WAVE_SIZE = 32 WAVE_SIZE = 32
NUM_WAVES = 2 NUM_WAVES = 2
FLOPS_PER_MATMUL = 16*16*16*2 FLOPS_PER_MATMUL = 16*16*16*2
INTERNAL_LOOP = 1_000_000 INTERNAL_LOOP = 1_000_00
INSTRUCTIONS_PER_LOOP = 1_000 INSTRUCTIONS_PER_LOOP = 200
DIRECTIVE = ".amdhsa_wavefront_size32 1"
assemblyTemplate = (pathlib.Path(__file__).parent / "template.s").read_text() assemblyTemplate = (pathlib.Path(__file__).parent / "template.s").read_text()
def launchBenchmark(instruction, vgprIndices, dense = True): def launchBenchmark(instruction, vgprIndices, dense=True, accum=False, extra=""):
if dense: 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], instructions = "{} v[0:{}], v[{}:{}], v[{}:{}], 1\n".format(instruction, vgprIndices[0],
vgprIndices[1], vgprIndices[2], vgprIndices[1], vgprIndices[2],
vgprIndices[1], vgprIndices[2]) * INSTRUCTIONS_PER_LOOP vgprIndices[1], vgprIndices[2])
else: else:
instructions = "{} v[0:{}], v[{}:{}], v[{}:{}], v{}\n".format(instruction, vgprIndices[0], instructions = "{} v[0:{}], v[{}:{}], v[{}:{}], v{}\n".format(instruction, vgprIndices[0],
vgprIndices[1], vgprIndices[2], vgprIndices[1], vgprIndices[2],
vgprIndices[3], vgprIndices[4], vgprIndices[3], vgprIndices[4],
vgprIndices[5]) * INSTRUCTIONS_PER_LOOP vgprIndices[5])
src = assemblyTemplate.replace("INSTRUCTION", instructions) src = assemblyTemplate.replace("INTERNAL_LOOP", str(INTERNAL_LOOP)).replace("INSTRUCTION", instructions*INSTRUCTIONS_PER_LOOP)
src = src.replace("DIRECTIVE", DIRECTIVE)
lib = COMPILER.compile(src) lib = COMPILER.compile(src)
fxn = AMDProgram(DEV, "matmul", lib) fxn = AMDProgram(DEV, "matmul", lib)
start = time.perf_counter() elapsed = fxn(global_size=(NUM_WORKGROUPS,1,1), local_size=(WAVE_SIZE*NUM_WAVES,1,1), wait=True)
fxn(global_size=(NUM_WORKGROUPS,1,1), local_size=(WAVE_SIZE*NUM_WAVES,1,1), wait=True) #For some reason the returned time is very small after the first kernel execution
end = time.perf_counter()
elapsed = end-start
FLOPs = FLOPS_PER_MATMUL * NUM_WAVES * NUM_WORKGROUPS * INTERNAL_LOOP * INSTRUCTIONS_PER_LOOP 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__": if __name__=="__main__":
DEVICENUM = os.getenv("DEVICENUM", "0") DEVICENUM = os.getenv("DEVICENUM", "0")
@@ -40,18 +43,17 @@ if __name__=="__main__":
except: except:
raise RuntimeError("Error while initiating AMD device") raise RuntimeError("Error while initiating AMD device")
if (ARCH := DEV.arch) not in ['gfx1100', 'gfx1201']: COMPILER = HIPCompiler(DEV.arch)
raise RuntimeError("only gfx1100 and gfx1201 supported") if DEV.arch in {'gfx1100', 'gfx1103'}:
COMPILER = HIPCompiler(ARCH) if DEV.arch == 'gfx1103':
NUM_WORKGROUPS = 8
if ARCH == 'gfx1100':
launchBenchmark("v_wmma_bf16_16x16x16_bf16", (7,8,15)) launchBenchmark("v_wmma_bf16_16x16x16_bf16", (7,8,15))
launchBenchmark("v_wmma_f16_16x16x16_f16", (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_bf16", (7,8,15))
launchBenchmark("v_wmma_f32_16x16x16_f16", (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_iu4", (7,8,9))
launchBenchmark("v_wmma_i32_16x16x16_iu8", (7,8,11)) launchBenchmark("v_wmma_i32_16x16x16_iu8", (7,8,11))
if ARCH == 'gfx1201': elif DEV.arch == 'gfx1201':
NUM_WORKGROUPS = 64 NUM_WORKGROUPS = 64
launchBenchmark("v_wmma_bf16_16x16x16_bf16", (3,4,7)) launchBenchmark("v_wmma_bf16_16x16x16_bf16", (3,4,7))
launchBenchmark("v_wmma_f16_16x16x16_f16", (3,4,7)) launchBenchmark("v_wmma_f16_16x16x16_f16", (3,4,7))
@@ -76,4 +78,18 @@ if __name__=="__main__":
launchBenchmark("v_swmmac_f32_16x16x32_bf8_fp8", (7,8,9,10,13,14), False) 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) launchBenchmark("v_swmmac_f32_16x16x32_bf8_bf8", (7,8,9,10,13,14), False)
FLOPS_PER_MATMUL = 16*16*64*2 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_bf16", (3,0,1), accum=True)
FLOPS_PER_MATMUL = 16*16*32*2
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.")
+4 -5
View File
@@ -1,9 +1,9 @@
.text .text
.globl matmul .globl matmul
.p2align 8 .p2align 8
.type matmul,@function .type matmul,@function
matmul: matmul:
s_mov_b32 s1, 1000000 s_mov_b32 s1, INTERNAL_LOOP
s_mov_b32 s2, 0 s_mov_b32 s2, 0
inner_loop: inner_loop:
INSTRUCTION INSTRUCTION
@@ -17,7 +17,7 @@ matmul:
.amdhsa_kernel matmul .amdhsa_kernel matmul
.amdhsa_next_free_vgpr .amdgcn.next_free_vgpr .amdhsa_next_free_vgpr .amdgcn.next_free_vgpr
.amdhsa_next_free_sgpr .amdgcn.next_free_sgpr .amdhsa_next_free_sgpr .amdgcn.next_free_sgpr
.amdhsa_wavefront_size32 1 DIRECTIVE
.end_amdhsa_kernel .end_amdhsa_kernel
.amdgpu_metadata .amdgpu_metadata
@@ -28,7 +28,7 @@ amdhsa.version:
amdhsa.kernels: amdhsa.kernels:
- .name: matmul - .name: matmul
.symbol: matmul.kd .symbol: matmul.kd
.kernarg_segment_size: 0 .kernarg_segment_size: 0
.group_segment_fixed_size: 0 .group_segment_fixed_size: 0
.private_segment_fixed_size: 0 .private_segment_fixed_size: 0
.kernarg_segment_align: 4 .kernarg_segment_align: 4
@@ -36,6 +36,5 @@ amdhsa.kernels:
.sgpr_count: 8 .sgpr_count: 8
.vgpr_count: 32 .vgpr_count: 32
.max_flat_workgroup_size: 1024 .max_flat_workgroup_size: 1024
.args:
... ...
.end_amdgpu_metadata .end_amdgpu_metadata
+107 -4
View File
@@ -3,8 +3,21 @@ from tinygrad.tensor import _to_np_dtype
from tinygrad.nn.onnx import OnnxRunner, OnnxValue from tinygrad.nn.onnx import OnnxRunner, OnnxValue
import numpy as np import numpy as np
import onnxruntime as ort import onnxruntime as ort
ort_options = ort.SessionOptions()
ort_options.log_severity_level = 3
def get_example_inputs(graph_inputs:dict[str, OnnxValue], config={}): 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]): def _get_shape(onnx_shape: tuple[str|int]):
shape = [] shape = []
for onnx_dim in onnx_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}) ret.update({name:value})
return ret 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) run_onnx = OnnxRunner(onnx_file)
ort_options = ort.SessionOptions()
ort_options.log_severity_level = 3
ort_sess = ort.InferenceSession(onnx_file, ort_options, ["CPUExecutionProvider"]) 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()} np_inputs = {k:v.numpy() if isinstance(v, Tensor) else v for k,v in inputs.items()}
out_names = list(run_onnx.graph_outputs) 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)) ort_out = dict(zip(out_names, out_values))
tinygrad_out = run_onnx(inputs) 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() assert tinygrad_out.keys() == ort_out.keys()
for k in tinygrad_out.keys(): for k in tinygrad_out.keys():
tiny_v, onnx_v = tinygrad_out[k], ort_out[k] 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=}" 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
+293 -224
View File
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<database xmlns="http://nouveau.freedesktop.org/" <database xmlns="http://nouveau.freedesktop.org/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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"/> <import file="adreno/adreno_common.xml"/>
<enum name="vgt_event_type" varset="chip"> <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="HLSQ_FLUSH" value="7" variants="A3XX-A4XX"/>
<value name="VIZQUERY_END" value="8" variants="A2XX"/> <value name="VIZQUERY_END" value="8" variants="A2XX"/>
<value name="SC_WAIT_WC" value="9" variants="A2XX"/> <value name="SC_WAIT_WC" value="9" variants="A2XX"/>
<value name="WRITE_PRIMITIVE_COUNTS" value="9" variants="A6XX"/> <value name="WRITE_PRIMITIVE_COUNTS" value="9" variants="A6XX-"/>
<value name="START_PRIMITIVE_CTRS" value="11" variants="A6XX"/> <value name="START_PRIMITIVE_CTRS" value="11" variants="A6XX-"/>
<value name="STOP_PRIMITIVE_CTRS" value="12" 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+ --> <!-- 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_PIX_CNT" value="13" variants="A2XX-A4XX"/>
<value name="RST_VTX_CNT" value="14" 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="STAT_EVENT" value="16" variants="A2XX-A4XX"/>
<value name="CACHE_FLUSH_AND_INV_TS_EVENT" value="20" variants="A2XX-A4XX"/> <value name="CACHE_FLUSH_AND_INV_TS_EVENT" value="20" variants="A2XX-A4XX"/>
<doc> <doc>
If A6XX_RB_SAMPLE_COUNT_CONTROL.copy is true, writes OQ Z passed If A6XX_RB_SAMPLE_COUNTER_CNTL.copy is true, writes OQ Z passed
sample counts to RB_SAMPLE_COUNT_ADDR. This writes to main sample counts to RB_SAMPLE_COUNTER_BASE. This writes to main
memory, skipping UCHE. memory, skipping UCHE.
</doc> </doc>
<value name="ZPASS_DONE" value="21"/> <value name="ZPASS_DONE" value="21"/>
@@ -96,6 +97,13 @@ xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.xsd">
</doc> </doc>
<value name="BLIT" value="30" variants="A5XX-"/> <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> <doc>
Clears based on GRAS_LRZ_CNTL configuration, could clear Clears based on GRAS_LRZ_CNTL configuration, could clear
fast-clear buffer or LRZ direction. 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="LRZ_FLUSH" value="38" variants="A5XX-"/>
<value name="BLIT_OP_FILL_2D" value="39" 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="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="BLIT_OP_SCALE_2D" value="42" variants="A5XX-"/>
<value name="CONTEXT_DONE_2D" value="43" variants="A5XX-"/> <value name="CONTEXT_DONE_2D" value="43" variants="A5XX-"/>
<value name="UNK_2C" value="44" variants="A5XX-"/> <value name="VSC_BINNING_START" value="44" variants="A5XX-"/>
<value name="UNK_2D" value="45" variants="A5XX-"/> <value name="VSC_BINNING_END" value="45" variants="A5XX-"/>
<!-- a6xx events --> <!-- a6xx events -->
<doc> <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 --> <!-- note, some of these are the same as a6xx, just named differently -->
<doc> Doesn't seem to do anything </doc> <doc> Doesn't seem to do anything </doc>
<value name="DUMMY_EVENT" value="1" variants="A7XX"/> <value name="DUMMY_EVENT" value="1" variants="A7XX-"/>
<value name="CCU_INVALIDATE_DEPTH" value="24" variants="A7XX"/> <value name="CCU_INVALIDATE_DEPTH" value="24" variants="A7XX-"/>
<value name="CCU_INVALIDATE_COLOR" value="25" variants="A7XX"/> <value name="CCU_INVALIDATE_COLOR" value="25" variants="A7XX-"/>
<value name="CCU_RESOLVE_CLEAN" value="26" variants="A7XX"/> <value name="CCU_RESOLVE_CLEAN" value="26" variants="A7XX-"/>
<value name="CCU_FLUSH_DEPTH" value="28" variants="A7XX"/> <value name="CCU_FLUSH_DEPTH" value="28" variants="A7XX-"/>
<value name="CCU_FLUSH_COLOR" value="29" variants="A7XX"/> <value name="CCU_FLUSH_COLOR" value="29" variants="A7XX-"/>
<value name="CCU_RESOLVE" value="30" variants="A7XX"/> <value name="CCU_RESOLVE" value="30" variants="A7XX-"/>
<value name="CCU_END_RESOLVE_GROUP" value="31" 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_DEPTH" value="32" variants="A7XX-"/>
<value name="CCU_CLEAN_COLOR" value="33" variants="A7XX"/> <value name="CCU_CLEAN_COLOR" value="33" variants="A7XX-"/>
<value name="CACHE_RESET" value="48" variants="A7XX"/> <value name="CACHE_RESET" value="48" variants="A7XX-"/>
<value name="CACHE_CLEAN" value="49" variants="A7XX"/> <value name="CACHE_CLEAN" value="49" variants="A7XX-"/>
<!-- TODO: deal with name conflicts with other gens --> <!-- TODO: deal with name conflicts with other gens -->
<value name="CACHE_FLUSH7" value="50" variants="A7XX"/> <value name="CACHE_FLUSH7" value="50" variants="A7XX-"/>
<value name="CACHE_INVALIDATE7" value="51" variants="A7XX"/> <value name="CACHE_INVALIDATE7" value="51" variants="A7XX-"/>
<value name="DEPTH_BUFFER_FLIP" value="0x3d" variants="A8XX-"/>
</enum> </enum>
<enum name="pc_di_primtype"> <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> <doc>fetch state sub-blocks and initiate shader code DMAs</doc>
<value name="CP_SET_STATE" value="0x25"/> <value name="CP_SET_STATE" value="0x25"/>
<doc>load constant into chip and to memory</doc> <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> <doc>load sequencer instruction memory (pointer-based)</doc>
<value name="CP_IM_LOAD" value="0x27"/> <value name="CP_IM_LOAD" value="0x27"/>
<doc>load sequencer instruction memory (code embedded in packet)</doc> <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_STATE" value="0x30" variants="A3XX"/>
<value name="CP_LOAD_STATE4" value="0x30" variants="A4XX-A5XX"/> <value name="CP_LOAD_STATE4" value="0x30" variants="A4XX-A5XX"/>
<doc>Conditionally load a IB based on a flag, prefetch enabled</doc> <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> <doc>Conditionally load a IB based on a flag, prefetch disabled</doc>
<value name="CP_COND_INDIRECT_BUFFER_PFD" value="0x32" variants="A3XX"/> <value name="CP_COND_INDIRECT_BUFFER_PFD" value="0x32" variants="A3XX"/>
<doc>Load a buffer with pre-fetch enabled</doc> <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 Seems to set the mode flags which control which CP_SET_DRAW_STATE
packets are executed, based on their ENABLE_MASK values packets are executed, based on their ENABLE_MASK values
CP_SET_MODE w/ payload of 0x1 seems to cause CP_SET_DRAW_STATE CP_SET_MODE w/ payload of 0x1 seems to cause CP_SET_DRAW_STATE
packets w/ ENABLE_MASK & 0x6 to execute immediately 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_GEOM" value="0x32" variants="A6XX-"/>
<value name="CP_LOAD_STATE6_FRAG" value="0x34" 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 shader stages, for 3d pipeline CP_LOAD_STATE6 is used. But for
compute shaders, CP_LOAD_STATE6_FRAG is used. Possibly they are compute shaders, CP_LOAD_STATE6_FRAG is used. Possibly they are
interchangable. interchangable.
@@ -566,20 +576,21 @@ xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.xsd">
<value name="IN_PREEMPT" value="0x0f" variants="A6XX-"/> <value name="IN_PREEMPT" value="0x0f" variants="A6XX-"/>
<!-- TODO do these exist on A5xx? --> <!-- 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_MEM" value="0x74" variants="A6XX-"/>
<value name="CP_REG_TO_MEM_OFFSET_REG" value="0x72" 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_MEM_GTE" value="0x14" variants="A6XX"/>
<value name="CP_WAIT_TWO_REGS" value="0x70" variants="A6XX"/> <value name="CP_WAIT_TWO_REGS" value="0x70" variants="A6XX"/>
<value name="CP_MEMCPY" value="0x75" variants="A6XX-"/> <value name="CP_MEMCPY" value="0x75" variants="A6XX-"/>
<value name="CP_SET_BIN_DATA5_OFFSET" value="0x2e" 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> <doc>
Write CP_CONTEXT_SWITCH_*_INFO from CP to the following dwords, Write CP_CONTEXT_SWITCH_*_INFO from CP to the following dwords,
and forcibly switch to the indicated context. and forcibly switch to the indicated context.
</doc> </doc>
<value name="CP_CONTEXT_SWITCH" value="0x54" variants="A6XX"/> <value name="CP_CONTEXT_SWITCH" value="0x54" variants="A6XX"/>
<!-- Note, kgsl calls this CP_SET_AMBLE: --> <value name="CP_SET_AMBLE" value="0x55" variants="A6XX-"/>
<value name="CP_SET_CTXSWITCH_IB" value="0x55" variants="A6XX-"/>
<!-- <!--
Seems to always have the payload: 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-"/> <value name="CP_BV_BR_COUNT_OPS" value="0x1b" variants="A7XX-"/>
<doc> Clears, adds to local, or adds to global timestamp </doc> <doc> Clears, adds to local, or adds to global timestamp </doc>
<value name="CP_MODIFY_TIMESTAMP" value="0x1c" variants="A7XX-"/> <value name="CP_MODIFY_TIMESTAMP" value="0x1c" variants="A7XX-"/>
<!-- similar to CP_CONTEXT_REG_BUNCH, but discards first two dwords?? --> <value name="CP_NON_CONTEXT_REG_BUNCH" value="0x5d" variants="A7XX-"/>
<value name="CP_CONTEXT_REG_BUNCH2" value="0x5d" variants="A7XX-"/>
<doc> <doc>
Write to a scratch memory that is read by CP_REG_TEST with 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. 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> <doc>Reset various on-chip state used for synchronization</doc>
<value name="CP_RESET_CONTEXT_STATE" value="0x1f" variants="A7XX-"/> <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> </enum>
@@ -790,14 +805,14 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<value name="SB6_GS_SHADER" value="0xb"/> <value name="SB6_GS_SHADER" value="0xb"/>
<value name="SB6_FS_SHADER" value="0xc"/> <value name="SB6_FS_SHADER" value="0xc"/>
<value name="SB6_CS_SHADER" value="0xd"/> <value name="SB6_CS_SHADER" value="0xd"/>
<value name="SB6_IBO" value="0xe"/> <value name="SB6_UAV" value="0xe"/>
<value name="SB6_CS_IBO" value="0xf"/> <value name="SB6_CS_UAV" value="0xf"/>
</enum> </enum>
<enum name="a6xx_state_type"> <enum name="a6xx_state_type">
<value name="ST6_SHADER" value="0"/> <value name="ST6_SHADER" value="0"/>
<value name="ST6_CONSTANTS" value="1"/> <value name="ST6_CONSTANTS" value="1"/>
<value name="ST6_UBO" value="2"/> <value name="ST6_UBO" value="2"/>
<value name="ST6_IBO" value="3"/> <value name="ST6_UAV" value="3"/>
</enum> </enum>
<enum name="a6xx_state_src"> <enum name="a6xx_state_src">
<value name="SS6_DIRECT" value="0"/> <value name="SS6_DIRECT" value="0"/>
@@ -903,12 +918,6 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
</reg32> </reg32>
<stripe varset="chip" variants="A5XX-"> <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"/> <reg64 offset="4" name="INDX_BASE" type="address"/>
<reg32 offset="6" name="6"> <reg32 offset="6" name="6">
<!-- max # of elements in index buffer --> <!-- 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="BINNING" pos="20" varset="chip" variants="A6XX-" type="boolean"/>
<bitfield name="GMEM" pos="21" 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="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> </reg32>
<reg64 offset="1" name="ADDR" type="address"/>
<reg32 offset="1" name="1"> <reg32 offset="1" name="1">
<bitfield name="ADDR_LO" low="0" high="31" type="hex"/> <bitfield name="ADDR_LO" low="0" high="31" type="hex"/>
</reg32> </reg32>
@@ -1119,39 +1130,63 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
</reg32> </reg32>
</domain> </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"> <domain name="CP_SET_BIN_DATA5" width="32">
<reg32 offset="0" name="0"> <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: --> <!-- equiv to PC_VSTREAM_CONTROL.SIZE on a3xx/a4xx: -->
<bitfield name="VSC_SIZE" low="16" high="21" type="uint"/> <bitfield name="VSC_SIZE" low="16" high="21" type="uint"/>
<!-- equiv to PC_VSTREAM_CONTROL.N on a3xx/a4xx: --> <!-- equiv to PC_VSTREAM_CONTROL.N on a3xx/a4xx: -->
<bitfield name="VSC_N" low="22" high="26" type="uint"/> <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> </reg32>
<!-- BIN_DATA_ADDR -> VSC_PIPE[p].DATA_ADDRESS --> <stripe varset="a7xx_abs_mask_mode" variants="NO_ABS_MASK">
<reg32 offset="1" name="1"> <!-- BIN_DATA_ADDR -> VSC_PIPE[p].DATA_ADDRESS -->
<bitfield name="BIN_DATA_ADDR_LO" low="0" high="31" type="hex"/> <reg64 offset="1" name="BIN_DATA_ADDR" type="address"/>
</reg32> <!-- BIN_SIZE_ADDRESS -> VSC_SIZE_ADDRESS + (p * 4)-->
<reg32 offset="2" name="2"> <reg64 offset="3" name="BIN_SIZE_ADDR" type="address"/>
<bitfield name="BIN_DATA_ADDR_HI" low="0" high="31" type="hex"/> <!-- new on a6xx, where BIN_DATA_ADDR is the DRAW_STRM: -->
</reg32> <reg64 offset="5" name="BIN_PRIM_STRM" type="address"/>
<!-- BIN_SIZE_ADDRESS -> VSC_SIZE_ADDRESS + (p * 4)--> <!--
<reg32 offset="3" name="3"> a7xx adds a few more addresses to the end of the pkt
<bitfield name="BIN_SIZE_ADDRESS_LO" low="0" high="31"/> -->
</reg32> <reg64 offset="7" name="7"/>
<reg32 offset="4" name="4"> <reg64 offset="9" name="9"/>
<bitfield name="BIN_SIZE_ADDRESS_HI" low="0" high="31"/> </stripe>
</reg32> <stripe varset="a7xx_abs_mask_mode" variants="ABS_MASK">
<!-- new on a6xx, where BIN_DATA_ADDR is the DRAW_STRM: --> <reg32 offset="1" name="ABS_MASK"/>
<reg32 offset="5" name="5"> <!-- BIN_DATA_ADDR -> VSC_PIPE[p].DATA_ADDRESS -->
<bitfield name="BIN_PRIM_STRM_LO" low="0" high="31"/> <reg64 offset="2" name="BIN_DATA_ADDR" type="address"/>
</reg32> <!-- BIN_SIZE_ADDRESS -> VSC_SIZE_ADDRESS + (p * 4)-->
<reg32 offset="6" name="6"> <reg64 offset="4" name="BIN_SIZE_ADDR" type="address"/>
<bitfield name="BIN_PRIM_STRM_HI" low="0" high="31"/> <!-- new on a6xx, where BIN_DATA_ADDR is the DRAW_STRM: -->
</reg32> <reg64 offset="6" name="BIN_PRIM_STRM" type="address"/>
<!-- <!--
a7xx adds a few more addresses to the end of the pkt a7xx adds a few more addresses to the end of the pkt
--> -->
<reg64 offset="7" name="7"/> <reg64 offset="8" name="8"/>
<reg64 offset="9" name="9"/> <reg64 offset="10" name="10"/>
</stripe>
</domain> </domain>
<domain name="CP_SET_BIN_DATA5_OFFSET" width="32"> <domain name="CP_SET_BIN_DATA5_OFFSET" width="32">
@@ -1162,23 +1197,42 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
stream is recorded. stream is recorded.
</doc> </doc>
<reg32 offset="0" name="0"> <reg32 offset="0" name="0">
<bitfield name="VSC_MASK" low="0" high="15" type="hex"/>
<!-- equiv to PC_VSTREAM_CONTROL.SIZE on a3xx/a4xx: --> <!-- equiv to PC_VSTREAM_CONTROL.SIZE on a3xx/a4xx: -->
<bitfield name="VSC_SIZE" low="16" high="21" type="uint"/> <bitfield name="VSC_SIZE" low="16" high="21" type="uint"/>
<!-- equiv to PC_VSTREAM_CONTROL.N on a3xx/a4xx: --> <!-- equiv to PC_VSTREAM_CONTROL.N on a3xx/a4xx: -->
<bitfield name="VSC_N" low="22" high="26" type="uint"/> <bitfield name="VSC_N" low="22" high="26" type="uint"/>
<bitfield name="ABS_MASK" pos="28" type="a7xx_abs_mask_mode" addvariant="yes"/>
</reg32> </reg32>
<!-- BIN_DATA_ADDR -> VSC_PIPE[p].DATA_ADDRESS --> <stripe varset="a7xx_abs_mask_mode" variants="NO_ABS_MASK">
<reg32 offset="1" name="1"> <!-- BIN_DATA_ADDR -> VSC_PIPE[p].DATA_ADDRESS -->
<bitfield name="BIN_DATA_OFFSET" low="0" high="31" type="uint"/> <reg32 offset="1" name="1">
</reg32> <bitfield name="BIN_DATA_OFFSET" low="0" high="31" type="uint"/>
<!-- BIN_SIZE_ADDRESS -> VSC_SIZE_ADDRESS + (p * 4)--> </reg32>
<reg32 offset="2" name="2"> <!-- BIN_SIZE_ADDRESS -> VSC_SIZE_ADDRESS + (p * 4)-->
<bitfield name="BIN_SIZE_OFFSET" low="0" high="31" type="uint"/> <reg32 offset="2" name="2">
</reg32> <bitfield name="BIN_SIZE_OFFSET" low="0" high="31" type="uint"/>
<!-- BIN_DATA2_ADDR -> VSC_PIPE[p].DATA2_ADDRESS --> </reg32>
<reg32 offset="3" name="3"> <!-- BIN_DATA2_ADDR -> VSC_PIPE[p].DATA2_ADDRESS -->
<bitfield name="BIN_DATA2_OFFSET" low="0" high="31" type="uint"/> <reg32 offset="3" name="3">
</reg32> <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>
<domain name="CP_REG_RMW" width="32"> <domain name="CP_REG_RMW" width="32">
@@ -1196,6 +1250,9 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
</doc> </doc>
<reg32 offset="0" name="0"> <reg32 offset="0" name="0">
<bitfield name="DST_REG" low="0" high="17" type="hex"/> <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="ROTATE" low="24" high="28" type="uint"/>
<bitfield name="SRC1_ADD" pos="29" type="boolean"/> <bitfield name="SRC1_ADD" pos="29" type="boolean"/>
<bitfield name="SRC1_IS_REG" pos="30" type="boolean"/> <bitfield name="SRC1_IS_REG" pos="30" type="boolean"/>
@@ -1209,7 +1266,7 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
</reg32> </reg32>
</domain> </domain>
<domain name="CP_REG_TO_MEM" width="32"> <domain name="CP_REG_TO_MEM" width="32" prefix="chip">
<reg32 offset="0" name="0"> <reg32 offset="0" name="0">
<bitfield name="REG" low="0" high="17" type="hex"/> <bitfield name="REG" low="0" high="17" type="hex"/>
<!-- number of registers/dwords copied is max(CNT, 1). --> <!-- 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="64B" pos="30" type="boolean"/>
<bitfield name="ACCUMULATE" pos="31" type="boolean"/> <bitfield name="ACCUMULATE" pos="31" type="boolean"/>
</reg32> </reg32>
<reg32 offset="1" name="1"> <stripe varset="chip" variants="A2XX-A4XX">
<bitfield name="DEST" low="0" high="31"/> <reg32 offset="1" name="DEST" type="address"/>
</reg32> </stripe>
<reg32 offset="2" name="2" varset="chip" variants="A5XX-"> <stripe varset="chip" variants="A5XX-">
<bitfield name="DEST_HI" low="0" high="31"/> <reg64 offset="1" name="DEST" type="address"/>
</reg32> </stripe>
</domain> </domain>
<domain name="CP_REG_TO_MEM_OFFSET_REG" width="32"> <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="64B" pos="30" type="boolean"/>
<bitfield name="ACCUMULATE" pos="31" type="boolean"/> <bitfield name="ACCUMULATE" pos="31" type="boolean"/>
</reg32> </reg32>
<reg32 offset="1" name="1"> <reg64 offset="1" name="DEST" type="waddress"/>
<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"> <reg32 offset="3" name="3">
<bitfield name="OFFSET0" low="0" high="17" type="hex"/> <bitfield name="OFFSET0" low="0" high="17" type="hex"/>
<bitfield name="OFFSET0_SCRATCH" pos="19" type="boolean"/> <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="64B" pos="30" type="boolean"/>
<bitfield name="ACCUMULATE" pos="31" type="boolean"/> <bitfield name="ACCUMULATE" pos="31" type="boolean"/>
</reg32> </reg32>
<reg32 offset="1" name="1"> <reg64 offset="1" name="DEST" type="waddress"/>
<bitfield name="DEST" low="0" high="31"/> <reg64 offset="3" name="OFFSET" type="waddress"/>
</reg32>
<reg32 offset="2" name="2" varset="chip" variants="A5XX-">
<bitfield name="DEST_HI" low="0" high="31"/>
</reg32>
<reg32 offset="3" name="3">
<bitfield name="OFFSET_LO" low="0" high="31" type="hex"/>
</reg32>
<reg32 offset="4" name="4">
<bitfield name="OFFSET_HI" low="0" high="31" type="hex"/>
</reg32>
</domain> </domain>
<domain name="CP_MEM_TO_REG" width="32"> <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 --> <!-- does the same thing as CP_MEM_TO_MEM::UNK31 -->
<bitfield name="UNK31" pos="31" type="boolean"/> <bitfield name="UNK31" pos="31" type="boolean"/>
</reg32> </reg32>
<reg32 offset="1" name="1"> <stripe varset="chip" variants="A2XX-A4XX">
<bitfield name="SRC" low="0" high="31"/> <reg32 offset="1" name="SRC" type="address"/>
</reg32> </stripe>
<reg32 offset="2" name="2" varset="chip" variants="A5XX-"> <stripe varset="chip" variants="A5XX-">
<bitfield name="SRC_HI" low="0" high="31"/> <reg64 offset="1" name="SRC" type="address"/>
</reg32> </stripe>
</domain> </domain>
<domain name="CP_MEM_TO_MEM" width="32"> <domain name="CP_MEM_TO_MEM" width="32">
@@ -1312,6 +1354,10 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<!-- some other kind of wait --> <!-- some other kind of wait -->
<bitfield name="UNK31" pos="31" type="boolean"/> <bitfield name="UNK31" pos="31" type="boolean"/>
</reg32> </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 followed by sequence of addresses.. the first is the
destination and the rest are N src addresses which are 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"/> <bitfield name="SCRATCH" low="20" high="22" type="uint"/>
<!-- number of registers/dwords copied is CNT + 1. --> <!-- number of registers/dwords copied is CNT + 1. -->
<bitfield name="CNT" low="24" high="26" type="uint"/> <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> </reg32>
</domain> </domain>
@@ -1368,12 +1416,12 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
</domain> </domain>
<domain name="CP_MEM_WRITE" width="32"> <domain name="CP_MEM_WRITE" width="32">
<reg32 offset="0" name="0"> <stripe varset="chip" variants="A2XX-A4XX">
<bitfield name="ADDR_LO" low="0" high="31"/> <reg32 offset="0" name="ADDR" type="address"/>
</reg32> </stripe>
<reg32 offset="1" name="1"> <stripe varset="chip" variants="A5XX-">
<bitfield name="ADDR_HI" low="0" high="31"/> <reg64 offset="0" name="ADDR" type="address"/>
</reg32> </stripe>
<!-- followed by the DWORDs to write --> <!-- followed by the DWORDs to write -->
</domain> </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="POLL" low="4" high="5" type="poll_memory_type"/>
<bitfield name="WRITE_MEMORY" pos="8" type="boolean"/> <bitfield name="WRITE_MEMORY" pos="8" type="boolean"/>
</reg32> </reg32>
<reg32 offset="1" name="1"> <reg64 offset="1" name="POLL_ADDR" type="address"/>
<bitfield name="POLL_ADDR_LO" low="0" high="31" type="hex"/>
</reg32>
<reg32 offset="2" name="2">
<bitfield name="POLL_ADDR_HI" low="0" high="31" type="hex"/>
</reg32>
<reg32 offset="3" name="3"> <reg32 offset="3" name="3">
<bitfield name="REF" low="0" high="31"/> <bitfield name="REF" low="0" high="31"/>
</reg32> </reg32>
<reg32 offset="4" name="4"> <reg32 offset="4" name="4">
<bitfield name="MASK" low="0" high="31"/> <bitfield name="MASK" low="0" high="31"/>
</reg32> </reg32>
<reg32 offset="5" name="5"> <reg64 offset="5" name="WRITE_ADDR" type="waddress"/>
<bitfield name="WRITE_ADDR_LO" low="0" high="31" type="hex"/>
</reg32>
<reg32 offset="6" name="6">
<bitfield name="WRITE_ADDR_HI" low="0" high="31" type="hex"/>
</reg32>
<reg32 offset="7" name="7"> <reg32 offset="7" name="7">
<bitfield name="WRITE_DATA" low="0" high="31"/> <bitfield name="WRITE_DATA" low="0" high="31"/>
</reg32> </reg32>
@@ -1457,12 +1495,7 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<!-- Reserved for flags, presumably? Unused in FW --> <!-- Reserved for flags, presumably? Unused in FW -->
<bitfield name="RESERVED" low="0" high="31" type="hex"/> <bitfield name="RESERVED" low="0" high="31" type="hex"/>
</reg32> </reg32>
<reg32 offset="1" name="1"> <reg64 offset="1" name="POLL_ADDR" type="address"/>
<bitfield name="POLL_ADDR_LO" low="0" high="31" type="hex"/>
</reg32>
<reg32 offset="2" name="2">
<bitfield name="POLL_ADDR_HI" low="0" high="31" type="hex"/>
</reg32>
<reg32 offset="3" name="3"> <reg32 offset="3" name="3">
<bitfield name="REF" low="0" high="31"/> <bitfield name="REF" low="0" high="31"/>
</reg32> </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="POLL" low="4" high="5" type="poll_memory_type"/>
<bitfield name="WRITE_MEMORY" pos="8" type="boolean"/> <bitfield name="WRITE_MEMORY" pos="8" type="boolean"/>
</reg32> </reg32>
<reg32 offset="1" name="1"> <reg64 offset="1" name="POLL_ADDR" type="address"/>
<bitfield name="POLL_ADDR_LO" low="0" high="31" type="hex"/>
</reg32>
<reg32 offset="2" name="2">
<bitfield name="POLL_ADDR_HI" low="0" high="31" type="hex"/>
</reg32>
<reg32 offset="3" name="3"> <reg32 offset="3" name="3">
<bitfield name="REF" low="0" high="31"/> <bitfield name="REF" low="0" high="31"/>
</reg32> </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 TODO what is gpuaddr for, seems to be all 0's.. maybe needed for
context switch? context switch?
--> -->
<reg32 offset="1" name="1"> <reg64 offset="1" name="ADDR" type="waddress"/>
<bitfield name="ADDR_0_LO" low="0" high="31"/>
</reg32>
<reg32 offset="2" name="2">
<bitfield name="ADDR_0_HI" low="0" high="31"/>
</reg32>
<reg32 offset="3" name="3"> <reg32 offset="3" name="3">
<!-- ??? --> <!-- ??? -->
</reg32> </reg32>
@@ -1653,8 +1676,8 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<bitfield name="WRITE_SAMPLE_COUNT" pos="12" type="boolean"/> <bitfield name="WRITE_SAMPLE_COUNT" pos="12" type="boolean"/>
<!-- Write sample count at (iova + 16) --> <!-- Write sample count at (iova + 16) -->
<bitfield name="SAMPLE_COUNT_END_OFFSET" pos="13" type="boolean"/> <bitfield name="SAMPLE_COUNT_END_OFFSET" pos="13" type="boolean"/>
<!-- *(iova + 8) = *(iova + 16) - *iova --> <!-- *(iova + 8) += *(iova + 16) - *iova -->
<bitfield name="WRITE_SAMPLE_COUNT_DIFF" pos="14" type="boolean"/> <bitfield name="WRITE_ACCUM_SAMPLE_COUNT_DIFF" pos="14" type="boolean"/>
<!-- Next 4 flags are valid to set only when concurrent binning is enabled --> <!-- Next 4 flags are valid to set only when concurrent binning is enabled -->
<!-- Increment 16b BV counter. Valid only in BV pipe --> <!-- 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"/> <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. --> <!-- Writes into WRITE_DST from WRITE_SRC. RB_DONE_TS requires WRITE_ENABLED. -->
<bitfield name="WRITE_ENABLED" pos="27" type="boolean"/> <bitfield name="WRITE_ENABLED" pos="27" type="boolean"/>
<bitfield name="IRQ" pos="31" type="boolean"/>
</reg32> </reg32>
<stripe varset="event_write_dst" variants="EV_DST_RAM"> <stripe varset="event_write_dst" variants="EV_DST_RAM">
<reg32 offset="1" name="1"> <reg64 offset="1" name="1" type="waddress"/>
<bitfield name="ADDR_0_LO" low="0" high="31"/>
</reg32>
<reg32 offset="2" name="2">
<bitfield name="ADDR_0_HI" low="0" high="31"/>
</reg32>
<reg32 offset="3" name="3"> <reg32 offset="3" name="3">
<bitfield name="PAYLOAD_0" low="0" high="31"/> <bitfield name="PAYLOAD_0" low="0" high="31"/>
</reg32> </reg32>
@@ -1743,9 +1762,7 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<reg32 offset="0" name="0"> <reg32 offset="0" name="0">
</reg32> </reg32>
<stripe varset="chip" variants="A4XX"> <stripe varset="chip" variants="A4XX">
<reg32 offset="1" name="1"> <reg32 offset="1" name="ADDR" type="address"/>
<bitfield name="ADDR" low="0" high="31"/>
</reg32>
<reg32 offset="2" name="2"> <reg32 offset="2" name="2">
<!-- localsize is value minus one: --> <!-- localsize is value minus one: -->
<bitfield name="LOCALSIZEX" low="2" high="11" type="uint"/> <bitfield name="LOCALSIZEX" low="2" high="11" type="uint"/>
@@ -1754,12 +1771,7 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
</reg32> </reg32>
</stripe> </stripe>
<stripe varset="chip" variants="A5XX-"> <stripe varset="chip" variants="A5XX-">
<reg32 offset="1" name="1"> <reg64 offset="1" name="ADDR" type="address"/>
<bitfield name="ADDR_LO" low="0" high="31"/>
</reg32>
<reg32 offset="2" name="2">
<bitfield name="ADDR_HI" low="0" high="31"/>
</reg32>
<reg32 offset="3" name="3"> <reg32 offset="3" name="3">
<!-- localsize is value minus one: --> <!-- localsize is value minus one: -->
<bitfield name="LOCALSIZEX" low="2" high="11" type="uint"/> <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-"> <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> <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"> <enum name="a6xx_marker">
<value value="1" name="RM6_BYPASS"/> <value value="1" name="RM6_DIRECT_RENDER"/>
<value value="2" name="RM6_BINNING"/> <value value="2" name="RM6_BIN_VISIBILITY"/>
<value value="4" name="RM6_GMEM"/> <value value="3" name="RM6_BIN_DIRECT"/>
<value value="5" name="RM6_ENDVIS"/> <value value="4" name="RM6_BIN_RENDER_START"/>
<value value="6" name="RM6_RESOLVE"/> <value value="5" name="RM6_BIN_END_OF_DRAWS"/>
<value value="7" name="RM6_YIELD"/> <value value="6" name="RM6_BIN_RESOLVE"/>
<value value="7" name="RM6_BIN_RENDER_END"/>
<value value="8" name="RM6_COMPUTE"/> <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 These values come from a6xx_set_marker() in the
downstream kernel, and they can only be set by the kernel downstream kernel, and they can only be set by the kernel
--> -->
<value value="0xd" name="RM6_IB1LIST_START"/> <value value="13" name="RM6_IB1LIST_START"/>
<value value="0xe" name="RM6_IB1LIST_END"/> <value value="14" name="RM6_IB1LIST_END"/>
<!-- IFPC - inter-frame power collapse --> <value value="15" name="RM7_BIN_VISIBILITY_END"/>
<value value="0x100" name="RM6_IFPC_ENABLE"/>
<value value="0x101" name="RM6_IFPC_DISABLE"/> <!-- new in a8xx: -->
<value value="32" name="RM8_DEPTH_PASS_START"/>
<value value="33" name="RM8_DEPTH_PASS_END"/>
</enum> </enum>
<reg32 offset="0" name="0"> <stripe varset="chip" variants="A6XX-A7XX">
<!-- <reg32 offset="0" name="0">
NOTE: blob driver and some versions of freedreno/turnip set <!-- if b8 is set, the low bits are interpreted differently (and b4 ignored) -->
b4, which is unused (at least by current sqe fw), but interferes <bitfield name="MARKER_MODE" pos="8" type="set_marker_mode" addvariant="yes"/>
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 <bitfield name="MODE" low="0" high="3" type="a6xx_marker" varset="set_marker_mode" variants="SET_RENDER_MODE"/>
if b8 is set, the low bits are interpreted differently. To <!-- used by preemption to determine if GMEM needs to be saved or not -->
model this, without getting confused by spurious b4, this is <bitfield name="USES_GMEM" pos="4" type="boolean" varset="set_marker_mode" variants="SET_RENDER_MODE"/>
described as two overlapping bitfields:
-->
<bitfield name="MODE" low="0" high="8" type="a6xx_marker"/> <bitfield name="IFPC_MODE" pos="0" type="a6xx_ifpc_mode" varset="set_marker_mode" variants="SET_IFPC_MODE"/>
<bitfield name="MARKER" low="0" high="3" type="a6xx_marker"/>
</reg32>
<!--
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>
<domain name="CP_SET_PSEUDO_REG" width="32" varset="chip" prefix="chip" variants="A6XX-"> <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 If concurrent binning is disabled then BR also does binning so it will also
write the "real" registers in BR. write the "real" registers in BR.
--> -->
<value value="8" name="DRAW_STRM_ADDRESS"/> <value value="8" name="VSC_PIPE_DATA_DRAW_BASE"/>
<value value="9" name="DRAW_STRM_SIZE_ADDRESS"/> <value value="9" name="VSC_SIZE_BASE"/>
<value value="10" name="PRIM_STRM_ADDRESS"/> <value value="10" name="VSC_PIPE_DATA_PRIM_BASE"/>
<value value="11" name="UNK_STRM_ADDRESS"/> <value value="11" name="UNK_STRM_ADDRESS"/>
<value value="12" name="UNK_STRM_SIZE_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. a bitmask of which modes pass the test.
--> -->
<!-- RM6_BINNING --> <!-- RM6_BIN_VISIBILITY -->
<bitfield name="BINNING" pos="25" variants="RENDER_MODE" type="boolean"/> <bitfield name="BINNING" pos="25" variants="RENDER_MODE" type="boolean"/>
<!-- all others --> <!-- all others -->
<bitfield name="GMEM" pos="26" variants="RENDER_MODE" type="boolean"/> <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="SYSMEM" pos="27" variants="RENDER_MODE" type="boolean"/>
<bitfield name="BV" pos="25" variants="THREAD_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> </reg32>
</domain> </domain>
<domain name="CP_SET_CTXSWITCH_IB" width="32"> <domain name="CP_SET_AMBLE" width="32">
<doc> <doc>
Used by the userspace driver to set various IB's which are Used by the userspace and kernel drivers to set various IB's
executed during context save/restore for handling which are executed during context save/restore for handling
state that isn't restored by the state that isn't restored by the context switch routine itself.
context switch routine itself.
</doc> </doc>
<enum name="ctxswitch_ib"> <enum name="amble_type">
<value name="RESTORE_IB" value="0"> <value name="PREAMBLE_AMBLE_TYPE" value="0">
<doc>Executed unconditionally when switching back to the context.</doc> <doc>Executed unconditionally when switching back to the context.</doc>
</value> </value>
<value name="YIELD_RESTORE_IB" value="1"> <value name="BIN_PREAMBLE_AMBLE_TYPE" value="1">
<doc> <doc>
Executed when switching back after switching Executed when switching back after switching
away during execution of away during execution of
a CP_SET_MARKER packet with RM6_YIELD as the a CP_SET_MARKER packet with RM6_BIN_RENDER_END as the
payload *and* the normal save routine was payload *and* skipsaverestore is set. This is
bypassed for a shorter one. I think this is expected to restore static register values not
connected to the "skipsaverestore" bit set by saved when skipsaverestore is set.
the kernel when preempting.
</doc> </doc>
</value> </value>
<value name="SAVE_IB" value="2"> <value name="POSTAMBLE_AMBLE_TYPE" value="2">
<doc> <doc>
Executed when switching away from the context, Executed when switching away from the context,
except for context switches initiated via except for context switches initiated via
CP_YIELD. CP_YIELD.
</doc> </doc>
</value> </value>
<value name="RB_SAVE_IB" value="3"> <value name="KMD_AMBLE_TYPE" value="3">
<doc> <doc>
This can only be set by the RB (i.e. the kernel) This can only be set by the RB (i.e. the kernel)
and executes with protected mode off, but and executes with protected mode off, but
is otherwise similar to SAVE_IB. is otherwise similar to POSTAMBLE_AMBLE_TYPE.
Note, kgsl calls this CP_KMD_AMBLE_TYPE
</doc> </doc>
</value> </value>
</enum> </enum>
<reg32 offset="0" name="0"> <reg64 offset="0" name="ADDR" type="address"/>
<bitfield name="ADDR_LO" low="0" high="31"/>
</reg32>
<reg32 offset="1" name="1">
<bitfield name="ADDR_HI" low="0" high="31"/>
</reg32>
<reg32 offset="2" name="2"> <reg32 offset="2" name="2">
<bitfield name="DWORDS" low="0" high="19" type="uint"/> <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> </reg32>
</domain> </domain>
@@ -2089,12 +2140,12 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<value name="UNK_EVENT_WRITE" value="0x4"/> <value name="UNK_EVENT_WRITE" value="0x4"/>
<doc> <doc>
Tracks GRAS_LRZ_CNTL::GREATER, GRAS_LRZ_CNTL::DIR, and 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: the following is true:
- GRAS_LRZ_CNTL::GREATER has changed - GRAS_LRZ_CNTL::GREATER has changed
- GRAS_LRZ_CNTL::DIR has changed, the old value is not - GRAS_LRZ_CNTL::DIR has changed, the old value is not
CUR_DIR_GE, and the new value is not CUR_DIR_DISABLED 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 then it does a LRZ_FLUSH with GRAS_LRZ_CNTL::ENABLE
forced to 1. forced to 1.
Only exists in a650_sqe.fw. 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"> <domain name="CP_MEM_TO_SCRATCH_MEM" width="32">
<doc> <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 and keep them in a local scratch memory instead of fetching every time
when skipping IBs. when skipping IBs.
</doc> </doc>
@@ -2257,7 +2308,25 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<reg32 offset="0" name="0"> <reg32 offset="0" name="0">
<bitfield name="CLEAR_ON_CHIP_TS" pos="0" type="boolean"/> <bitfield name="CLEAR_ON_CHIP_TS" pos="0" type="boolean"/>
<bitfield name="CLEAR_RESOURCE_TABLE" pos="1" 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> </reg32>
</domain> </domain>
+16 -7
View File
@@ -97,7 +97,7 @@ def parse_cmd_buf(dat):
if state_block == SB6_CS_SHADER: if state_block == SB6_CS_SHADER:
from extra.disassemblers.adreno import disasm_raw 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)) disasm_raw(get_mem(((vals[2] << 32) | vals[1]), num_unit * 128))
if state_type == ST6_CONSTANTS: if state_type == ST6_CONSTANTS:
x = get_mem(((vals[2] << 32) | vals[1]), num_unit*4) x = get_mem(((vals[2] << 32) | vals[1]), num_unit*4)
@@ -106,25 +106,30 @@ def parse_cmd_buf(dat):
print('constants') print('constants')
hexdump(x) hexdump(x)
if state_type == ST6_IBO: 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[:] CAPTURED_STATE['ibos'] = ibos_bytes[:]
if IOCTL > 1: if IOCTL > 1:
print('texture ibos') print('texture ibos')
hexdump(ibos_bytes) hexdump(ibos_bytes)
elif state_block == SB6_CS_TEX: elif state_block == SB6_CS_TEX:
if state_type == ST6_SHADER: 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[:] CAPTURED_STATE['samplers'] = samplers_bytes[:]
if IOCTL > 1: if IOCTL > 1:
print('texture samplers') print('texture samplers')
hexdump(samplers_bytes) hexdump(samplers_bytes)
if state_type == ST6_CONSTANTS: 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[:] CAPTURED_STATE['descriptors'] = descriptors_bytes[:]
if IOCTL > 1: if IOCTL > 1:
print('texture descriptors') print('texture descriptors')
hexdump(descriptors_bytes) hexdump(descriptors_bytes)
elif ops[opcode] == "CP_REG_TO_MEM": 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 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) dest = vals[1] | (vals[2] << 32)
@@ -152,6 +157,10 @@ def parse_cmd_buf(dat):
if IOCTL > 0: 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'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]}') 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 offset == 0xb180:
if IOCTL > 0: if IOCTL > 0:
print('border color offset', hex(vals[1] << 32 | vals[0])) print('border color offset', hex(vals[1] << 32 | vals[0]))
@@ -171,8 +180,8 @@ def ioctl(fd, request, argp):
name, stype = nrs[nr] name, stype = nrs[nr]
s = get_struct(argp, stype) s = get_struct(argp, stype)
if IOCTL > 0: print(f"{ret:2d} = {name:40s}", ' '.join(format_struct(s))) if IOCTL > 0: print(f"{ret:2d} = {name:40s}", ' '.join(format_struct(s)))
if name == "IOCTL_KGSL_GPUOBJ_INFO": pass if name == "IOCTL_KGSL_GPUOBJ_INFO":
# mmaped[s.gpuaddr] = mmap.mmap(fd, s.size, offset=s.id*0x1000) mmaped[s.gpuaddr] = mmap.mmap(fd, s.size, offset=s.id*0x1000)
if name == "IOCTL_KGSL_GPU_COMMAND": if name == "IOCTL_KGSL_GPU_COMMAND":
for i in range(s.numcmds): 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) cmd = get_struct(s.cmdlist+ctypes.sizeof(msm_kgsl.struct_kgsl_command_object)*i, msm_kgsl.struct_kgsl_command_object)
+10 -1
View File
@@ -882,6 +882,11 @@ impl<'a> Thread<'a> {
let s1 = sign_ext((s1 & 0xffffff) as u64, 24) as i32; let s1 = sign_ext((s1 & 0xffffff) as u64, 24) as i32;
(s0 * s1) as u32 (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 => { 17 | 18 | 26 => {
let (s0, s1) = (s0 as i32, s1 as i32); let (s0, s1) = (s0 as i32, s1 as i32);
(match op { (match op {
@@ -930,7 +935,7 @@ impl<'a> Thread<'a> {
let op = ((instr >> 16) & 0x3ff) as u32; let op = ((instr >> 16) & 0x3ff) as u32;
match op { 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 vdst = (instr & 0xff) as usize;
let sdst = ((instr >> 8) & 0x7f) as usize; let sdst = ((instr >> 8) & 0x7f) as usize;
let f = |i: u32| -> usize { ((instr >> i) & 0x1ff) 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); let ret = s0.wrapping_sub(s1);
(ret as u32, s1 > s0) (ret as u32, s1 > s0)
} }
770 => {
let ret = s1.wrapping_sub(s0);
(ret as u32, s0 > s1)
}
_ => todo_instr!(instruction)?, _ => todo_instr!(instruction)?,
}; };
if self.exec.read() { if self.exec.read() {
+64 -119
View File
@@ -1,98 +1,32 @@
import numpy as np import numpy as np
import unittest import unittest
import subprocess, struct, math import subprocess, struct, math
from typing import cast from tinygrad import Tensor, dtypes, Device, UOp
from tinygrad.runtime.ops_amd import AMDProgram, AMDDevice from tinygrad.helpers import getenv
from tinygrad import Tensor, dtypes, Device from tinygrad.runtime.support.compiler_amd import amdgpu_disassemble
from tinygrad.helpers import diskcache, OSX, getenv from tinygrad.renderer import ProgramSpec
from tinygrad.engine.realize import CompiledRunner
@diskcache def get_output(asm:str, n_threads:int=1):
def assemble(code:str) -> bytes: input_asm = "\n".join([ln if ln.strip().startswith('asm volatile') else f'asm volatile("{ln.strip().lstrip()}" : "+v"(a), "+v"(b));'
try: for ln in asm.strip().splitlines() if ln.strip()])
LLVM_MC = "llvm-mc" if OSX else "/opt/rocm/llvm/bin/llvm-mc" src = f"""
return subprocess.run([LLVM_MC, "--arch=amdgcn", "--mcpu=gfx1100", "--triple=amdgcn-amd-amdhsa", "-filetype=obj", "-o", "-"], typedef long unsigned int size_t;
input=code.encode("utf-8"), stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True).stdout extern "C" __attribute__((device, const)) size_t __ockl_get_local_id(unsigned int);
except subprocess.CalledProcessError as e: extern "C" __attribute__((global)) void __attribute__((amdgpu_flat_work_group_size(1, {n_threads}))) test(unsigned int* data0_1) {{
print("stderr:") int l = __ockl_get_local_id(0);
print(e.stderr.decode()) unsigned a = 0, b = 0, c = 0;
raise {input_asm}
unsigned res;
# copied from extra/rdna asm volatile("v_mov_b32 %0, %1" : "=v"(res) : "v"(a));
def get_prg(code:str, v_cnt:int, s_cnt:int): *(data0_1+l) = res;
function_name = "test" }}"""
metadata = f""" t = Tensor.zeros(n_threads, dtype=dtypes.uint32).contiguous().realize()
amdhsa.kernels: prg = ProgramSpec("test", src, Device.DEFAULT, UOp.sink(t), global_size=[1, 1, 1], local_size=[n_threads, 1, 1])
- .args: car = CompiledRunner(prg)
- .address_space: global if getenv("PRINT_ASM"): amdgpu_disassemble(car.lib)
.name: buf_0 car([t.uop.buffer], {}, wait=True)
.offset: 0 return t.numpy()
.size: 8
.type_name: unsigned int*
.value_kind: global_buffer
.group_segment_fixed_size: 0
.kernarg_segment_align: 8
.kernarg_segment_size: 8
.language: OpenCL C
.language_version:
- 1
- 2
.max_flat_workgroup_size: 256
.name: test
.private_segment_fixed_size: 0
.sgpr_count: {s_cnt}
.sgpr_spill_count: 0
.symbol: test.kd
.uses_dynamic_stack: false
.vgpr_count: {v_cnt}
.vgpr_spill_count: 0
.wavefront_size: 32
amdhsa.target: amdgcn-amd-amdhsa--gfx1100
amdhsa.version:
- 1
- 2
"""
boilerplate_start = f"""
.rodata
.global {function_name}.kd
.type {function_name}.kd,STT_OBJECT
.align 0x10
.amdhsa_kernel {function_name}"""
kernel_desc = {
'.amdhsa_group_segment_fixed_size': 0, '.amdhsa_private_segment_fixed_size': 0, '.amdhsa_kernarg_size': 0,
'.amdhsa_next_free_vgpr': v_cnt, # this matters!
'.amdhsa_reserve_vcc': 0, '.amdhsa_reserve_xnack_mask': 0,
'.amdhsa_next_free_sgpr': s_cnt,
'.amdhsa_float_round_mode_32': 0, '.amdhsa_float_round_mode_16_64': 0, '.amdhsa_float_denorm_mode_32': 3, '.amdhsa_float_denorm_mode_16_64': 3,
'.amdhsa_dx10_clamp': 1, '.amdhsa_ieee_mode': 1, '.amdhsa_fp16_overflow': 0,
'.amdhsa_workgroup_processor_mode': 1, '.amdhsa_memory_ordered': 1, '.amdhsa_forward_progress': 0, '.amdhsa_enable_private_segment': 0,
'.amdhsa_system_sgpr_workgroup_id_x': 1, '.amdhsa_system_sgpr_workgroup_id_y': 1, '.amdhsa_system_sgpr_workgroup_id_z': 1,
'.amdhsa_system_sgpr_workgroup_info': 0, '.amdhsa_system_vgpr_workitem_id': 2, # is amdhsa_system_vgpr_workitem_id real?
'.amdhsa_exception_fp_ieee_invalid_op': 0, '.amdhsa_exception_fp_denorm_src': 0,
'.amdhsa_exception_fp_ieee_div_zero': 0, '.amdhsa_exception_fp_ieee_overflow': 0, '.amdhsa_exception_fp_ieee_underflow': 0,
'.amdhsa_exception_fp_ieee_inexact': 0, '.amdhsa_exception_int_div_zero': 0,
'.amdhsa_user_sgpr_dispatch_ptr': 0, '.amdhsa_user_sgpr_queue_ptr': 0, '.amdhsa_user_sgpr_kernarg_segment_ptr': 1,
'.amdhsa_user_sgpr_dispatch_id': 0, '.amdhsa_user_sgpr_private_segment_size': 0, '.amdhsa_wavefront_size32': 1, '.amdhsa_uses_dynamic_stack': 0}
code_start = f""".end_amdhsa_kernel
.text
.global {function_name}
.type {function_name},@function
.p2align 8
{function_name}:
"""
ret = ".amdgpu_metadata\n" + metadata + ".end_amdgpu_metadata" + boilerplate_start + "\n" + '\n'.join("%s %d" % x for x in kernel_desc.items()) \
+ "\n" + code_start + code + f"\n.size {function_name}, .-{function_name}"
return AMDProgram(cast(AMDDevice, Device["AMD"]), function_name, assemble(ret))
def get_output(s:str, n_threads:int=1):
assert n_threads <= 32
code = "\n".join(["s_load_b64 s[0:1], s[0:1], null", "v_lshlrev_b32_e32 v0, 2, v0", s,
"s_waitcnt 0",
"global_store_b32 v0, v1, s[0:1]",
"s_nop 0", "s_sendmsg sendmsg(MSG_DEALLOC_VGPRS)", "s_endpgm"])
test = Tensor.zeros((n_threads,), dtype=dtypes.uint32).contiguous().realize().uop.buffer
prg = get_prg(code, 32, 32)
prg(test._buf, global_size=(1, 1, 1), local_size=(n_threads, 1, 1), wait=True)
return test.numpy()
def f16_to_bits(x:float) -> int: return struct.unpack('<H', struct.pack('<e', x))[0] def 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] 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): def test_simple(self):
out = get_output(""" out = get_output("""
v_mov_b32_e32 v10 42 v_mov_b32_e32 %1 42
v_mov_b32_e32 v1 v10 v_mov_b32_e32 %2 %1
""", n_threads=2) """)[0]
np.testing.assert_equal(out, 42) np.testing.assert_equal(out, 42)
def test_exec_mov(self): def test_exec_mov(self):
out = get_output(""" out = get_output("""
v_mov_b32_e32 v10 42 v_mov_b32_e32 %1 42
s_mov_b32_e32 exec_lo 0b10 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 s_mov_b32_e32 exec_lo 0b11
v_mov_b32_e32 v1 v10 v_mov_b32_e32 %2 %1
""", n_threads=2) """, n_threads=2)
np.testing.assert_equal(out, [42, 10]) np.testing.assert_equal(out, [42, 10])
def test_exec_cmp_vopc(self): def test_exec_cmp_vopc(self):
out = get_output(""" out = get_output("""
s_mov_b32 vcc_lo 0 // reset vcc s_mov_b32 vcc_lo 0 // reset vcc
v_mov_b32_e32 v10 42 v_mov_b32_e32 %1 42
v_mov_b32_e32 v11 10 v_mov_b32_e32 %2 10
s_mov_b32_e32 exec_lo 0b01 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 s_mov_b32_e32 exec_lo 0b11
v_mov_b32_e32 v1 vcc_lo v_mov_b32_e32 %2 vcc_lo
""", n_threads=2) """, n_threads=2)
np.testing.assert_equal(out, 0b01) np.testing.assert_equal(out, 0b01)
def test_exec_cmpx_vop3(self): def test_exec_cmpx_vop3(self):
out = get_output(""" out = get_output("""
v_mov_b32_e32 v10 42 s_mov_b32_e32 exec_lo 0b11
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 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 s10 exec_lo
s_mov_b32_e32 exec_lo 0b11 s_mov_b32_e32 exec_lo 0b11
v_mov_b32_e32 v1 s10 v_mov_b32_e32 %2 s10
""", n_threads=2) """, n_threads=2)[0]
np.testing.assert_equal(out, 0b01) np.testing.assert_equal(out & 0b11, 0b01)
def test_fmac_vop3_modifier(self): def test_fmac_vop3_modifier(self):
init_state = f""" init_state = f"""
v_mov_b32_e32 v10 {f16_to_bits(4.0)} asm volatile("v_mov_b32_e32 %1, {f16_to_bits(4.0)}" : "+v"(a));
v_mov_b32_e32 v11 {f16_to_bits(3.0)} asm volatile("v_mov_b32_e32 %1, {f16_to_bits(3.0)}" : "+v"(b));
v_mov_b32_e32 v1 {f16_to_bits(2.0)} 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.)) mov = """asm volatile("v_mov_b32_e32 %1, %2" : "+v"(c), "+v"(a));"""
self.assertEqual(get_output(init_state+"\n"+"v_fmac_f16_e64 v1 -v11 v10"), f16_to_bits(-10.)) 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"+"v_fmac_f16_e64 v1 -v11 -v10"), f16_to_bits(14.)) 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 test_s_abs_i32(self):
def s_abs_i32(x, y, dst="s10", scc=0): def s_abs_i32(x, y, dst="s10", scc=0):
@@ -160,7 +97,7 @@ class TestHW(unittest.TestCase):
self.assertEqual(get_output(f""" self.assertEqual(get_output(f"""
s_mov_b32_e32 {dst} {x} s_mov_b32_e32 {dst} {x}
s_abs_i32 {dst} {dst} s_abs_i32 {dst} {dst}
v_mov_b32_e32 v1 {reg} v_mov_b32_e32 %2 {reg}
""")[0], val) """)[0], val)
s_abs_i32(0x00000001, 0x00000001, scc=1) s_abs_i32(0x00000001, 0x00000001, scc=1)
s_abs_i32(0x7fffffff, 0x7fffffff, 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 test_v_rcp_f32_neg_vop3(self):
def v_neg_rcp_f32(x:float, y:float): def v_neg_rcp_f32(x:float, y:float):
out = get_output(f""" out = get_output(f"""
v_mov_b32_e32 v1 {f32_to_bits(x)} v_mov_b32_e32 %2 {f32_to_bits(x)}
v_rcp_f32_e64 v1, -v1 v_rcp_f32_e64 %2, -%2
""")[0] """)[0]
assert out == f32_to_bits(y), f"{f32_from_bits(out)} != {y} / {out} != {f32_to_bits(y)}" 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) v_neg_rcp_f32(math.inf, -0.0)
@@ -186,10 +123,11 @@ class TestHW(unittest.TestCase):
def test_v_cndmask_b32_neg(self): def test_v_cndmask_b32_neg(self):
def v_neg(x:int|float, y:float): def v_neg(x:int|float, y:float):
# always pick -v1
out = get_output(f""" out = get_output(f"""
v_mov_b32_e32 v1 {f32_to_bits(x)} v_mov_b32_e32 %2 {f32_to_bits(x)}
s_mov_b32_e32 s10 1 // always pick -v1 s_mov_b32_e32 s10 1
v_cndmask_b32 v1, v1, -v1 s10 v_cndmask_b32 %2, %2, -%2 s10
""")[0] """)[0]
assert out == f32_to_bits(y), f"{f32_from_bits(out)} != {y} / {out} != {f32_to_bits(y)}" assert out == f32_to_bits(y), f"{f32_from_bits(out)} != {y} / {out} != {f32_to_bits(y)}"
v_neg(-0.0, 0.0) 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)
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__": if __name__ == "__main__":
unittest.main() unittest.main()
+7 -3
View File
@@ -27,14 +27,18 @@ class _ROCParseCtx:
self.disasms[prog.base + addr] = info self.disasms[prog.base + addr] = info
self.addr2prg[prog.base + addr] = prog 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 find_program(self, addr): return self.addr2prg[addr]
def on_occupancy_ev(self, ev): 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): 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 = {} asm = {}
for j in range(ev.instructions_size): for j in range(ev.instructions_size):
+11
View File
@@ -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()
+39
View File
@@ -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
+31
View File
@@ -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)}")
+5 -4
View File
@@ -642,10 +642,11 @@ def get_real_tinygrad_buffers():
torch.nn.modules.module.register_module_buffer_registration_hook(register_torch_buffer) torch.nn.modules.module.register_module_buffer_registration_hook(register_torch_buffer)
from torch.nn.modules import Module from torch.nn.modules import Module
def backward_hook(model:Module, _grad_input, _grad_out): def param_hook(_grad):
grads_to_realize = [unwrap(p.grad) for p in model.parameters() if p.grad is not None] if _grad is not None and _grad.is_tiny: Tensor.realize(unwrap(_grad))
if len(grads_to_realize): Tensor.realize(*grads_to_realize) def module_hook(module:Module, _name, _submodule):
def module_hook(module:Module, _name, _submodule): module.register_backward_hook(backward_hook) 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) torch.nn.modules.module.register_module_module_registration_hook(module_hook)
def realize_optimizer_step(optimizer: torch.optim.Optimizer, *args, **kwargs): def realize_optimizer_step(optimizer: torch.optim.Optimizer, *args, **kwargs):
-75
View File
@@ -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
+13
View File
@@ -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
@@ -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 */;
}
@@ -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&amp;0x0000FFFF</string>
</dict>
<dict>
<key>IOPCIMatch</key>
<string>0x000010de&amp;0x0000FFFF</string>
</dict>
</array>
<key>com.apple.developer.driverkit.allow-any-userclient-access</key>
<true/>
<key>com.apple.developer.driverkit</key>
<true/>
</dict>
</plist>
@@ -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>
+39
View File
@@ -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 its safe!)
volatile uint32_t *mmio = (volatile uint32_t*)(uintptr_t)addr;
uint32_t v = mmio[0];
printf("mmio[0]=0x%08x\n", v);
kr = IOConnectUnmapMemory64(conn, bar, mach_task_self(), addr);
if (kr) fprintf(stderr, "Unmap failed 0x%x\n", kr);
IOServiceClose(conn);
return 0;
}
+82
View File
@@ -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)
-2
View File
@@ -25,8 +25,6 @@ nav:
- Layout: developer/layout.md - Layout: developer/layout.md
- Speed: developer/speed.md - Speed: developer/speed.md
- UOp: developer/uop.md - UOp: developer/uop.md
- Grouper:
- developer/kernelize.md
- Runtime: - Runtime:
- developer/runtime.md - developer/runtime.md
- HCQ: developer/hcq.md - HCQ: developer/hcq.md
+1 -2
View File
@@ -9,7 +9,7 @@ with open(directory / 'README.md', encoding='utf-8') as f:
testing_minimal = [ testing_minimal = [
"numpy", "numpy",
"torch==2.8.0", "torch==2.9.0",
"pytest", "pytest",
"pytest-xdist", "pytest-xdist",
"pytest-timeout", "pytest-timeout",
@@ -42,7 +42,6 @@ setup(name='tinygrad',
'tinygrad.runtime.support.am', 'tinygrad.runtime.support.am',
'tinygrad.runtime.support.nv', 'tinygrad.runtime.support.nv',
'tinygrad.schedule', 'tinygrad.schedule',
'tinygrad.shape',
'tinygrad.uop', 'tinygrad.uop',
'tinygrad.viz', 'tinygrad.viz',
], ],
+7 -2
View File
@@ -54,6 +54,8 @@ def gen_diff(table_old, table_new):
def display_diff(diff): return "+"+str(diff) if diff > 0 else str(diff) 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 __name__ == "__main__":
if len(sys.argv) == 3: if len(sys.argv) == 3:
headers = ["Name", "Lines", "Diff", "Tokens/Line", "Diff"] headers = ["Name", "Lines", "Diff", "Tokens/Line", "Diff"]
@@ -76,9 +78,12 @@ if __name__ == "__main__":
else: else:
print(tabulate([headers] + sorted(table, key=lambda x: -x[1]), headers="firstrow", floatfmt=".1f")+"\n") 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]) 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]): 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]) 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")) 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" assert max_line_count == -1 or total_lines <= max_line_count, f"OVER {max_line_count} LINES"
+2 -3
View File
@@ -1,4 +1,4 @@
from tinygrad import Tensor, dtypes, Context, GlobalCounters from tinygrad import Tensor, dtypes, GlobalCounters
dtypes.default_float = dtypes.float16 dtypes.default_float = dtypes.float16
from tinygrad.dtype import to_dtype from tinygrad.dtype import to_dtype
from tinygrad.helpers import getenv from tinygrad.helpers import getenv
@@ -13,6 +13,5 @@ if __name__ == "__main__":
# test single kernel softmax # test single kernel softmax
GlobalCounters.reset() GlobalCounters.reset()
with Context(DONT_GROUP_REDUCES=1): single_kernel_softmax(t, -1, acc_dtype).realize()
single_kernel_softmax(t, -1, acc_dtype).realize()
-63
View File
@@ -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
View File
@@ -2,8 +2,9 @@ from extra.models.resnet import ResNet50
from tinygrad import Tensor, nn, Device from tinygrad import Tensor, nn, Device
from tinygrad.helpers import Profiling, Timing, getenv from tinygrad.helpers import Profiling, Timing, getenv
from tinygrad.uop.ops import Ops from tinygrad.uop.ops import Ops
from tinygrad.codegen import get_rewrites_for_renderer, apply_rewrites, rewrites_for_linearizer from tinygrad.codegen import full_rewrite_to_sink
from tinygrad.uop.spec import type_verify from tinygrad.codegen.late.control_flow import linearize
from tinygrad.uop.spec import type_verify, program_spec
if __name__ == "__main__": if __name__ == "__main__":
mdl = ResNet50() 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()) 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] 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 Profiling(PROFILE, fn="/tmp/rewrite.prof"):
with Timing("***** model rewrite in "): with Timing("***** model rewrite in "):
rewritten_uops = [] rewritten_uops = []
for u in asts: 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: if LINEARIZE:
with Timing("***** model linearize in "): with Timing("***** model linearize in "):
uops_line = [] uops_line = []
for u in rewritten_uops: for u in rewritten_uops:
uops_line.append(apply_rewrites(u, rewrites_for_linearizer)) uops_line.append(linearize(u))
with Timing("***** model verify in "): with Timing("***** model verify in "):
for u in uops_line: type_verify(u.arg.lst) for u in uops_line: type_verify(u, program_spec)
print(sum(len(u.arg.lst) for u in uops_line)) print(sum(len(u) for u in uops_line))
-46
View File
@@ -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
+14 -9
View File
@@ -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_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_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): class TestTrainingOnnxOps(TestOnnxOps):
# NOTE: ORT doesn't actually support training ops on cpu so we test using functions provided by onnx # NOTE: ORT doesn't actually support training ops on cpu so we test using functions provided by onnx
DOMAIN = AI_ONNX_PREVIEW_TRAINING_DOMAIN DOMAIN = AI_ONNX_PREVIEW_TRAINING_DOMAIN
@@ -282,11 +286,11 @@ class TestTrainingOnnxOps(TestOnnxOps):
tiny_out = runner(inps) tiny_out = runner(inps)
onnx_out = onnx_fxn(**inps, **opts) onnx_out = onnx_fxn(**inps, **opts)
for (nm, t_out), o_out in zip(tiny_out.items(), onnx_out): 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 from onnx.backend.test.case.node.adagrad import apply_adagrad
for t in [1, 3, 100]: for t in [0, 1, 3, 100]:
inputs = { inputs = {
"r": np.array(0.01, dtype=np.float32), "r": np.array(0.01, dtype=np.float32),
"t": np.array(t, dtype=np.int32), "t": np.array(t, dtype=np.int32),
@@ -298,10 +302,10 @@ class TestTrainingOnnxOps(TestOnnxOps):
outputs = ["X_out", "H_out"] outputs = ["X_out", "H_out"]
self._validate_training("Adagrad", apply_adagrad, inputs, attributes, outputs) 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 from onnx.backend.test.case.node.momentum import apply_momentum, apply_nesterov
for onnx_fxn, mode in ((apply_momentum, "standard"), (apply_nesterov, "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 = { inputs = {
"r": np.array(0.01, dtype=np.float32), "r": np.array(0.01, dtype=np.float32),
"t": np.array(t, dtype=np.int32), "t": np.array(t, dtype=np.int32),
@@ -313,9 +317,9 @@ class TestTrainingOnnxOps(TestOnnxOps):
outputs = ["X_out", "V_out"] outputs = ["X_out", "V_out"]
self._validate_training("Momentum", onnx_fxn, inputs, attributes, outputs) 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 from onnx.backend.test.case.node.adam import apply_adam
for t in [1, 3, 100]: for t in [0, 1, 3, 100]:
inputs = { inputs = {
"r": np.array(0.01, dtype=np.float32), "r": np.array(0.01, dtype=np.float32),
"t": np.array(t, dtype=np.int32), "t": np.array(t, dtype=np.int32),
@@ -422,6 +426,7 @@ class TestContribOnnxOps(TestOnnxOps):
outputs = ["C"] outputs = ["C"]
self.helper_test_single_op("QLinearAdd", inputs, attributes, outputs, atol=1) # TODO: look into why this is inaccurate 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"): with self.subTest(test_case="round_half_to_even"):
inputs = { inputs = {
"A": np.array([1, 1, 1, 1], dtype=np.int8), "A": np.array([1, 1, 1, 1], dtype=np.int8),
@@ -435,7 +440,7 @@ class TestContribOnnxOps(TestOnnxOps):
} }
attributes = {} attributes = {}
outputs = ["C"] 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): def test_qlinear_mul(self):
for dtype, zero_point in [(np.uint8, 128), (np.int8, 0)]: 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) self.helper_test_single_op("QLinearGlobalAveragePool", inputs, attributes, outputs)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
-55
View File
@@ -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()
-2
View File
@@ -1,6 +1,5 @@
import gc import gc
from tinygrad import Tensor, UOp, Device, nn 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.engine.realize import method_cache, get_program
from tinygrad.schedule.indexing import apply_movement_op from tinygrad.schedule.indexing import apply_movement_op
from test.test_tiny import TestTiny from test.test_tiny import TestTiny
@@ -69,7 +68,6 @@ if __name__ == "__main__":
# these caches will keep uops alive # these caches will keep uops alive
method_cache.clear() method_cache.clear()
views_to_valid_uop.cache_clear()
apply_movement_op.cache_clear() apply_movement_op.cache_clear()
Tensor._device_seeds.clear() Tensor._device_seeds.clear()
Tensor._device_rng_counters.clear() Tensor._device_rng_counters.clear()
+1 -1
View File
@@ -1,7 +1,7 @@
import random import random
import z3 import z3
from tinygrad import dtypes 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.ops import UOp
from tinygrad.uop.decompositions import fast_idiv from tinygrad.uop.decompositions import fast_idiv
random.seed(42) random.seed(42)
+2 -2
View File
@@ -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}") if not FUZZ_ALL_ACTIONS and test_lin.applied_opts: print(f"applied opts: {test_lin.applied_opts}")
# stop if kernel uops repeat # 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 KeyboardInterrupt: raise
except BaseException as e: except BaseException as e:
print(test_lin.ast) 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) (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: if state1 is not None and validate_device is not None:
validate_lin = test_lin.copy() validate_lin = test_lin.copy()
validate_lin.opts = validate_device.renderer validate_lin.ren = validate_device.renderer
if validate_rawbufs is None: if validate_rawbufs is None:
validate_rawbufs = [get_fuzz_rawbuf_like(x, copy=True, force_device=validate_device.device) for x in rawbufs] 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) (_msg, _, _, _, state2) = compare_linearizer(validate_lin, validate_rawbufs, var_vals, ground_truth, rtol=rtol, atol=atol)
+1 -1
View File
@@ -2,7 +2,7 @@ import random, operator
import z3 import z3
from tinygrad import Variable, dtypes from tinygrad import Variable, dtypes
from tinygrad.uop.ops import UOp 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 from tinygrad.helpers import DEBUG, Context
seed = random.randint(0, 100) seed = random.randint(0, 100)
+2 -2
View File
@@ -91,11 +91,11 @@ class TestKernelSpeed(unittest.TestCase):
# theoretical is nv_tflops=165, amd_tflops=123 # 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_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 # 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_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__': if __name__ == '__main__':
unittest.main() unittest.main()
-29
View File
@@ -2,7 +2,6 @@ import unittest
from tinygrad import Device, Tensor, dtypes from tinygrad import Device, Tensor, dtypes
from tinygrad.uop.ops import UOp, Ops from tinygrad.uop.ops import UOp, Ops
from tinygrad.codegen.opt import Opt, OptOps from tinygrad.codegen.opt import Opt, OptOps
from tinygrad.shape.shapetracker import ShapeTracker, View
from tinygrad.engine.realize import get_program from tinygrad.engine.realize import get_program
from tinygrad.helpers import AMX from tinygrad.helpers import AMX
@@ -149,33 +148,5 @@ class TestFloat4(unittest.TestCase):
assert TestFloat4.count_float4(uops) == (1, 1) 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__': if __name__ == '__main__':
unittest.main() unittest.main()
+6 -9
View File
@@ -1,6 +1,5 @@
import unittest import unittest
from tinygrad import Device, Tensor, dtypes from tinygrad import Device, Tensor, dtypes
from tinygrad.helpers import CI
from tinygrad.codegen.opt import Opt, OptOps, KernelOptError from tinygrad.codegen.opt import Opt, OptOps, KernelOptError
# TODO: write a clean version of this # TODO: write a clean version of this
@@ -177,9 +176,7 @@ class TestKernelOpts(unittest.TestCase):
], apply_tc=True, atol=atol, rtol=rtol) ], apply_tc=True, atol=atol, rtol=rtol)
def test_padto_matmul(self): def test_padto_matmul(self):
if (CI and Device.DEFAULT in ["AMD", "NV", "CUDA"]): N = 17
self.skipTest("super slow on CUDA and AMD because of the big grid dims")
N = 17 * 17
Tensor.manual_seed(289) Tensor.manual_seed(289)
a = Tensor.rand(N, N) a = Tensor.rand(N, N)
b = 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)]]) helper_linearizer_opt(a@b, [[Opt(OptOps.UNROLL, 0, 0), Opt(OptOps.PADTO, 2, 8)]])
def test_padto_sum_ok(self): def test_padto_sum_ok(self):
N = 18 * 18 N = 18
# NOTE: this setup prevents 17 * 17 contiguous merged into one dimension # NOTE: this setup prevents 17 * 17 contiguous merged into one dimension
a = Tensor.rand(N, N).realize().shrink(((0, 17), (0, 17))) * 100 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))) 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)],]) helper_linearizer_opt(a.sum(0).exp(), [[Opt(OptOps.PADTO, 1, 32)],])
def test_padto_sum_not_ok(self): def test_padto_sum_not_ok(self):
N = 18 * 18 N = 18
# NOTE: this setup prevents 17 * 17 contiguous merged into one dimension # NOTE: this setup prevents 17 * 17 contiguous merged into one dimension
a = Tensor.rand(N, N).shrink(((0, 17), (0, 17))).exp() a = Tensor.rand(N, N).shrink(((0, 17), (0, 17))).exp()
# exp is not safe to pad # 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)],]) helper_linearizer_opt(b.sum(0), [[Opt(OptOps.PADTO, 1, 32)],])
def test_padto_max(self): def test_padto_max(self):
N = 18 * 18 N = 18
# NOTE: this setup prevents 17 * 17 contiguous merged into one axis # NOTE: this setup prevents 17 * 17 contiguous merged into one axis
a = -Tensor.rand(N, N).shrink(((0, 17), (0, 17))) * 100 a = -Tensor.rand(N, N).shrink(((0, 17), (0, 17))) * 100
@@ -282,7 +279,7 @@ class TestKernelOpts(unittest.TestCase):
def test_padto_where(self): def test_padto_where(self):
Tensor.manual_seed(0) Tensor.manual_seed(0)
N = 17 * 17 N = 17
a = (Tensor.randn(N, N).realize().max(axis=0, keepdim=True) > 1).where(1, 0) a = (Tensor.randn(N, N).realize().max(axis=0, keepdim=True) > 1).where(1, 0)
helper_linearizer_opt(a.max(0), [ helper_linearizer_opt(a.max(0), [
[Opt(OptOps.PADTO, 0, 32)], [Opt(OptOps.PADTO, 0, 32)],
@@ -291,7 +288,7 @@ class TestKernelOpts(unittest.TestCase):
def test_padto_where_multioutput(self): def test_padto_where_multioutput(self):
Tensor.manual_seed(0) Tensor.manual_seed(0)
N = 17 * 17 N = 17
r = Tensor.randn(N, N).realize().max(axis=0, keepdim=True) > 1 r = Tensor.randn(N, N).realize().max(axis=0, keepdim=True) > 1
a0 = r.where(1, 0) a0 = r.where(1, 0)
a1 = r.where(2, 0) a1 = r.where(2, 0)
+1 -2
View File
@@ -131,8 +131,7 @@ class TestIndexing(unittest.TestCase):
# llama3 is 128256 # llama3 is 128256
vocab_size, embed_size = (10, 3) if CI else (32000, 4096) vocab_size, embed_size = (10, 3) if CI else (32000, 4096)
emb = nn.Embedding(vocab_size, embed_size) emb = nn.Embedding(vocab_size, embed_size)
# TODO: why is a new realize needed here emb_w = emb.weight.numpy()
emb_w = emb.weight.realize().numpy()
x = Tensor([1,2,3,4]) x = Tensor([1,2,3,4])
with Context(NOOPT=noopt): with Context(NOOPT=noopt):
GlobalCounters.reset() GlobalCounters.reset()
+2 -2
View File
@@ -1,7 +1,7 @@
import unittest, io import unittest, io
from contextlib import redirect_stdout from contextlib import redirect_stdout
from tinygrad import Tensor, dtypes, Device from tinygrad import Tensor, dtypes, Device
from tinygrad.helpers import OSX, CPU_LLVM from tinygrad.helpers import OSX, CPU_LLVM, CPU_LVP
from tinygrad.engine.realize import lower_schedule from tinygrad.engine.realize import lower_schedule
from tinygrad.device import is_dtype_supported from tinygrad.device import is_dtype_supported
from tinygrad.engine.realize import get_program from tinygrad.engine.realize import get_program
@@ -19,7 +19,7 @@ class TestCompileFailures(unittest.TestCase):
class TestDisassembly(unittest.TestCase): class TestDisassembly(unittest.TestCase):
# TODO: fails on llvm. llvm.LLVMGetHostCPUName() returns "generic" # TODO: fails on llvm. llvm.LLVMGetHostCPUName() returns "generic"
@unittest.skipUnless(Device.DEFAULT in ("CPU",) and not CPU_LLVM and OSX, "m series cpus support fp16 arithmetic") @unittest.skipUnless(Device.DEFAULT in ("CPU",) and not (CPU_LLVM or CPU_LVP) and OSX, "m series cpus support fp16 arithmetic")
def test_float16_alu(self): def test_float16_alu(self):
c = Tensor([1], dtype=dtypes.float16) + Tensor([1], dtype=dtypes.float16) c = Tensor([1], dtype=dtypes.float16) + Tensor([1], dtype=dtypes.float16)
s = c.schedule()[-1] s = c.schedule()[-1]
+1 -7
View File
@@ -24,7 +24,6 @@ class TestUnaryOpsConstFolding(unittest.TestCase):
_check_ast_count(0, Tensor.ones(4).cast(dtypes.int16)) _check_ast_count(0, Tensor.ones(4).cast(dtypes.int16))
_check_ast_count(0, Tensor.full(4, fill_value=-1).cast(dtypes.uint16)) _check_ast_count(0, Tensor.full(4, fill_value=-1).cast(dtypes.uint16))
@unittest.expectedFailure # no two level fold
def test_neg_folding(self): def test_neg_folding(self):
_check_ast_count(0, Tensor([1, 2, 3]).mul(-1).neg()) _check_ast_count(0, Tensor([1, 2, 3]).mul(-1).neg())
_check_ast_count(0, Tensor([1, 2, 3]).neg().mul(-1)) _check_ast_count(0, Tensor([1, 2, 3]).neg().mul(-1))
@@ -68,12 +67,9 @@ class TestBinaryOpsConstFolding(unittest.TestCase):
def test_tensor_one_mul(self): def test_tensor_one_mul(self):
_check_ast_count(0, Tensor.ones(4) * Tensor([1.0, 2, 3, 4])) _check_ast_count(0, Tensor.ones(4) * Tensor([1.0, 2, 3, 4]))
# TODO: these will be fixed with better folding
@unittest.expectedFailure
def test_bool_tensor_mul_bool(self): def test_bool_tensor_mul_bool(self):
_check_ast_count(0, Tensor([True, False]) * True) _check_ast_count(0, Tensor([True, False]) * True)
_check_ast_count(0, Tensor([True, False]) * False) _check_ast_count(0, Tensor([True, False]) * False)
@unittest.expectedFailure
def test_bool_mul_bool_tensor(self): def test_bool_mul_bool_tensor(self):
_check_ast_count(0, True * Tensor([True, False])) _check_ast_count(0, True * Tensor([True, False]))
_check_ast_count(0, False * Tensor([True, False])) _check_ast_count(0, False * Tensor([True, False]))
@@ -83,10 +79,8 @@ class TestBinaryOpsConstFolding(unittest.TestCase):
def test_div_tensor_one(self): def test_div_tensor_one(self):
_check_ast_count(0, Tensor([1.0, 2, 3, 4]) / Tensor.ones(4)) _check_ast_count(0, Tensor([1.0, 2, 3, 4]) / Tensor.ones(4))
@unittest.expectedFailure # TODO: fix
def test_idiv_literal_one(self): def test_idiv_literal_one(self):
_check_ast_count(0, Tensor([1, 2, 3, 4]) // 1) _check_ast_count(0, Tensor([1, 2, 3, 4]) // 1)
@unittest.expectedFailure # TODO: fix
def test_idiv_tensor_one(self): def test_idiv_tensor_one(self):
_check_ast_count(0, Tensor([1, 2, 3, 4]) // Tensor.ones(4, dtype=dtypes.int32)) _check_ast_count(0, Tensor([1, 2, 3, 4]) // Tensor.ones(4, dtype=dtypes.int32))
@@ -188,7 +182,7 @@ class TestReduceOpsConstFolding(unittest.TestCase):
np.testing.assert_equal(Tensor(4).sum().numpy(), 4) np.testing.assert_equal(Tensor(4).sum().numpy(), 4)
def test_padded_const_sum(self): def test_padded_const_sum(self):
_check_ast_count(1, Tensor.ones(4).pad(((1, 1),)).sum()) _check_ast_count(0, Tensor.ones(4).pad(((1, 1),)).sum())
np.testing.assert_equal(Tensor.ones(4).pad(((1, 1),)).sum().numpy(), 4) np.testing.assert_equal(Tensor.ones(4).pad(((1, 1),)).sum().numpy(), 4)
# NOTE: cannot just count the non-padded area because some Ops f do not have f(0) = 0. # NOTE: cannot just count the non-padded area because some Ops f do not have f(0) = 0.
+3 -2
View File
@@ -6,6 +6,7 @@ from tinygrad.device import is_dtype_supported
from tinygrad.helpers import getenv, DEBUG, CI from tinygrad.helpers import getenv, DEBUG, CI
from tinygrad.dtype import DType, DTYPES_DICT, least_upper_dtype, fp8_to_float, float_to_fp8, _to_np_dtype, _to_torch_dtype, truncate from tinygrad.dtype import DType, DTYPES_DICT, least_upper_dtype, fp8_to_float, float_to_fp8, _to_np_dtype, _to_torch_dtype, truncate
from tinygrad.renderer.ptx import PTXRenderer from tinygrad.renderer.ptx import PTXRenderer
from tinygrad.renderer.nir import NIRRenderer
from tinygrad import Device, Tensor, dtypes from tinygrad import Device, Tensor, dtypes
from hypothesis import given, settings, strategies as strat from hypothesis import given, settings, strategies as strat
from test.helpers import rand_for_dtype from test.helpers import rand_for_dtype
@@ -102,7 +103,7 @@ class TestDType(unittest.TestCase):
)) ))
@unittest.skipIf(Device.DEFAULT == "PYTHON", "skip for now") @unittest.skipIf(Device.DEFAULT == "PYTHON", "skip for now")
@unittest.skipIf(isinstance(Device[Device.DEFAULT].renderer, PTXRenderer), "skip for now") @unittest.skipIf(isinstance(Device[Device.DEFAULT].renderer, (PTXRenderer, NIRRenderer)), "skip for now")
def test_uint_overflow(self): def test_uint_overflow(self):
if not dtypes.is_unsigned(self.DTYPE): raise unittest.SkipTest("only for unsigned") if not dtypes.is_unsigned(self.DTYPE): raise unittest.SkipTest("only for unsigned")
v = dtypes.max(self.DTYPE) v = dtypes.max(self.DTYPE)
@@ -261,7 +262,7 @@ class TestFloatDType(TestDType):
class TestDoubleDType(TestDType): class TestDoubleDType(TestDType):
DTYPE = dtypes.double DTYPE = dtypes.double
@unittest.skipIf((CI and Device.DEFAULT in {"CUDA", "NV"}) or \ @unittest.skipIf((CI and Device.DEFAULT in {"CUDA", "NV"}) or \
isinstance(Device[Device.DEFAULT].renderer, PTXRenderer), "conversion not supported on CI CUDA and PTX") # TODO: why not? isinstance(Device[Device.DEFAULT].renderer, (PTXRenderer, NIRRenderer)), "conversion not supported on CI CUDA, PTX, and NIR") # TODO: why not?
def test_float64_increased_precision(self): def test_float64_increased_precision(self):
for func in [ for func in [
lambda t: t.exp(), lambda t: t.exp(),
+9 -5
View File
@@ -6,6 +6,7 @@ from tinygrad.tensor import _to_np_dtype
from tinygrad.device import is_dtype_supported from tinygrad.device import is_dtype_supported
from tinygrad.runtime.ops_python import from_storage_scalar from tinygrad.runtime.ops_python import from_storage_scalar
from tinygrad.renderer.ptx import PTXRenderer from tinygrad.renderer.ptx import PTXRenderer
from tinygrad.renderer.nir import NIRRenderer
import numpy as np import numpy as np
import pytest import pytest
from hypothesis import assume, given, strategies as strat, settings, HealthCheck from hypothesis import assume, given, strategies as strat, settings, HealthCheck
@@ -29,8 +30,8 @@ unary_operations = [(Tensor.exp, np.exp), (Tensor.log, np.log), (Tensor.sin, np.
# TODO: enable this (this is a dtype issue) # TODO: enable this (this is a dtype issue)
#binary_operations.append(operator.truediv) #binary_operations.append(operator.truediv)
# TODO: CI CUDA segfaults on sin, WEBGPU sin is not precise enough for large numbers # TODO: CI CUDA segfaults on sin, WEBGPU and NIR sines are not precise enough for large numbers
if (getenv("MOCKGPU") and Device.DEFAULT in {"NV", "CUDA"}) or Device.DEFAULT == "WEBGPU": if (getenv("MOCKGPU") and Device.DEFAULT in {"NV", "CUDA"}) or Device.DEFAULT == "WEBGPU" or isinstance(Device[Device.DEFAULT].renderer, NIRRenderer):
unary_operations.remove((Tensor.sin, np.sin)) unary_operations.remove((Tensor.sin, np.sin))
unary_operations.remove((Tensor.cos, np.cos)) unary_operations.remove((Tensor.cos, np.cos))
@@ -74,7 +75,10 @@ def universal_test_unary(a, dtype, op):
out: Tensor = op[0](ta) out: Tensor = op[0](ta)
tensor_value = out.numpy() tensor_value = out.numpy()
numpy_value = op[1](ta.numpy()) numpy_value = op[1](ta.numpy())
if dtype in dtypes.fp8s: numpy_value = truncate[dtype](numpy_value) if dtype in dtypes.fp8s:
# cuda cast f32 inf to f8 MAX, amd cast it to nan(E4M3)/inf(E5M2)
if math.isinf(numpy_value): return
numpy_value = truncate[dtype](numpy_value)
if dtype in dtypes.floats: if dtype in dtypes.floats:
atol, rtol = { dtypes.float16:(1e-3, 1e-2), dtypes.bfloat16:(1e-3, 2e-2), atol, rtol = { dtypes.float16:(1e-3, 1e-2), dtypes.bfloat16:(1e-3, 2e-2),
dtypes.fp8e4m3:(1e-1, 1e-1), dtypes.fp8e5m2: (1.0, 5e-1)}.get(dtype, (1e-6, 1e-5)) dtypes.fp8e4m3:(1e-1, 1e-1), dtypes.fp8e5m2: (1.0, 5e-1)}.get(dtype, (1e-6, 1e-5))
@@ -184,8 +188,8 @@ class TestDTypeALU(unittest.TestCase):
@given(ht.int32, ht.int32, ht.float32, strat.sampled_from(integer_binary_operations), strat.sampled_from(binary_operations)) @given(ht.int32, ht.int32, ht.float32, strat.sampled_from(integer_binary_operations), strat.sampled_from(binary_operations))
def test_int32_midcast_float(self, a, b, c, op1, op2): universal_test_midcast(a, b, c, op1, op2, dtypes.int32, dtypes.float32) def test_int32_midcast_float(self, a, b, c, op1, op2): universal_test_midcast(a, b, c, op1, op2, dtypes.int32, dtypes.float32)
# Metal and CUDA and HIP behave differently than numpy in CI for overflows # Metal and CUDA and HIP and NIR behave differently than numpy in CI for overflows
skip_overflow = CI and Device.DEFAULT in {"AMD", "NV", "CUDA"} skip_overflow = (CI and Device.DEFAULT in {"AMD", "NV", "CUDA"}) or isinstance(Device[Device.DEFAULT].renderer, NIRRenderer)
@given(strat.floats(width=32, min_value=0, max_value=10.0) if skip_overflow else ht.float32, @given(strat.floats(width=32, min_value=0, max_value=10.0) if skip_overflow else ht.float32,
strat.floats(width=32, min_value=0, max_value=10.0) if skip_overflow else ht.float32, strat.floats(width=32, min_value=0, max_value=10.0) if skip_overflow else ht.float32,
ht.int32, strat.sampled_from(binary_operations), strat.sampled_from(integer_binary_operations)) ht.int32, strat.sampled_from(binary_operations), strat.sampled_from(integer_binary_operations))
+4 -2
View File
@@ -26,8 +26,9 @@ import unittest
import numpy as np import numpy as np
import torch import torch
from tinygrad import Tensor, dtypes, nn from tinygrad import Tensor, dtypes, nn
from tinygrad.device import is_dtype_supported from tinygrad.device import Device, is_dtype_supported
from tinygrad.helpers import getenv from tinygrad.helpers import getenv
from tinygrad.renderer.nir import NIRRenderer
MOCKGPU = getenv("MOCKGPU") MOCKGPU = getenv("MOCKGPU")
@@ -206,7 +207,8 @@ class TestUOpValidationIssue(unittest.TestCase):
# these fail with UOp verification error. # these fail with UOp verification error.
# we want more of these with diverse errors! # we want more of these with diverse errors!
@unittest.skipIf((not is_dtype_supported(dtypes.long)) or MOCKGPU, "hangs gpuocelot") @unittest.skipIf((not is_dtype_supported(dtypes.long)) or MOCKGPU or isinstance(Device[Device.DEFAULT].renderer, NIRRenderer),
"hangs gpuocelot, NIR cannot render")
def test_tensor_index_overflow(self): def test_tensor_index_overflow(self):
val = Tensor([1]) val = Tensor([1])
big = val.expand(2**31 + 3) big = val.expand(2**31 + 3)
+1 -1
View File
@@ -51,7 +51,7 @@ class TestFusionOp(unittest.TestCase):
a = Tensor(val) a = Tensor(val)
for _ in range(24): a = Tensor.stack(a, a)[0] for _ in range(24): a = Tensor.stack(a, a)[0]
sched = a.schedule() sched = a.schedule()
self.assertEqual(len(sched), 1) self.assertEqual(len(sched), 0)
self.assertLess(time.perf_counter()-st, 2.0) self.assertLess(time.perf_counter()-st, 2.0)
def test_recursive_reshape(self): def test_recursive_reshape(self):
-1
View File
@@ -52,7 +52,6 @@ class TestImageDType(unittest.TestCase):
assert isinstance(it.uop.base.realized.dtype, ImageDType) assert isinstance(it.uop.base.realized.dtype, ImageDType)
np.testing.assert_equal(tst, it.numpy()) np.testing.assert_equal(tst, it.numpy())
@unittest.expectedFailure # this isn't supported anymore, CAST to ImageDType stays ImageDType
def test_image_cast_and_back_collapses(self): def test_image_cast_and_back_collapses(self):
data = Tensor.randn(9*27*4).realize() data = Tensor.randn(9*27*4).realize()
tst = data.numpy() tst = data.numpy()
+7 -6
View File
@@ -41,7 +41,7 @@ class TestLinearizer(unittest.TestCase):
def _test_no_nested_ranges(self, lins, skip=None): def _test_no_nested_ranges(self, lins, skip=None):
for l in lins: for l in lins:
range_in_acc = flatten([[x for x in u.src if x.op is Ops.RANGE] for u in l.uops if u.op is Ops.DEFINE_REG]) range_in_acc = flatten([[x for x in u.src if x.op is Ops.RANGE] for u in l.uops if u.op is Ops.DEFINE_REG])
ranges = [u.op for u in l.uops if (u.op is Ops.RANGE and u in range_in_acc) or (u.op is Ops.ENDRANGE and u.src[0] in range_in_acc)] ranges = [u.op for u in l.uops if (u.op is Ops.RANGE and u in range_in_acc) or (u.op is Ops.END and u.src[0] in range_in_acc)]
for i,u in enumerate(ranges): for i,u in enumerate(ranges):
if skip and i in skip: continue if skip and i in skip: continue
assert ranges[i-1] != u, f"multireduce nested the ranges! {ranges[i-1], {u}}" assert ranges[i-1] != u, f"multireduce nested the ranges! {ranges[i-1], {u}}"
@@ -205,7 +205,7 @@ class TestLinearizer(unittest.TestCase):
# the uops graph is DEFINE_REG -> 4x STORE 0.0 -> RANGE -> 4x ALU -> 4x STORE -> ENDRANGE # the uops graph is DEFINE_REG -> 4x STORE 0.0 -> RANGE -> 4x ALU -> 4x STORE -> ENDRANGE
uops = get_program(ast, opts=opt).uops uops = get_program(ast, opts=opt).uops
begin_range = [i for i, x in enumerate(uops) if x.op is Ops.RANGE][-1] begin_range = [i for i, x in enumerate(uops) if x.op is Ops.RANGE][-1]
end_range = [i for i, x in enumerate(uops) if x.op is Ops.ENDRANGE][0] end_range = [i for i, x in enumerate(uops) if x.op is Ops.END][0]
for i,u in enumerate(uops): print(i, u.op, [uops.index(s) for s in u.src], u.arg, u.dtype) for i,u in enumerate(uops): print(i, u.op, [uops.index(s) for s in u.src], u.arg, u.dtype)
for u in uops: for u in uops:
if u.op is Ops.STORE and isinstance(dt:=u.src[0].dtype, PtrDType) and dt.addrspace is AddrSpace.REG: if u.op is Ops.STORE and isinstance(dt:=u.src[0].dtype, PtrDType) and dt.addrspace is AddrSpace.REG:
@@ -214,8 +214,8 @@ class TestLinearizer(unittest.TestCase):
else: else:
assert u.src[1].op in GroupOp.ALU assert u.src[1].op in GroupOp.ALU
assert begin_range < uops.index(u) < end_range assert begin_range < uops.index(u) < end_range
# children of STORE are placed after ENDRANGE # children of END are placed after ENDRANGE
if any(x.op is Ops.STORE and x.src[1].op in GroupOp.ALU for x in u.src): if any(x.op is Ops.END and x.src[1].op in GroupOp.ALU for x in u.src):
assert end_range < uops.index(u) assert end_range < uops.index(u)
def test_grouped_dims(self): def test_grouped_dims(self):
@@ -393,14 +393,15 @@ class TestLinearizer(unittest.TestCase):
uops = get_program(ast, opts=opt).uops uops = get_program(ast, opts=opt).uops
local_stores = [u for u in uops if u.op is Ops.STORE and any(x.op is Ops.DEFINE_LOCAL for x in get_recursive(u.src[0]))] local_stores = [u for u in uops if u.op is Ops.STORE and any(x.op is Ops.DEFINE_LOCAL for x in get_recursive(u.src[0]))]
global_stores = [u for u in uops if u.op is Ops.STORE and any(x.op is Ops.DEFINE_GLOBAL for x in get_recursive(u.src[0]))] global_stores = [u for u in uops if u.op is Ops.STORE and any(x.op is Ops.DEFINE_GLOBAL for x in get_recursive(u.src[0]))]
barrier = [u for u in uops if u.op is Ops.BARRIER][0] barrier = [u for u in uops if u.op is Ops.BARRIER]
assert len(barrier) == 1
# check that the float4 cast collapses for all stores # check that the float4 cast collapses for all stores
for store in local_stores+global_stores: for store in local_stores+global_stores:
assert store.src[1].dtype.count > 1 # and store.src[2].op is not Ops.VECTORIZE assert store.src[1].dtype.count > 1 # and store.src[2].op is not Ops.VECTORIZE
# # check the children's vins # # check the children's vins
# TODO: src ALU are not the same, should it? # TODO: src ALU are not the same, should it?
# assert barrier.src == tuple(local_stores) # assert barrier.src == tuple(local_stores)
assert len([u for u in uops if u.op is Ops.IF and u.src[-1] == barrier]) == 1 assert len([u for u in uops if u.op is Ops.IF])
@unittest.skipUnless(Device[Device.DEFAULT].renderer.has_local, "test requires locals") @unittest.skipUnless(Device[Device.DEFAULT].renderer.has_local, "test requires locals")
@unittest.skipUnless(Device[Device.DEFAULT].renderer.has_shared, "test requires shared") @unittest.skipUnless(Device[Device.DEFAULT].renderer.has_shared, "test requires shared")
+1 -90
View File
@@ -4,12 +4,9 @@
import unittest import unittest
from tinygrad import Device, dtypes from tinygrad import Device, dtypes
from tinygrad.device import is_dtype_supported
from tinygrad.uop.ops import UOp, Ops, AxisType, KernelInfo from tinygrad.uop.ops import UOp, Ops, AxisType, KernelInfo
from tinygrad.shape.shapetracker import ShapeTracker, View
from tinygrad.codegen.opt.search import Opt, OptOps from tinygrad.codegen.opt.search import Opt, OptOps
from tinygrad.engine.realize import get_program from tinygrad.engine.realize import get_program
from tinygrad.renderer.ptx import PTXRenderer
class TestLinearizerFailure(unittest.TestCase): class TestLinearizerFailure(unittest.TestCase):
@unittest.skipUnless(Device.DEFAULT == "METAL", "only tested on METAL") @unittest.skipUnless(Device.DEFAULT == "METAL", "only tested on METAL")
@@ -26,95 +23,9 @@ class TestLinearizerFailure(unittest.TestCase):
c9 = UOp(Ops.DEFINE_GLOBAL, dtypes.uchar.ptr(47040000), arg=2, src=()) c9 = UOp(Ops.DEFINE_GLOBAL, dtypes.uchar.ptr(47040000), arg=2, src=())
c10 = c9.index((((c3*UOp.const(dtypes.index, 4704000))+c2)+(c6*UOp.const(dtypes.index, 784))).valid(UOp.const(dtypes.bool, True))).load() c10 = c9.index((((c3*UOp.const(dtypes.index, 4704000))+c2)+(c6*UOp.const(dtypes.index, 784))).valid(UOp.const(dtypes.bool, True))).load()
c11 = c5.alu(Ops.CMPNE, ((((c3*UOp.const(dtypes.index, 6000))+c6)+((c7*UOp.const(dtypes.index, 16))+c8)).alu(Ops.CMPLT, UOp.const(dtypes.index, 59999)).where(UOp.const(dtypes.int, 0), UOp.const(dtypes.int, 1)).reduce(c7, c8, arg=Ops.ADD)+UOp.const(dtypes.int, -1))).where(UOp.const(dtypes.uchar, 0), c10).reduce(c6, arg=Ops.ADD) c11 = c5.alu(Ops.CMPNE, ((((c3*UOp.const(dtypes.index, 6000))+c6)+((c7*UOp.const(dtypes.index, 16))+c8)).alu(Ops.CMPLT, UOp.const(dtypes.index, 59999)).where(UOp.const(dtypes.int, 0), UOp.const(dtypes.int, 1)).reduce(c7, c8, arg=Ops.ADD)+UOp.const(dtypes.int, -1))).where(UOp.const(dtypes.uchar, 0), c10).reduce(c6, arg=Ops.ADD)
c12 = c0.index((((c1*UOp.const(dtypes.index, 7840))+(c2*UOp.const(dtypes.index, 10)))+c3).valid(UOp.const(dtypes.bool, True))).store(c11, c1, c2, c3) c12 = c0.index((((c1*UOp.const(dtypes.index, 7840))+(c2*UOp.const(dtypes.index, 10)))+c3).valid(UOp.const(dtypes.bool, True))).store(c11).end(c1, c2, c3)
ast = c12.sink(arg=KernelInfo(name='test', axis_types=(), dont_use_locals=False, applied_opts=(Opt(op=OptOps.GROUP, axis=1, arg=16),), opts_to_apply=None)) ast = c12.sink(arg=KernelInfo(name='test', axis_types=(), dont_use_locals=False, applied_opts=(Opt(op=OptOps.GROUP, axis=1, arg=16),), opts_to_apply=None))
_ = get_program(ast, Device["METAL"].renderer) _ = get_program(ast, Device["METAL"].renderer)
class TestLinearizerDumb(unittest.TestCase):
@unittest.skipUnless(Device[Device.DEFAULT].renderer.has_local, "need local")
@unittest.skip("Ops.VALID no longer exists")
def test_max_simplify_and_cancel(self):
c0 = UOp(Ops.DEFINE_GLOBAL, dtypes.int.ptr(1000), arg=0, src=())
c1 = c0.view(ShapeTracker(views=(View(shape=(1000, 1), strides=(1, 0), offset=0, mask=None, contiguous=True),)))
c2 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(1000), arg=1, src=())
c3 = c2.view(ShapeTracker(views=(View(shape=(1000, 1), strides=(1, 0), offset=0, mask=None, contiguous=True),)))
c4 = c3.load()
c5 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(1), arg=2, src=())
c6 = c5.view(ShapeTracker(views=(View(shape=(1000, 1), strides=(0, 0), offset=0, mask=None, contiguous=False),)))
c7 = c6.load()
c8 = UOp(Ops.VIEW, dtypes.void, arg=ShapeTracker(views=(View(shape=(1000, 1), strides=(0, 0), offset=0, mask=None, contiguous=False),)), src=())
c9 = UOp(Ops.VIEW, dtypes.void, arg=ShapeTracker(views=(View(shape=(1001, 1999), strides=(0, 0), offset=0, mask=((0, 1001), (999, 1999)), contiguous=False), View(shape=(1000, 1000), strides=(1, 2000), offset=0, mask=None, contiguous=False))), src=())
c10 = UOp(Ops.VIEW, dtypes.void, arg=ShapeTracker(views=(View(shape=(1000, 1000), strides=(0, 0), offset=0, mask=None, contiguous=False),)), src=())
c11 = c1.store((c4.alu(Ops.CMPNE, c7).alu(Ops.CMPNE, UOp.const(dtypes.bool, True, src=c8)).cast(dtypes.int)*(c9.f(Ops.VALID, dtype=dtypes.bool).where(UOp.const(dtypes.int, -1, src=c10), UOp.const(dtypes.int, 0, src=c10)).f(Ops.REDUCE_AXIS, arg=(Ops.ADD, (1,)))+UOp.const(dtypes.int, 1000, src=c8))))
ast = c11.sink()
#opts = [Opt(op=OptOps.UNROLL, axis=0, arg=4), Opt(op=OptOps.LOCAL, axis=0, arg=8)]
opts = [Opt(op=OptOps.LOCAL, axis=0, arg=8)]
prg = get_program(ast, Device[Device.DEFAULT].renderer, opts)
print(prg.src)
assert prg.uops is not None and not any(uop.op is Ops.MAX for uop in prg.uops), "leftover MAX"
# this was a bug in embedding, someday we should fold this anyway
@unittest.skipUnless(is_dtype_supported(dtypes.half), f"half dtype not supported on {Device.DEFAULT}")
@unittest.skip("UOp.view is no longer supported")
def test_llama_embedding(self):
c0 = UOp(Ops.DEFINE_GLOBAL, dtypes.half.ptr(4096), arg=0, src=())
c1 = c0.view(ShapeTracker(views=(View(shape=(4096, 1, 1), strides=(1, 0, 0), offset=0, mask=None, contiguous=True),)))
c2 = UOp(Ops.VIEW, dtypes.void, arg=ShapeTracker(views=(View(shape=(32001, 63999), strides=(0, 0), offset=0, mask=((0, 32001), (31999, 63999)), contiguous=False), View(shape=(4096, 32000, 32000), strides=(0, 1, 64000), offset=0, mask=None, contiguous=False))), src=())
c3 = UOp(Ops.VIEW, dtypes.void, arg=ShapeTracker(views=(View(shape=(4096, 32000, 32000), strides=(0, 0, 0), offset=0, mask=None, contiguous=False),)), src=())
c4 = UOp(Ops.VIEW, dtypes.void, arg=ShapeTracker(views=(View(shape=(4096, 32000, 1), strides=(0, 0, 0), offset=0, mask=None, contiguous=False),)), src=())
c5 = UOp(Ops.DEFINE_GLOBAL, dtypes.int.ptr(1), arg=1, src=())
c6 = c5.view(ShapeTracker(views=(View(shape=(4096, 32000, 1), strides=(0, 0, 0), offset=0, mask=None, contiguous=False),)))
c7 = c6.load()
c8 = UOp(Ops.DEFINE_GLOBAL, dtypes.half.ptr(131072000), arg=2, src=())
c9 = c8.view(ShapeTracker(views=(View(shape=(4096, 32000, 1), strides=(1, 4096, 0), offset=0, mask=None, contiguous=False),)))
c10 = c9.load()
c11 = c1.store(((c2.f(Ops.VALID, dtype=dtypes.bool).where(UOp.const(dtypes.int, 1, src=c3), UOp.const(dtypes.int, 0, src=c3)).f(Ops.REDUCE_AXIS, arg=(Ops.ADD, (2,)))+UOp.const(dtypes.int, -1, src=c4)).alu(Ops.CMPNE, c7).alu(Ops.CMPNE, UOp.const(dtypes.bool, True, src=c4)).cast(dtypes.half)*c10).cast(dtypes.float).f(Ops.REDUCE_AXIS, arg=(Ops.ADD, (1,))).cast(dtypes.half))
ast = c11.sink()
prg = get_program(ast, Device[Device.DEFAULT].renderer)
print(prg.src)
@unittest.expectedFailure
@unittest.skipUnless(Device[Device.DEFAULT].renderer.supports_float4, "need float4")
def test_unrolled_float4_align(self):
c0 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(1), arg=0, src=())
c1 = c0.view(ShapeTracker(views=(View(shape=(1, 1), strides=(0, 0), offset=0, mask=None, contiguous=True),)))
c2 = UOp(Ops.DEFINE_GLOBAL, dtypes.long.ptr(18), arg=1, src=())
c3 = c2.view(ShapeTracker(views=(View(shape=(3, 6), strides=(6, 1), offset=0, mask=None, contiguous=True),)))
c4 = c3.load()
c5 = UOp(Ops.VIEW, dtypes.void, arg=ShapeTracker(views=(View(shape=(3, 6), strides=(0, 0), offset=0, mask=None, contiguous=False),)), src=())
c6 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(18), arg=2, src=())
c7 = c6.view(ShapeTracker(views=(View(shape=(3, 6), strides=(6, 1), offset=0, mask=None, contiguous=True),)))
c8 = c7.load()
c9 = c1.store(c4.alu(Ops.CMPNE, UOp.const(dtypes.long, -1, src=c5)).alu(Ops.CMPNE, UOp.const(dtypes.bool, True, src=c5)).where(UOp.const(dtypes.float, 0.0, src=c5), c8).f(Ops.REDUCE_AXIS, arg=(Ops.ADD, (0, 1))))
ast = c9.sink()
opts = [Opt(op=OptOps.UNROLL, axis=0, arg=0)]
prg = get_program(ast, Device[Device.DEFAULT].renderer, opts)
print(prg.src)
load_idxs = [x.src[1] for x in prg.uops if x.op is Ops.LOAD and x.src[0].arg == 2]
assert load_idxs[0] < load_idxs[1], f"first loaded idx {load_idxs[0].arg} then {load_idxs[1].arg}!"
@unittest.expectedFailure
@unittest.skipUnless(Device[Device.DEFAULT].renderer.supports_float4, "need float4")
@unittest.skipIf(isinstance(Device[Device.DEFAULT].renderer, PTXRenderer), "this is somehow correct in PTX")
def test_upcasted_stores_out_of_order(self):
c0 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(9360), arg=0, src=())
c1 = c0.view(ShapeTracker(views=(View(shape=(4, 5, 13, 1, 1, 1, 1, 1, 4, 3, 3), strides=(2340, 468, 36, 0, 0, 0, 0, 0, 9, 3, 1), offset=0, mask=None, contiguous=True),)))
c2 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(144), arg=1, src=())
c3 = c2.view(ShapeTracker(views=(View(shape=(4, 5, 13, 1, 1, 1, 4, 1, 4, 3, 3), strides=(0, 0, 0, 0, 0, 0, 1, 0, 4, 48, 16), offset=0, mask=None, contiguous=False),)))
c4 = c3.load()
c5 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(1040), arg=2, src=())
c6 = c5.view(ShapeTracker(views=(View(shape=(4, 5, 13, 1, 1, 1, 4, 1, 4, 3, 3), strides=(260, 13, 1, 0, 0, 0, 65, 0, 0, 0, 0), offset=0, mask=None, contiguous=False),)))
c7 = c6.load()
c8 = c1.store((c4*c7).f(Ops.REDUCE_AXIS, arg=(Ops.ADD, (6,))))
ast = c8.sink()
opts = [Opt(op=OptOps.UPCAST, axis=3, arg=0), Opt(op=OptOps.UPCAST, axis=2, arg=0)]
prg = get_program(ast, Device[Device.DEFAULT].renderer, opts)
print(prg.src)
store_idxs = [x.src[1] for x in prg.uops if x.op is Ops.STORE]
for i in range(len(store_idxs) - 1):
first_bounds = store_idxs[i].vmin+store_idxs[i].vmax
next_bounds = store_idxs[i+1].vmin+store_idxs[i+1].vmax
assert first_bounds < next_bounds, f"first stored (max) idx {first_bounds} then {next_bounds}!"
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
+1 -1
View File
@@ -16,7 +16,7 @@ class TestLinearizerFailures(unittest.TestCase):
c7 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(64), arg=2, src=()) c7 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(64), arg=2, src=())
c8 = c7.index(c3).load() c8 = c7.index(c3).load()
c9 = ((((c6+(c8*UOp.const(dtypes.float, -1.0)))*(c6+(c8*UOp.const(dtypes.float, -1.0)))).reduce(c5, arg=Ops.ADD)*UOp.const(dtypes.float, 0.000390625))+UOp.const(dtypes.float, 1e-05)).sqrt().reciprocal() c9 = ((((c6+(c8*UOp.const(dtypes.float, -1.0)))*(c6+(c8*UOp.const(dtypes.float, -1.0)))).reduce(c5, arg=Ops.ADD)*UOp.const(dtypes.float, 0.000390625))+UOp.const(dtypes.float, 1e-05)).sqrt().reciprocal()
c10 = c0.index(c3).store(c9, c1, c2) c10 = c0.index(c3).store(c9).end(c1, c2)
ast = c10.sink() ast = c10.sink()
get_program(ast) get_program(ast)
+1 -1
View File
@@ -658,7 +658,7 @@ class TestMultiTensor(unittest.TestCase):
# it doesn't work like this anymore # it doesn't work like this anymore
# NOTE: this never failed in assign_multi, it failed tensor spec because MULTI was never pushed in the graph # NOTE: this never failed in assign_multi, it failed tensor spec because MULTI was never pushed in the graph
@unittest.expectedFailure @unittest.skip("this test is broken")
def test_mlb_assign_change_axis(self): def test_mlb_assign_change_axis(self):
t_none = Tensor.zeros((16, 16)).shard(devices_2).contiguous().realize() t_none = Tensor.zeros((16, 16)).shard(devices_2).contiguous().realize()
t_zero = Tensor.ones((16, 16)).shard(devices_2, axis=0) t_zero = Tensor.ones((16, 16)).shard(devices_2, axis=0)
+13 -14
View File
@@ -2,7 +2,7 @@ import time, math, unittest, functools, platform, warnings
import numpy as np import numpy as np
from typing import List, Callable from typing import List, Callable
import torch import torch
from tinygrad.helpers import getenv, IMAGE, DEBUG, CI, Context, CPU_LLVM, AMD_LLVM from tinygrad.helpers import getenv, IMAGE, DEBUG, CI, Context, CPU_LLVM, CPU_LVP, AMD_LLVM
from tinygrad import Tensor, Device, dtypes from tinygrad import Tensor, Device, dtypes
from tinygrad.tensor import _to_np_dtype from tinygrad.tensor import _to_np_dtype
from tinygrad.device import is_dtype_supported from tinygrad.device import is_dtype_supported
@@ -698,8 +698,8 @@ class TestOps(unittest.TestCase):
def test_pow_zero_tensor(self): def test_pow_zero_tensor(self):
helper_test_op(None, lambda x,y: x**y, vals=[[0.0], [0.0]]) helper_test_op(None, lambda x,y: x**y, vals=[[0.0], [0.0]])
# TODO: fix WEBGPU # TODO: fix WEBGPU and LVP
if Device.DEFAULT != "WEBGPU": if Device.DEFAULT != "WEBGPU" and not CPU_LVP:
helper_test_op(None, lambda x,y: x**y, vals=[[0.0], [0.3]]) helper_test_op(None, lambda x,y: x**y, vals=[[0.0], [0.3]])
helper_test_op(None, lambda x,y: x**y, vals=[[0.0], [-0.3]]) helper_test_op(None, lambda x,y: x**y, vals=[[0.0], [-0.3]])
def test_pow_zero_const(self): def test_pow_zero_const(self):
@@ -830,6 +830,7 @@ class TestOps(unittest.TestCase):
self.assertEqual(a, b) self.assertEqual(a, b)
self.assertEqual(Tensor(-1).contiguous().idiv(4).item(), 0) # NOTE this is trunc-div behaviour self.assertEqual(Tensor(-1).contiguous().idiv(4).item(), 0) # NOTE this is trunc-div behaviour
@unittest.skipIf(getenv("NV_NAK"), "MUFU.SIN is not accurate enough")
def test_sin(self): def test_sin(self):
helper_test_op([(45,65)], lambda x: x.sin()) helper_test_op([(45,65)], lambda x: x.sin())
helper_test_op([()], lambda x: x.sin()) helper_test_op([()], lambda x: x.sin())
@@ -839,6 +840,7 @@ class TestOps(unittest.TestCase):
helper_test_op(None, lambda x: x.sin(), vals=[[1e1, 1e2, 1e3, 1e4, 1e5, 1e6, -1e1, -1e2, -1e3, -1e4, -1e5, -1e6]], helper_test_op(None, lambda x: x.sin(), vals=[[1e1, 1e2, 1e3, 1e4, 1e5, 1e6, -1e1, -1e2, -1e3, -1e4, -1e5, -1e6]],
atol=3e-3, rtol=3e-3, grad_atol=3e-3, grad_rtol=3e-3) atol=3e-3, rtol=3e-3, grad_atol=3e-3, grad_rtol=3e-3)
@unittest.skipIf(Device.DEFAULT == "WEBGPU" and platform.system() == "Windows", "Not accurate enough with DirectX backend") @unittest.skipIf(Device.DEFAULT == "WEBGPU" and platform.system() == "Windows", "Not accurate enough with DirectX backend")
@unittest.skipIf(getenv("NV_NAK"), "MUFU.SIN is not accurate enough")
def test_cos(self): def test_cos(self):
helper_test_op([(45,65)], lambda x: x.cos()) helper_test_op([(45,65)], lambda x: x.cos())
helper_test_op([()], lambda x: x.cos()) helper_test_op([()], lambda x: x.cos())
@@ -847,6 +849,7 @@ class TestOps(unittest.TestCase):
helper_test_op(None, lambda x: x.cos(), vals=[[1e1, 1e2, 1e3, 1e4, 1e5, 1e6, -1e1, -1e2, -1e3, -1e4, -1e5, -1e6]], helper_test_op(None, lambda x: x.cos(), vals=[[1e1, 1e2, 1e3, 1e4, 1e5, 1e6, -1e1, -1e2, -1e3, -1e4, -1e5, -1e6]],
atol=3e-3, rtol=3e-3, grad_atol=3e-3, grad_rtol=3e-3) atol=3e-3, rtol=3e-3, grad_atol=3e-3, grad_rtol=3e-3)
@unittest.skipIf(Device.DEFAULT == "WEBGPU" and platform.system() == "Windows", "Not accurate enough with DirectX backend") @unittest.skipIf(Device.DEFAULT == "WEBGPU" and platform.system() == "Windows", "Not accurate enough with DirectX backend")
@unittest.skipIf(getenv("NV_NAK"), "MUFU.SIN is not accurate enough")
def test_tan(self): def test_tan(self):
# NOTE: backward has much higher diff with input close to pi/2 and -pi/2 # NOTE: backward has much higher diff with input close to pi/2 and -pi/2
helper_test_op([(45,65)], lambda x: x.tan(), low=-1.5, high=1.5) helper_test_op([(45,65)], lambda x: x.tan(), low=-1.5, high=1.5)
@@ -2599,17 +2602,13 @@ class TestOps(unittest.TestCase):
lambda x: torch.nn.functional.avg_pool2d(x, kernel_size=(111,28)), lambda x: torch.nn.functional.avg_pool2d(x, kernel_size=(111,28)),
lambda x: Tensor.avg_pool2d(x, kernel_size=(111,28)), rtol=1e-5) lambda x: Tensor.avg_pool2d(x, kernel_size=(111,28)), rtol=1e-5)
def test_avg_pool3d_failure(self): def test_avg_pool3d(self):
with Context(NOOPT=0): # TODO: AMD_LLVM has larger atol
helper_test_op([(1,1,16,16,16)], # TODO: PYTHON=1 backward hangs?
lambda x: torch.nn.functional.avg_pool3d(x, kernel_size=(8,8,8), stride=5, padding=1, count_include_pad=False), atol = 1e-2 if AMD_LLVM else 1e-6
lambda x: Tensor.avg_pool2d(x, kernel_size=(8,8,8), stride=5, padding=1, count_include_pad=False), rtol=1e-5, forward_only=True) helper_test_op([(1,1,16,16,16)],
lambda x: torch.nn.functional.avg_pool3d(x, kernel_size=(8,8,8), stride=5, padding=1, count_include_pad=False),
def test_avg_pool3d_noopt(self): lambda x: Tensor.avg_pool2d(x, kernel_size=(8,8,8), stride=5, padding=1, count_include_pad=False), atol=atol, rtol=1e-5, forward_only=True)
with Context(NOOPT=1):
helper_test_op([(1,1,16,16,16)],
lambda x: torch.nn.functional.avg_pool3d(x, kernel_size=(8,8,8), stride=5, padding=1, count_include_pad=False),
lambda x: Tensor.avg_pool2d(x, kernel_size=(8,8,8), stride=5, padding=1, count_include_pad=False), rtol=1e-5, forward_only=True)
def test_interpolate_linear(self): def test_interpolate_linear(self):
for in_sz, out_sz in [((52,),(29,)), ((29,),(52,))]: for in_sz, out_sz in [((52,),(29,)), ((29,),(52,))]:
+25 -20
View File
@@ -5,7 +5,6 @@ from tinygrad import Tensor, Device, dtypes
from tinygrad.nn.optim import Adam, SGD, AdamW, Muon from tinygrad.nn.optim import Adam, SGD, AdamW, Muon
from tinygrad.helpers import CI from tinygrad.helpers import CI
from tinygrad.device import is_dtype_supported from tinygrad.device import is_dtype_supported
from extra.torch_muon import SingleDeviceMuon as TorchMuon
np.random.seed(1337) np.random.seed(1337)
x_init = np.random.randn(1,4).astype(np.float32) x_init = np.random.randn(1,4).astype(np.float32)
@@ -58,12 +57,11 @@ class TestOptim(unittest.TestCase):
def _test_sgd(self, steps, opts, atol, rtol): self._test_optim(SGD, torch.optim.SGD, steps, opts, atol, rtol) def _test_sgd(self, steps, opts, atol, rtol): self._test_optim(SGD, torch.optim.SGD, steps, opts, atol, rtol)
def _test_adam(self, steps, opts, atol, rtol): self._test_optim(Adam, torch.optim.Adam, steps, opts, atol, rtol) def _test_adam(self, steps, opts, atol, rtol): self._test_optim(Adam, torch.optim.Adam, steps, opts, atol, rtol)
def _test_adamw(self, steps, opts, atol, rtol): self._test_optim(AdamW, torch.optim.AdamW, steps, opts, atol, rtol) def _test_adamw(self, steps, opts, atol, rtol): self._test_optim(AdamW, torch.optim.AdamW, steps, opts, atol, rtol)
#TODO: use torch.muon when it comes out def _test_muon(self, steps, opts, atol, rtol): self._test_optim(Muon, torch.optim.Muon, steps, opts, atol, rtol)
def _test_muon(self, steps, opts, atol, rtol): self._test_optim(Muon, TorchMuon, steps, opts, atol, rtol)
def test_multistep_sgd_high_lr_teeny(self): self._test_sgd(2, {'lr': 1.1, 'teeny': True}, 1e-6, 1e-5) def test_multistep_sgd_high_lr_teeny(self): self._test_sgd(2, {'lr': 1.1, 'teeny': True}, 1e-6, 1e-5)
def test_multistep_adam_high_lr_teeny(self): self._test_adam(2, {'lr': 1.1, 'teeny': True}, 2e-4, 5e-4) def test_multistep_adam_high_lr_teeny(self): self._test_adam(2, {'lr': 1.1, 'teeny': True}, 2e-4, 5e-4)
def test_multistep_muon_high_lr_teeny(self): self._test_muon(2, {'lr': 1.1, 'teeny': True}, 2e-4, 5e-4) def test_multistep_muon_high_lr_teeny(self): self._test_muon(2, {'lr': 1.1, 'teeny': True}, 1e-2, 5e-4)
def test_sgd(self): self._test_sgd(1, {'lr': 0.001}, 1e-6, 0) def test_sgd(self): self._test_sgd(1, {'lr': 0.001}, 1e-6, 0)
def test_sgd_high_lr(self): self._test_sgd(1, {'lr': 10}, 1e-6, 1e-5) def test_sgd_high_lr(self): self._test_sgd(1, {'lr': 10}, 1e-6, 1e-5)
@@ -87,27 +85,34 @@ class TestOptim(unittest.TestCase):
def test_multistep_sgd_high_lr_nesterov_momentum_wd(self): def test_multistep_sgd_high_lr_nesterov_momentum_wd(self):
self._test_sgd(10, {'lr': 9, 'momentum': 0.9, 'nesterov': True, 'weight_decay': 0.1}, 1e-5, 3e-4) self._test_sgd(10, {'lr': 9, 'momentum': 0.9, 'nesterov': True, 'weight_decay': 0.1}, 1e-5, 3e-4)
def test_muon(self): self._test_muon(1, {'lr': 0.001}, 1e-6, 0) def test_muon(self): self._test_muon(1, {'lr': 0.001}, 1e-3, 0)
def test_muon_high_lr(self): self._test_muon(1, {'lr': 10}, 1e-6, 3e-4) # TODO: disabled due to big atol
def test_muon_wd(self): self._test_muon(1, {'lr': 0.001, 'weight_decay': 0.01}, 1e-6, 0) # def test_muon_high_lr(self): self._test_muon(1, {'lr': 10}, 1e-6, 3e-4)
def test_muon_high_lr_wd(self): self._test_muon(1, {'lr': 10, 'weight_decay': 0.01}, 1e-6, 5e-4) def test_muon_wd(self): self._test_muon(1, {'lr': 0.001, 'weight_decay': 0.01}, 1e-3, 3e-4)
# TODO: disabled due to big atol
# def test_muon_high_lr_wd(self): self._test_muon(1, {'lr': 10, 'weight_decay': 0.01}, 1e-6, 5e-4)
# NOTE: momentum set to 0.95 by default, nesterov set to True by default # NOTE: momentum set to 0.95 by default, nesterov set to True by default
def test_multistep_muon_momentum_wd(self): self._test_muon(10, {'lr': 0.001, 'weight_decay': 0.01}, 1e-5, 0) def test_multistep_muon_momentum_wd(self): self._test_muon(10, {'lr': 0.001, 'weight_decay': 0.01}, 3e-3, 0)
# ns defaults are numerically unstable, but it is tolerable in real training (see nsteps/nparam tests) # ns defaults are numerically unstable, but it is tolerable in real training (see nsteps/nparam tests)
def test_multistep_muon_high_lr_momentum_wd(self): self._test_muon(10, {'lr': 10, 'weight_decay': 0.01}, 1e-1, 3e-4) # TODO: disabled due to big atol
def test_multistep_muon_no_nesterov_momentum(self): self._test_muon(10, {'lr': 0.001, 'nesterov': False}, 1e-5, 0) # def test_multistep_muon_high_lr_momentum_wd(self): self._test_muon(10, {'lr': 10, 'weight_decay': 0.01}, 1e-1, 3e-4)
def test_multistep_muon_high_lr_no_nesterov_momentum(self): self._test_muon(10, {'lr': 10, 'nesterov': False}, 0.5e-1, 1e-1) def test_multistep_muon_no_nesterov_momentum(self): self._test_muon(10, {'lr': 0.001, 'nesterov': False}, 1e-3, 0)
# TODO: disabled due to big atol
# def test_multistep_muon_high_lr_no_nesterov_momentum(self): self._test_muon(10, {'lr': 10, 'nesterov': False}, 5e-2, 1e-1)
def test_muon_ns_steps(self): self._test_muon(1, {'lr': 0.001, 'ns_steps': 3}, 1e-6, 0) def test_muon_ns_steps(self): self._test_muon(1, {'lr': 0.001, 'ns_steps': 3}, 1e-4, 0)
def test_muon_high_lr_ns_steps(self): self._test_muon(1, {'lr': 10, 'ns_steps': 3}, 1e-5, 3e-4) # TODO: disabled due to big atol
def test_muon_ns_params(self): self._test_muon(1, {'lr': 0.001,'ns_params': (2.0,-1.5,0.5)}, 1e-6, 0) # def test_muon_high_lr_ns_steps(self): self._test_muon(1, {'lr': 10, 'ns_steps': 3}, 1e-5, 3e-4)
def test_muon_high_lr_ns_params(self): self._test_muon(1, {'lr': 10,'ns_params': (2.0,-1.5,0.5)}, 1e-5, 3e-4) def test_muon_ns_coefficients(self): self._test_muon(1, {'lr': 0.001,'ns_coefficients': (2.0,-1.5,0.5)}, 1e-5, 3e-4)
# TODO: disabled due to big atol
# def test_muon_high_lr_ns_coefficients(self): self._test_muon(1, {'lr': 10,'ns_coefficients': (2.0,-1.5,0.5)}, 1e-5, 3e-4)
def test_muon_momentum_wd_ns_steps_ns_params(self): def test_muon_momentum_wd_ns_steps_ns_coefficients(self):
self._test_muon(10, {'lr': 0.001, 'momentum': 0.90, 'weight_decay': 0.01, 'ns_steps': 3, 'ns_params': (2.0,-1.5,0.5)}, 1e-5, 0) self._test_muon(10, {'lr': 0.001, 'momentum': 0.90, 'weight_decay': 0.01, 'ns_steps': 3, 'ns_coefficients': (2.0,-1.5,0.5)}, 1e-4, 0)
def test_multistep_muon_high_lr_momentum_wd_ns_steps_ns_params(self): # TODO: disabled due to big atol
self._test_muon(10, {'lr': 10, 'momentum': 0.90, 'weight_decay': 0.01, 'ns_steps': 3, 'ns_params': (2.0,-1.5,0.5)}, 1e-5, 3e-4) # def test_multistep_muon_high_lr_momentum_wd_ns_steps_ns_coefficients(self):
# self._test_muon(10, {'lr': 10, 'momentum': 0.90, 'weight_decay': 0.01, 'ns_steps': 3, 'ns_coefficients': (2.0,-1.5,0.5)}, 1e-5, 3e-4)
def test_adam(self): self._test_adam(1, {'lr': 0.001}, 1e-5, 0) def test_adam(self): self._test_adam(1, {'lr': 0.001}, 1e-5, 0)
def test_adam_high_lr(self): self._test_adam(1, {'lr': 10}, 1e-4, 1e-4) def test_adam_high_lr(self): self._test_adam(1, {'lr': 10}, 1e-4, 1e-4)
+2 -2
View File
@@ -1,6 +1,6 @@
import unittest import unittest
from tinygrad import Tensor, Device from tinygrad import Tensor, Device
from tinygrad.helpers import CPU_LLVM from tinygrad.helpers import CPU_LLVM, CPU_LVP
from tinygrad.codegen.opt import Opt, OptOps from tinygrad.codegen.opt import Opt, OptOps
from tinygrad.engine.realize import get_program from tinygrad.engine.realize import get_program
@@ -12,7 +12,7 @@ class TestOpts(unittest.TestCase):
out = (a+b).contiguous(arg=opts) out = (a+b).contiguous(arg=opts)
s = out.schedule() s = out.schedule()
self.assertEqual(s[-1].ast.arg.opts_to_apply, opts) self.assertEqual(s[-1].ast.arg.opts_to_apply, opts)
if Device.DEFAULT in {"CPU", "CL", "METAL"} and not CPU_LLVM: if Device.DEFAULT in {"CPU", "CL", "METAL"} and not CPU_LLVM and not CPU_LVP:
prg = get_program(s[-1].ast) prg = get_program(s[-1].ast)
self.assertIn('float4', prg.src) self.assertIn('float4', prg.src)
+12 -6
View File
@@ -92,7 +92,9 @@ class TestProfiler(unittest.TestCase):
# assert evs[i].st > evs[i-1].en, "timestamp not aranged" # assert evs[i].st > evs[i-1].en, "timestamp not aranged"
def test_profile_multidev(self): def test_profile_multidev(self):
d1 = Device[f"{Device.DEFAULT}:1"] try: d1 = Device[f"{Device.DEFAULT}:1"]
except Exception as e: self.skipTest(f"second device not available {e}")
buf1 = Buffer(Device.DEFAULT, 2, dtypes.float, options=BufferSpec(nolru=True)).ensure_allocated() buf1 = Buffer(Device.DEFAULT, 2, dtypes.float, options=BufferSpec(nolru=True)).ensure_allocated()
buf2 = Buffer(f"{Device.DEFAULT}:1", 2, dtypes.float, options=BufferSpec(nolru=True)).ensure_allocated() buf2 = Buffer(f"{Device.DEFAULT}:1", 2, dtypes.float, options=BufferSpec(nolru=True)).ensure_allocated()
@@ -109,7 +111,8 @@ class TestProfiler(unittest.TestCase):
assert evs[0].is_copy, "kernel should be copy" assert evs[0].is_copy, "kernel should be copy"
def test_profile_multidev_transfer(self): def test_profile_multidev_transfer(self):
d1 = Device[f"{Device.DEFAULT}:1"] try: d1 = Device[f"{Device.DEFAULT}:1"]
except Exception as e: self.skipTest(f"second device not available {e}")
buf1 = Tensor.randn(10, 10, device=f"{Device.DEFAULT}:0").realize() buf1 = Tensor.randn(10, 10, device=f"{Device.DEFAULT}:0").realize()
with helper_collect_profile(TestProfiler.d0, d1) as profile: with helper_collect_profile(TestProfiler.d0, d1) as profile:
@@ -122,7 +125,8 @@ class TestProfiler(unittest.TestCase):
@unittest.skipIf(Device.DEFAULT in "METAL" or (MOCKGPU and Device.DEFAULT == "AMD"), "AMD mockgpu does not support queue wait interrupts") @unittest.skipIf(Device.DEFAULT in "METAL" or (MOCKGPU and Device.DEFAULT == "AMD"), "AMD mockgpu does not support queue wait interrupts")
def test_profile_graph(self): def test_profile_graph(self):
d1 = Device[f"{Device.DEFAULT}:1"] try: d1 = Device[f"{Device.DEFAULT}:1"]
except Exception as e: self.skipTest(f"second device not available {e}")
def f(a): def f(a):
x = (a + 1).realize() x = (a + 1).realize()
@@ -145,7 +149,9 @@ class TestProfiler(unittest.TestCase):
@unittest.skipIf(CI or not issubclass(type(Device[Device.DEFAULT]), HCQCompiled), "skip CI") @unittest.skipIf(CI or not issubclass(type(Device[Device.DEFAULT]), HCQCompiled), "skip CI")
def test_dev_jitter_matrix(self): def test_dev_jitter_matrix(self):
dev_cnt = 6 dev_cnt = 6
devs = [Device[f"{Device.DEFAULT}:{i}"] for i in range(dev_cnt)] try: devs = [Device[f"{Device.DEFAULT}:{i}"] for i in range(dev_cnt)]
except Exception as e: self.skipTest(f"multiple devices not available {e}")
for dev in devs: dev.synchronize() for dev in devs: dev.synchronize()
for dev in devs: dev._at_profile_finalize() for dev in devs: dev._at_profile_finalize()
@@ -217,9 +223,9 @@ class TestProfiler(unittest.TestCase):
Tensor.realize(a, b) Tensor.realize(a, b)
profile, _ = helper_profile_filter_device(profile, TestProfiler.d0.device) profile, _ = helper_profile_filter_device(profile, TestProfiler.d0.device)
exec_points = [e for e in profile if isinstance(e, ProfilePointEvent) and e.name == "exec"] exec_points = [e for e in profile if isinstance(e, ProfilePointEvent) and e.name == "exec"]
range_events = [e for e in profile if isinstance(e, ProfileRangeEvent)] range_events = [e for e in profile if isinstance(e, ProfileRangeEvent) and not e.is_copy]
self.assertEqual(len(exec_points), len(range_events), 2) self.assertEqual(len(exec_points), len(range_events), 2)
self.assertEqual(len(dedup(e.key for e in exec_points)), 1) self.assertEqual(len(dedup(e.arg['name'] for e in exec_points)), 1)
self.assertEqual(len(dedup(e.arg['metadata'] for e in exec_points)), 1) self.assertEqual(len(dedup(e.arg['metadata'] for e in exec_points)), 1)
if __name__ == "__main__": if __name__ == "__main__":
+11 -16
View File
@@ -72,7 +72,7 @@ class TestQuantizeOnnxCPU(unittest.TestCase):
out_file = get_quantized_model(sz) out_file = get_quantized_model(sz)
run_onnx = OnnxRunner(out_file) run_onnx = OnnxRunner(out_file)
inp = Tensor(np.random.uniform(size=(sz, sz)).astype(np.float32)) inp = Tensor(np.random.uniform(size=(sz, sz)).astype(np.float32))
with Context(DONT_REALIZE_EXPAND=1, QUANTIZE=1): with Context(QUANTIZE=1):
sched = run_onnx({"input":inp})["output"].schedule() sched = run_onnx({"input":inp})["output"].schedule()
ei = lower_schedule_item(sched[-2]) ei = lower_schedule_item(sched[-2])
daccs = [u for u in ei.prg.p.uops if u.op is Ops.DEFINE_REG] daccs = [u for u in ei.prg.p.uops if u.op is Ops.DEFINE_REG]
@@ -86,8 +86,7 @@ class TestQuantizeOnnx(unittest.TestCase):
# divide is ~1500-2000 without reduce_range, 750-900 with it # divide is ~1500-2000 without reduce_range, 750-900 with it
out_file = get_quantized_model(sz) out_file = get_quantized_model(sz)
run_onnx_jit, _ = load_onnx_model(out_file) run_onnx_jit, _ = load_onnx_model(out_file)
with Context(DONT_REALIZE_EXPAND=1): run_onnx_jit(input=Tensor(np.random.uniform(size=(sz, sz)).astype(np.float32)))
run_onnx_jit(input=Tensor(np.random.uniform(size=(sz, sz)).astype(np.float32)))
def test_prequant_conv2d_1x1(self): def test_prequant_conv2d_1x1(self):
X = Tensor(np.random.uniform(0, 255, size=(1, 32, 128, 128)).astype(np.uint8)) X = Tensor(np.random.uniform(0, 255, size=(1, 32, 128, 128)).astype(np.uint8))
@@ -109,11 +108,10 @@ class TestQuantizeOnnx(unittest.TestCase):
N = 512 N = 512
X = Tensor(np.random.uniform(0, 255, size=(N,N)).astype(xi)) X = Tensor(np.random.uniform(0, 255, size=(N,N)).astype(xi))
W = Tensor(np.random.uniform(0, 255, size=(N,N)).astype(wi)) W = Tensor(np.random.uniform(0, 255, size=(N,N)).astype(wi))
with Context(DONT_REALIZE_EXPAND=1): # this divide is interesting and forces the accumulator to actually be an int
# this divide is interesting and forces the accumulator to actually be an int out = (X.cast("int").matmul(W.cast("int"))//1000).cast("int8")
out = (X.cast("int").matmul(W.cast("int"))//1000).cast("int8") opts = [Opt(op=OptOps.UPCAST, axis=1, arg=128), Opt(op=OptOps.UNROLL, axis=0, arg=4)]
opts = [Opt(op=OptOps.UPCAST, axis=1, arg=128), Opt(op=OptOps.UNROLL, axis=0, arg=4)] sexec(out, opts)
sexec(out, opts)
def test_prequant_gemm_handcode(self): def test_prequant_gemm_handcode(self):
src = """typedef int int128 __attribute__((aligned(512),vector_size(512))); src = """typedef int int128 __attribute__((aligned(512),vector_size(512)));
@@ -203,14 +201,12 @@ class TestQuantizeOnnx(unittest.TestCase):
def test_prequant_gemm_intacc(self, xi=np.uint8, wi=np.uint8, replace_src=None, N=512, clip=True, opts=None): def test_prequant_gemm_intacc(self, xi=np.uint8, wi=np.uint8, replace_src=None, N=512, clip=True, opts=None):
X = Tensor(m1:=(np.random.uniform(0, 255, size=(N,N)).astype(xi))).realize() X = Tensor(m1:=(np.random.uniform(0, 255, size=(N,N)).astype(xi))).realize()
W = Tensor(m2:=(np.random.uniform(0, 255, size=(N,N)).astype(wi))).realize() W = Tensor(m2:=(np.random.uniform(0, 255, size=(N,N)).astype(wi))).realize()
# ugh, it's so broken with those casts. need DONT_REALIZE_EXPAND=1 python3 test/test_quantize_onnx.py TestQuantizeOnnx.test_prequant
tg_dtype = dtypes.int8 if xi == np.int8 else dtypes.uint8 tg_dtype = dtypes.int8 if xi == np.int8 else dtypes.uint8
with Context(DONT_REALIZE_EXPAND=1): out = (X.int().matmul(W.int())//1000)
out = (X.int().matmul(W.int())//1000) if clip: out = out.clip(dtypes.min(tg_dtype),dtypes.max(tg_dtype))
if clip: out = out.clip(dtypes.min(tg_dtype),dtypes.max(tg_dtype)) out = out.cast(tg_dtype)
out = out.cast(tg_dtype) opts = [Opt(op=OptOps.UPCAST, axis=1, arg=128), Opt(op=OptOps.UNROLL, axis=0, arg=4)] if opts is None else opts
opts = [Opt(op=OptOps.UPCAST, axis=1, arg=128), Opt(op=OptOps.UNROLL, axis=0, arg=4)] if opts is None else opts sexec(out, opts, replace_src, run_count=1)
sexec(out, opts, replace_src, run_count=1)
tout = out.numpy() tout = out.numpy()
mout = ((m1.astype(np.int32) @ m2.astype(np.int32)) // 1000) mout = ((m1.astype(np.int32) @ m2.astype(np.int32)) // 1000)
if clip: mout = mout.clip(dtypes.min(tg_dtype),dtypes.max(tg_dtype)) if clip: mout = mout.clip(dtypes.min(tg_dtype),dtypes.max(tg_dtype))
@@ -225,7 +221,6 @@ class TestQuantizeOnnx(unittest.TestCase):
def test_prequant_gemv(self): def test_prequant_gemv(self):
N = 2048 N = 2048
# ugh, it's so broken with those casts. need DONT_REALIZE_EXPAND=1 python3 test/test_quantize_onnx.py TestQuantizeOnnx.test_prequant
X = Tensor(np.random.uniform(0, 255, size=(1,N)).astype(np.uint8)).realize() X = Tensor(np.random.uniform(0, 255, size=(1,N)).astype(np.uint8)).realize()
W = Tensor(np.random.uniform(0, 255, size=(N,N)).astype(np.uint8)).realize() W = Tensor(np.random.uniform(0, 255, size=(N,N)).astype(np.uint8)).realize()
#out = X.cast(dtypes.int) @ W.cast(dtypes.int) #out = X.cast(dtypes.int) @ W.cast(dtypes.int)
+2 -1
View File
@@ -6,6 +6,7 @@ from tinygrad.helpers import getenv, CI, OSX
from tinygrad.device import is_dtype_supported from tinygrad.device import is_dtype_supported
from tinygrad.engine.realize import lower_schedule, CompiledRunner from tinygrad.engine.realize import lower_schedule, CompiledRunner
from tinygrad.renderer.ptx import PTXRenderer from tinygrad.renderer.ptx import PTXRenderer
from tinygrad.renderer.nir import NIRRenderer
from test.helpers import not_support_multi_device from test.helpers import not_support_multi_device
import numpy as np import numpy as np
@@ -100,7 +101,7 @@ class TestRandomness(unittest.TestCase):
np.testing.assert_allclose(jr, r) np.testing.assert_allclose(jr, r)
@unittest.skipIf(isinstance(Device[Device.DEFAULT].renderer, PTXRenderer), "fails with PTX") @unittest.skipIf(isinstance(Device[Device.DEFAULT].renderer, (NIRRenderer, PTXRenderer)), "PTX and NIR use pointer arithmetic")
def test_threefry_doesnt_use_long(self): def test_threefry_doesnt_use_long(self):
for (_,ei) in lower_schedule(Tensor.rand(20).schedule()): for (_,ei) in lower_schedule(Tensor.rand(20).schedule()):
if isinstance(ei.prg, CompiledRunner): if isinstance(ei.prg, CompiledRunner):
+87 -40
View File
@@ -1,7 +1,9 @@
import unittest import unittest
from tinygrad import Tensor, nn from tinygrad import Tensor, nn, Device
from tinygrad.helpers import Context, GlobalCounters from tinygrad.helpers import Context, GlobalCounters, CI, getenv, PCONTIG
from tinygrad.uop.ops import graph_rewrite, PatternMatcher, UPat, Ops from tinygrad.uop.ops import graph_rewrite, PatternMatcher, UPat, Ops
from tinygrad.renderer.ptx import PTXRenderer
from tinygrad.renderer.nir import NIRRenderer
class TestRangeifyAssign(unittest.TestCase): class TestRangeifyAssign(unittest.TestCase):
def test_assign_permuted(self): def test_assign_permuted(self):
@@ -17,8 +19,90 @@ class TestRangeifyAssign(unittest.TestCase):
self.assertListEqual(lst, lst3) self.assertListEqual(lst, lst3)
self.assertListEqual(lst2, B.permute(1, 0).tolist()) self.assertListEqual(lst2, B.permute(1, 0).tolist())
class TestRangeifyEdgeCase(unittest.TestCase):
def test_matmul_relu_cat(self):
a = Tensor.ones(100, 512).contiguous().realize()
c = Tensor.ones(1, 512).contiguous().realize()
cm = Tensor.ones(512, 512)
c = c @ cm
c = c.relu()
res = Tensor.cat(a, c, dim=0)
self.assertEqual(res.numpy()[-1, :16].tolist(), [512] * 16)
if getenv("BIG") > 2:
# llama 8B (8192)
BS, HEADS, SEQLEN, EMB = 4, 32, 8192, 128
elif getenv("BIG") > 1:
# llama 8B
BS, HEADS, SEQLEN, EMB = 4, 32, 2048, 128
elif getenv("BIG") > 0:
# bigger
BS, HEADS, SEQLEN, EMB = 4, 32, 1024, 64
else:
BS, HEADS, SEQLEN, EMB = 4, 2, 16, 8
def fa():
Tensor.manual_seed(1337)
with Context(DEBUG=0): q,k,v = [Tensor.rand(BS, HEADS, SEQLEN, EMB).contiguous().realize() for _ in range(3)]
GlobalCounters.reset()
return q.scaled_dot_product_attention(k, v)
def fa_bw():
Tensor.manual_seed(1337)
with Context(DEBUG=0):
q,k,v = [Tensor.rand(BS, HEADS, SEQLEN, EMB).contiguous().realize().requires_grad_() for _ in range(3)]
attn_output = nn.Linear(HEADS*EMB, HEADS*EMB, bias=False)
attn_output.weight.requires_grad_().realize()
target = Tensor.rand(BS, SEQLEN, HEADS*EMB).contiguous().realize()
GlobalCounters.reset()
attn = q.scaled_dot_product_attention(k, v).contiguous().contiguous_backward()
attn = attn.transpose(1, 2).reshape(BS, SEQLEN, -1)
out = attn_output(attn)
loss = (out - target).square().mean()
loss.backward()
#ret = [out, Tensor.stack(q.grad, k.grad, v.grad, dim=-1)]
#ret = [out, Tensor.stack(q.grad, k.grad, dim=-1), v.grad]
ret = [out, q.grad, k.grad, v.grad]
Tensor.realize(*ret)
return ret
@unittest.skipIf(isinstance(Device[Device.DEFAULT].renderer, (NIRRenderer, PTXRenderer)), "broken in LVP and PTX")
class TestPcontig(unittest.TestCase):
def test_flash_attention_bw(self):
with Context(PCONTIG=max(2, PCONTIG.value), DEBUG=2):
grads = fa_bw()
print(f"{GlobalCounters.global_ops/1e9:.2f} GFLOPS")
with Context(PCONTIG=0, DEBUG=2):
cmp_grads = fa_bw()
print(f"{GlobalCounters.global_ops/1e9:.2f} GFLOPS")
with Context(DEBUG=0):
mses = [((x-y)**2).sum().item() for x,y in zip(grads, cmp_grads)]
mse = sum(mses)
print(f"mse: {mse}")
self.assertLessEqual(mse, 1e-6)
def test_flash_attention(self):
with Context(PCONTIG=2, DEBUG=2):
ret = fa().realize()
print(f"{GlobalCounters.global_ops/1e9:.2f} GFLOPS")
with Context(DEBUG=2):
cmp = fa().realize()
print(f"{GlobalCounters.global_ops/1e9:.2f} GFLOPS")
with Context(DEBUG=0):
mse = ((cmp-ret)**2).sum().item()
print(f"mse: {mse}")
self.assertLessEqual(mse, 1e-6)
# *** non CI rangeify tests below this line ***
N = 256 N = 256
@unittest.skipIf(CI, "useless in CI, doesn't test anything")
class TestRangeifyOpt(unittest.TestCase): class TestRangeifyOpt(unittest.TestCase):
def test_randperm(self): def test_randperm(self):
Tensor.randperm(10000).realize() Tensor.randperm(10000).realize()
@@ -54,6 +138,7 @@ class TestRangeifyOpt(unittest.TestCase):
A = Tensor.empty(8,8,8,8).permute(1,0,3,2).flatten() A = Tensor.empty(8,8,8,8).permute(1,0,3,2).flatten()
A.sum().realize() A.sum().realize()
@unittest.skipIf(CI, "useless in CI, doesn't test anything")
class TestRangeify(unittest.TestCase): class TestRangeify(unittest.TestCase):
def test_groupnorm(self): def test_groupnorm(self):
# ranges 1 and 3 are merging # ranges 1 and 3 are merging
@@ -200,33 +285,6 @@ class TestRangeify(unittest.TestCase):
out = blk._feed_forward(x) out = blk._feed_forward(x)
out.realize() out.realize()
@unittest.skip("RANGEIFY=0 does nothing")
def test_flash_attention(self):
BS, HEADS, SEQLEN, EMB = 4, 2, 16, 8
# bigger
#BS, HEADS, SEQLEN, EMB = 4, 32, 1024, 64
# llama 8B
#BS, HEADS, SEQLEN, EMB = 4, 32, 2048, 128
def fa():
Tensor.manual_seed(1337)
with Context(DEBUG=0): q,k,v = [Tensor.rand(BS, HEADS, SEQLEN, EMB).contiguous().realize() for _ in range(3)]
return q.scaled_dot_product_attention(k, v).realize()
with Context(DEBUG=4):
GlobalCounters.reset()
ret = fa()
with Context(RANGEIFY=0):
with Context(DEBUG=2):
GlobalCounters.reset()
cmp = fa()
with Context(DEBUG=0):
mse = ((cmp-ret)**2).sum().item()
print(f"mse: {mse}")
self.assertLessEqual(mse, 1e-6)
# contiguous + reduce can support ranges? # contiguous + reduce can support ranges?
@unittest.skip("pm_rangeify no longer exists. test this in a different way") @unittest.skip("pm_rangeify no longer exists. test this in a different way")
@@ -280,16 +338,5 @@ class TestRangeifyPM(unittest.TestCase):
b = self.base.pad(((0,1),(0,0))).pad(((0,0),(0,1))) b = self.base.pad(((0,1),(0,0))).pad(((0,0),(0,1)))
self.assert_same(a, b) self.assert_same(a, b)
class TestRangeifyEdgeCase(unittest.TestCase):
def test_matmul_relu_cat(self):
a = Tensor.ones(100, 512).contiguous().realize()
c = Tensor.ones(1, 512).contiguous().realize()
cm = Tensor.ones(512, 512)
c = c @ cm
c = c.relu()
res = Tensor.cat(a, c, dim=0)
self.assertEqual(res.numpy()[-1, :16].tolist(), [512] * 16)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
+100 -168
View File
@@ -2,16 +2,14 @@
# schedule confirms the right things are capable of fusing # schedule confirms the right things are capable of fusing
# NOTE: this has overlap with external_test_opt.py # NOTE: this has overlap with external_test_opt.py
import unittest import unittest, functools
import numpy as np import numpy as np
import functools
from typing import cast from typing import cast
from hypothesis import assume, given, settings, strategies as strat from hypothesis import assume, given, settings, strategies as strat
from tinygrad import nn, dtypes, Device, Tensor, Variable from tinygrad import nn, dtypes, Device, Tensor, Variable
from tinygrad.device import is_dtype_supported from tinygrad.device import is_dtype_supported
from tinygrad.dtype import DType, ImageDType from tinygrad.dtype import DType, ImageDType
from tinygrad.shape.shapetracker import ShapeTracker
from tinygrad.uop.ops import UOp, Ops, GroupOp, UPat from tinygrad.uop.ops import UOp, Ops, GroupOp, UPat
from tinygrad.helpers import CI, DEBUG, SPLIT_REDUCEOP, GlobalCounters, Context, getenv, all_same, temp from tinygrad.helpers import CI, DEBUG, SPLIT_REDUCEOP, GlobalCounters, Context, getenv, all_same, temp
from tinygrad.schedule.rangeify import get_rangeify_map, Kernel from tinygrad.schedule.rangeify import get_rangeify_map, Kernel
@@ -32,7 +30,6 @@ def check_schedule(t:Tensor|list[Tensor]|UOp, allowed:int, to_prerealize:list[Te
# test lowering all the ScheduleItems to ExecItems # test lowering all the ScheduleItems to ExecItems
kernel_cnt = len([si for si,ei in lower_schedule(sched.copy()) if isinstance(ei.prg, CompiledRunner) or not filter_sink]) kernel_cnt = len([si for si,ei in lower_schedule(sched.copy()) if isinstance(ei.prg, CompiledRunner) or not filter_sink])
if kernel_cnt != allowed: if kernel_cnt != allowed:
return sched # allow different kernel count, TODO: fix the asserts
print(f"SCHEDULE ISSUE, expecting {allowed} got {len(sched)}") print(f"SCHEDULE ISSUE, expecting {allowed} got {len(sched)}")
if DEBUG >= 3: if DEBUG >= 3:
for i,s in enumerate(sched): for i,s in enumerate(sched):
@@ -118,8 +115,7 @@ class TestSchedule(unittest.TestCase):
c = a+b c = a+b
with self.assertRaisesRegex(RuntimeError, "all buffers must be on the same device"): check_schedule(c, 2) with self.assertRaisesRegex(RuntimeError, "all buffers must be on the same device"): check_schedule(c, 2)
@unittest.skipUnless(is_dtype_supported(dtypes.half) and getenv("CAST_AFTER_EXPAND"), "need half and CAST_AFTER_EXPAND=1") @unittest.skipUnless(is_dtype_supported(dtypes.half), "need half")
@unittest.skip("CAST_AFTER_EXPAND is not supported")
def test_expand_buffer_before_cast(self): def test_expand_buffer_before_cast(self):
a = Tensor.randn(4, 2, 1).realize().permute((1, 0, 2)) a = Tensor.randn(4, 2, 1).realize().permute((1, 0, 2))
b = a.cast(dtypes.half).expand((2, 4, 4))+2 b = a.cast(dtypes.half).expand((2, 4, 4))+2
@@ -129,7 +125,7 @@ class TestSchedule(unittest.TestCase):
def test_indexing_scalars_simple(self): def test_indexing_scalars_simple(self):
X = Tensor.randn(2, 2).realize() X = Tensor.randn(2, 2).realize()
xt = X[Tensor(1)][Tensor(0)] xt = X[Tensor(1)][Tensor(0)]
run_schedule(check_schedule(xt, 2)) run_schedule(check_schedule(xt, 1))
np.testing.assert_equal(xt.numpy(), X.numpy()[1][0]) np.testing.assert_equal(xt.numpy(), X.numpy()[1][0])
@unittest.skipIf(CI and Device.DEFAULT == "NV", "crashes on NV CI") @unittest.skipIf(CI and Device.DEFAULT == "NV", "crashes on NV CI")
@@ -149,31 +145,30 @@ class TestSchedule(unittest.TestCase):
assume(a<x and b<y) assume(a<x and b<y)
X = Tensor.randn(x, y).realize() X = Tensor.randn(x, y).realize()
xt = X[Tensor(a)][Tensor(b)] xt = X[Tensor(a)][Tensor(b)]
run_schedule(check_schedule(xt, 2)) run_schedule(check_schedule(xt, 1))
np.testing.assert_equal(xt.numpy(), X.numpy()[a][b]) np.testing.assert_equal(xt.numpy(), X.numpy()[a][b])
def test_push_pads_elementwise(self): def test_push_pads_elementwise(self):
x = Tensor.full((4,4), 2.).contiguous().realize() x = Tensor.full((4,4), 2.).contiguous().realize()
y = Tensor.full((4,4), 4.).contiguous().realize() y = Tensor.full((4,4), 4.).contiguous().realize()
z = (x.reciprocal()*y).pad((None, (0,1),)).sum() z = (x.reciprocal()*y).pad((None, (0,1),)).sum()
run_schedule(check_schedule(z, 2)) run_schedule(check_schedule(z, 1))
self.assertEqual(z.item(), 32) self.assertEqual(z.item(), 32)
def test_push_pads_contiguous(self): def test_push_pads_contiguous(self):
x = Tensor.full((4,1), 2.).contiguous() x = Tensor.full((4,1), 2.).contiguous()
y = Tensor.full((4,4), 4.).contiguous() y = Tensor.full((4,4), 4.).contiguous()
z = (x.reciprocal().expand(4,4)*y).pad((None, (0,1),)).sum() z = (x.reciprocal().expand(4,4)*y).pad((None, (0,1),)).sum()
run_schedule(check_schedule(z, 2, [x,y])) run_schedule(check_schedule(z, 1, [x,y]))
self.assertEqual(z.item(), 32) self.assertEqual(z.item(), 32)
def test_rand(self): def test_rand(self):
x = Tensor.rand(32) x = Tensor.rand(32)
check_schedule(x, 4, [Tensor._device_rng_counters[x.device]]) check_schedule(x, 1, [Tensor._device_rng_counters[x.device]])
def test_rand_recompute_arange(self): def test_rand_recompute_arange(self):
x = Tensor.rand(32) x = Tensor.rand(32)
with Context(DONT_GROUP_REDUCES=1): check_schedule(x, 1, [Tensor._device_rng_counters[x.device]])
check_schedule(x, 3, [Tensor._device_rng_counters[x.device]])
def test_empty_is_not_realized(self): def test_empty_is_not_realized(self):
a = Tensor.empty(10) a = Tensor.empty(10)
@@ -190,10 +185,7 @@ class TestSchedule(unittest.TestCase):
def test_simplify_padded_const(self): def test_simplify_padded_const(self):
a = Tensor.empty(1022).cummax(axis=0) a = Tensor.empty(1022).cummax(axis=0)
check_schedule(a, 5) check_schedule(a, 3)
# TODO: what is this testing?
#ast = sched[0].ast
#self.assertLessEqual(len([u for u in ast.toposort() if u.op is Ops.WHERE]), 6)
def test_basic_binop_fusion(self): def test_basic_binop_fusion(self):
a = Tensor.empty(10) a = Tensor.empty(10)
@@ -266,18 +258,17 @@ class TestSchedule(unittest.TestCase):
c = a.sum(axis=0) + b c = a.sum(axis=0) + b
check_schedule(c, 1) check_schedule(c, 1)
# not pushing permutes through reduces
def test_reduce_permute_binop_fusion(self): def test_reduce_permute_binop_fusion(self):
a = Tensor.empty(10,10,10) a = Tensor.empty(10,10,10)
b = Tensor.empty(10,10,1) b = Tensor.empty(10,10,1)
c = a.sum(axis=0, keepdim=True).permute(2,1,0) + b c = a.sum(axis=0, keepdim=True).permute(2,1,0) + b
check_schedule(c, 2) check_schedule(c, 1)
def test_allow_push_permutes(self): def test_allow_push_permutes(self):
a = Tensor.randn(10,10,10).realize() a = Tensor.randn(10,10,10).realize()
b = Tensor.randn(10,10,1).realize() b = Tensor.randn(10,10,1).realize()
c = a.sum(axis=0, keepdim=True).permute(2,1,0) + b c = a.sum(axis=0, keepdim=True).permute(2,1,0) + b
with Context(DONT_GROUP_REDUCES=1): run_schedule(check_schedule(c, 1)) run_schedule(check_schedule(c, 1))
np.testing.assert_allclose(c.numpy(), np.sum(a.numpy(), axis=0, keepdims=True).transpose(2,1,0)+b.numpy()) np.testing.assert_allclose(c.numpy(), np.sum(a.numpy(), axis=0, keepdims=True).transpose(2,1,0)+b.numpy())
def test_binop_early_reshape_reduce_fusion(self): def test_binop_early_reshape_reduce_fusion(self):
@@ -342,7 +333,7 @@ class TestSchedule(unittest.TestCase):
r1 = (x - r0).sum(axis=0).div(2) r1 = (x - r0).sum(axis=0).div(2)
out0 = r0 + y out0 = r0 + y
out1 = r1 + y out1 = r1 + y
schedule = check_schedule([out0, out1], 2) schedule = check_schedule([out0, out1], 3)
reduceops = [x for si in schedule for x in si.ast.toposort() if x.op in {Ops.REDUCE_AXIS, Ops.REDUCE}] reduceops = [x for si in schedule for x in si.ast.toposort() if x.op in {Ops.REDUCE_AXIS, Ops.REDUCE}]
self.assertEqual(len(reduceops), 2) # why is RANGEIFY different? self.assertEqual(len(reduceops), 2) # why is RANGEIFY different?
@@ -375,7 +366,7 @@ class TestSchedule(unittest.TestCase):
b = Tensor.full((4,), 2.).contiguous() b = Tensor.full((4,), 2.).contiguous()
first = a.assign(b) first = a.assign(b)
second = a.assign(b) second = a.assign(b)
check_schedule([first, second], 1) check_schedule([first, second], 2) # TODO: 1?
# NOTE: this is causing "LAZYCACHE=1 incorrectly reuses contiguous const" #4562 # NOTE: this is causing "LAZYCACHE=1 incorrectly reuses contiguous const" #4562
# should contiguous dedup? # should contiguous dedup?
@@ -455,7 +446,7 @@ class TestSchedule(unittest.TestCase):
@unittest.skipUnless(is_dtype_supported(dtypes.ulong), "Needs ulong") @unittest.skipUnless(is_dtype_supported(dtypes.ulong), "Needs ulong")
def test_fold_conv_batchnorm_optim(self): def test_fold_conv_batchnorm_optim(self):
# this is too high # this is too high
for optim, cnt in [(nn.optim.Adam, 30), (nn.optim.SGD, 11)]: for optim, cnt in [(nn.optim.Adam, 21), (nn.optim.SGD, 8)]:
with self.subTest(optim=optim.__name__): with self.subTest(optim=optim.__name__):
with Tensor.train(): with Tensor.train():
img = Tensor.ones(1,3,4,4) img = Tensor.ones(1,3,4,4)
@@ -476,7 +467,7 @@ class TestSchedule(unittest.TestCase):
fw = bn(x).contiguous_backward().relu().contiguous() fw = bn(x).contiguous_backward().relu().contiguous()
fw.sum().backward() fw.sum().backward()
# TODO: this is too many # TODO: this is too many
check_schedule([x.grad, bn.weight.grad, bn.bias.grad, fw], 10) check_schedule([x.grad, bn.weight.grad, bn.bias.grad, fw], 9)
def test_fold_conv_relu(self): def test_fold_conv_relu(self):
c1 = nn.Conv2d(3,16,3) c1 = nn.Conv2d(3,16,3)
@@ -519,9 +510,8 @@ class TestSchedule(unittest.TestCase):
img = Tensor.empty(64,64) img = Tensor.empty(64,64)
x = (img.sum(0) + img.sum(1)) x = (img.sum(0) + img.sum(1))
out = x.relu() out = x.relu()
check_schedule(out, 2) check_schedule(out, 1)
#@unittest.skip("failing in old lazy")
def test_push_permute_through_reshape(self): def test_push_permute_through_reshape(self):
a = Tensor.empty(16,16) a = Tensor.empty(16,16)
b = Tensor.empty(16,16) b = Tensor.empty(16,16)
@@ -555,7 +545,7 @@ class TestSchedule(unittest.TestCase):
c = a+b c = a+b
d = a.reshape(10,1)+b.reshape(10,1) d = a.reshape(10,1)+b.reshape(10,1)
out = c.sum() + d.sum() out = c.sum() + d.sum()
check_schedule(out, 2) check_schedule(out, 1)
def test_children_dont_push(self): def test_children_dont_push(self):
a = Tensor.empty(10, 10, 1) a = Tensor.empty(10, 10, 1)
@@ -563,7 +553,7 @@ class TestSchedule(unittest.TestCase):
d = (a+b).expand(10, 10, 10) d = (a+b).expand(10, 10, 10)
e = (a+b).permute(2,1,0) e = (a+b).permute(2,1,0)
f = d+e f = d+e
check_schedule(f, 2) check_schedule(f, 1)
# failing in new lazy # failing in new lazy
@unittest.skip("always fusing elementwise") @unittest.skip("always fusing elementwise")
@@ -602,13 +592,13 @@ class TestSchedule(unittest.TestCase):
e = c[0] * d e = c[0] * d
check_schedule(e, 1) check_schedule(e, 1)
def test_expand_nofuse(self): def test_expand_fuse(self):
a = Tensor.empty(1, 16) a = Tensor.empty(1, 16)
b = Tensor.empty(1, 16) b = Tensor.empty(1, 16)
c = a * b c = a * b
d = Tensor.empty(8192, 16) d = Tensor.empty(8192, 16)
e = c * d e = c * d
check_schedule(e, 2) check_schedule(e, 1)
# this is the failing case in openpilot...it's very simple like this # this is the failing case in openpilot...it's very simple like this
def test_image_conv_fusion(self): def test_image_conv_fusion(self):
@@ -626,7 +616,7 @@ class TestSchedule(unittest.TestCase):
# NOOP, 3 convs, contiguous # NOOP, 3 convs, contiguous
#check_schedule(x, 5) #check_schedule(x, 5)
check_schedule(x, 8) check_schedule(x, 7)
def test_image_conv_fusion_minimal(self): def test_image_conv_fusion_minimal(self):
b1 = Tensor.empty(16) b1 = Tensor.empty(16)
@@ -809,13 +799,13 @@ class TestSchedule(unittest.TestCase):
x = Tensor.empty(32, 32, 32) x = Tensor.empty(32, 32, 32)
y = Tensor.empty(32, 32) y = Tensor.empty(32, 32)
out = x.sum(axis=2).T+y out = x.sum(axis=2).T+y
check_schedule(out, 2) check_schedule(out, 1)
def test_two_elus_sum(self): def test_two_elus_sum(self):
x = Tensor.empty(32, 32) x = Tensor.empty(32, 32)
y = Tensor.empty(32, 32) y = Tensor.empty(32, 32)
out = x.sum(1).relu().elu() + y.sum(1).relu().elu() out = x.sum(1).relu().elu() + y.sum(1).relu().elu()
check_schedule(out, 2) check_schedule(out, 1)
@unittest.skipUnless(SPLIT_REDUCEOP, "Testing split reducop requires SPLIT_REDUCEOP") @unittest.skipUnless(SPLIT_REDUCEOP, "Testing split reducop requires SPLIT_REDUCEOP")
def test_preserve_multistage_reduce(self): def test_preserve_multistage_reduce(self):
@@ -828,7 +818,7 @@ class TestSchedule(unittest.TestCase):
def test_multistage_reduce(self): def test_multistage_reduce(self):
x = Tensor.empty(32, 32, 32) x = Tensor.empty(32, 32, 32)
out = x.sum(2).relu().sum(1) out = x.sum(2).relu().sum(1)
check_schedule(out, 2) check_schedule(out, 1)
def test_multistage_reduce_fork(self): def test_multistage_reduce_fork(self):
x = Tensor.empty(32, 32, 32) x = Tensor.empty(32, 32, 32)
@@ -844,7 +834,7 @@ class TestSchedule(unittest.TestCase):
z = y.matmul(x).sum() z = y.matmul(x).sum()
z.backward() z.backward()
out = x.grad.contiguous() out = x.grad.contiguous()
run_schedule(check_schedule(out, 2)) run_schedule(check_schedule(out, 1))
np.testing.assert_allclose(out.numpy(), np.ones((64,64))) np.testing.assert_allclose(out.numpy(), np.ones((64,64)))
def test_example_matmul_contig(self): def test_example_matmul_contig(self):
@@ -853,7 +843,7 @@ class TestSchedule(unittest.TestCase):
z = y.matmul(x).sum() z = y.matmul(x).sum()
z.backward() z.backward()
out = x.grad.contiguous() out = x.grad.contiguous()
run_schedule(check_schedule(out, 2)) run_schedule(check_schedule(out, 1))
np.testing.assert_allclose(out.numpy(), np.ones((64,64))) np.testing.assert_allclose(out.numpy(), np.ones((64,64)))
def test_example_matmul_same(self): def test_example_matmul_same(self):
@@ -861,7 +851,7 @@ class TestSchedule(unittest.TestCase):
z = x.matmul(x).sum() z = x.matmul(x).sum()
z.backward() z.backward()
out = x.grad.contiguous() out = x.grad.contiguous()
run_schedule(check_schedule(out, 2)) run_schedule(check_schedule(out, 1))
# NOTE: the gradient flows twice # NOTE: the gradient flows twice
np.testing.assert_allclose(out.numpy(), 2*np.ones((64,64))) np.testing.assert_allclose(out.numpy(), 2*np.ones((64,64)))
@@ -884,8 +874,7 @@ class TestSchedule(unittest.TestCase):
x = x.sum(1) x = x.sum(1)
x = x[:16] x = x[:16]
out = x + y out = x + y
# NOTE: this could be 1 kernel if we mask the store? check_schedule(out, 1)
check_schedule(out, 2)
def test_multireduce_shrink(self): def test_multireduce_shrink(self):
Tensor.manual_seed(0) Tensor.manual_seed(0)
@@ -897,8 +886,7 @@ class TestSchedule(unittest.TestCase):
b_out = b.sum(1) b_out = b.sum(1)
b_out = b_out[:16] b_out = b_out[:16]
out = a_out + b_out + c out = a_out + b_out + c
# run_schedule(check_schedule(out, 2)) # TODO: this should be 1 (can we make it 1 with the new linearizer?) run_schedule(check_schedule(out, 1))
run_schedule(check_schedule(out, 3))
np.testing.assert_allclose(out.numpy(), a.numpy().sum(axis=1)[:16] + b.numpy().sum(axis=1)[:16] + c.numpy(), atol=1e-4, rtol=1e-4) np.testing.assert_allclose(out.numpy(), a.numpy().sum(axis=1)[:16] + b.numpy().sum(axis=1)[:16] + c.numpy(), atol=1e-4, rtol=1e-4)
# broken due to const folding and two contiguous are different kernels # broken due to const folding and two contiguous are different kernels
@@ -915,7 +903,7 @@ class TestSchedule(unittest.TestCase):
out0 = a.sum() + 2 out0 = a.sum() + 2
out1 = a.sum() + 4 out1 = a.sum() + 4
out2 = out0 * out1 out2 = out0 * out1
run_schedule(check_schedule([out0, out1, out2], 1)) run_schedule(check_schedule([out0, out1, out2], 3)) # TODO: 1?
np.testing.assert_allclose(out0.numpy(), out0_np:=a.numpy().sum()+2, atol=1e-4, rtol=1e-6) np.testing.assert_allclose(out0.numpy(), out0_np:=a.numpy().sum()+2, atol=1e-4, rtol=1e-6)
np.testing.assert_allclose(out1.numpy(), out1_np:=a.numpy().sum()+4, atol=1e-4, rtol=1e-6) np.testing.assert_allclose(out1.numpy(), out1_np:=a.numpy().sum()+4, atol=1e-4, rtol=1e-6)
np.testing.assert_allclose(out2.numpy(), out0_np*out1_np, atol=1e-4, rtol=1e-6) np.testing.assert_allclose(out2.numpy(), out0_np*out1_np, atol=1e-4, rtol=1e-6)
@@ -926,7 +914,7 @@ class TestSchedule(unittest.TestCase):
out0 = a.sum().exp2() out0 = a.sum().exp2()
# out1 has two paths to a.sum() # out1 has two paths to a.sum()
out1 = a.sum() + out0 out1 = a.sum() + out0
run_schedule(check_schedule([out0, out1], 1)) run_schedule(check_schedule([out0, out1], 2)) # TODO: 1?
np.testing.assert_allclose(out0.numpy(), out0_np:=np.exp2(a.numpy().sum()), atol=1e-4, rtol=1e-4) np.testing.assert_allclose(out0.numpy(), out0_np:=np.exp2(a.numpy().sum()), atol=1e-4, rtol=1e-4)
np.testing.assert_allclose(out1.numpy(), a.numpy().sum()+out0_np, atol=1e-4, rtol=1e-6) np.testing.assert_allclose(out1.numpy(), a.numpy().sum()+out0_np, atol=1e-4, rtol=1e-6)
@@ -939,7 +927,7 @@ class TestSchedule(unittest.TestCase):
out2 = b.sum().exp2() out2 = b.sum().exp2()
out3 = b.sum() + out2 out3 = b.sum() + out2
# run_schedule(check_schedule([out0, out1, out2, out3], 1)) # run_schedule(check_schedule([out0, out1, out2, out3], 1))
run_schedule(check_schedule([out0, out1, out2, out3], 6)) run_schedule(check_schedule([out0, out1, out2, out3], 4))
np.testing.assert_allclose(out0.numpy(), np_out0:=np.exp2(a.numpy().sum()), atol=1e-4, rtol=1e-4) np.testing.assert_allclose(out0.numpy(), np_out0:=np.exp2(a.numpy().sum()), atol=1e-4, rtol=1e-4)
np.testing.assert_allclose(out1.numpy(), np_out1:=a.numpy().sum()+np_out0, atol=1e-4, rtol=1e-4) np.testing.assert_allclose(out1.numpy(), np_out1:=a.numpy().sum()+np_out0, atol=1e-4, rtol=1e-4)
np_b = (a.numpy() + np_out0 + np_out1) np_b = (a.numpy() + np_out0 + np_out1)
@@ -954,7 +942,7 @@ class TestSchedule(unittest.TestCase):
out0 = a.sum() + b.sum() + 2 out0 = a.sum() + b.sum() + 2
out1 = a.sum() + b.sum() + 4 out1 = a.sum() + b.sum() + 4
# run_schedule(check_schedule([out0, out1], 1)) # run_schedule(check_schedule([out0, out1], 1))
run_schedule(check_schedule([out0, out1], 4)) run_schedule(check_schedule([out0, out1], 2))
np.testing.assert_allclose(out0.numpy(), a.numpy().sum()+b.numpy().sum()+2, atol=1e-4, rtol=1e-4) np.testing.assert_allclose(out0.numpy(), a.numpy().sum()+b.numpy().sum()+2, atol=1e-4, rtol=1e-4)
np.testing.assert_allclose(out1.numpy(), a.numpy().sum()+b.numpy().sum()+4, atol=1e-4, rtol=1e-4) np.testing.assert_allclose(out1.numpy(), a.numpy().sum()+b.numpy().sum()+4, atol=1e-4, rtol=1e-4)
@@ -981,7 +969,7 @@ class TestSchedule(unittest.TestCase):
out1 = b.max() + out0*2 out1 = b.max() + out0*2
out2 = a.sum() + out1 out2 = a.sum() + out1
# run_schedule(check_schedule([out0, out1, out2], 1)) # run_schedule(check_schedule([out0, out1, out2], 1))
run_schedule(check_schedule([out0, out1, out2], 4)) run_schedule(check_schedule([out0, out1, out2], 3))
np.testing.assert_allclose(out0.numpy(), out0_np:=a.numpy().sum()+4, atol=1e-4, rtol=1e-6) np.testing.assert_allclose(out0.numpy(), out0_np:=a.numpy().sum()+4, atol=1e-4, rtol=1e-6)
np.testing.assert_allclose(out1.numpy(), out1_np:=b.numpy().max() + out0_np*2, atol=1e-4, rtol=1e-6) np.testing.assert_allclose(out1.numpy(), out1_np:=b.numpy().max() + out0_np*2, atol=1e-4, rtol=1e-6)
np.testing.assert_allclose(out2.numpy(), a.numpy().sum() + out1_np, atol=1e-4, rtol=1e-6) np.testing.assert_allclose(out2.numpy(), a.numpy().sum() + out1_np, atol=1e-4, rtol=1e-6)
@@ -1018,7 +1006,7 @@ class TestSchedule(unittest.TestCase):
b = Tensor.empty(10,) b = Tensor.empty(10,)
c = a.sum() + b[0] c = a.sum() + b[0]
d = a.sum() + 2 d = a.sum() + 2
check_schedule([c, d], 1) check_schedule([c, d], 2) # TODO: 1?
def test_reduce_multiple_paths_midshrink(self): def test_reduce_multiple_paths_midshrink(self):
a = Tensor.empty(4, 4) a = Tensor.empty(4, 4)
@@ -1047,7 +1035,7 @@ class TestSchedule(unittest.TestCase):
k = Tensor.randn(32,8,16,8).realize() k = Tensor.randn(32,8,16,8).realize()
v = Tensor.randn(32,8,16,8).realize() v = Tensor.randn(32,8,16,8).realize()
out = Tensor.scaled_dot_product_attention(q,k,v) out = Tensor.scaled_dot_product_attention(q,k,v)
run_schedule(check_schedule(out, 5)) run_schedule(check_schedule(out, 4))
if getenv("CHECK", 1): if getenv("CHECK", 1):
import torch import torch
compare = torch.nn.functional.scaled_dot_product_attention(torch.tensor(q.numpy()),torch.tensor(k.numpy()),torch.tensor(v.numpy())) compare = torch.nn.functional.scaled_dot_product_attention(torch.tensor(q.numpy()),torch.tensor(k.numpy()),torch.tensor(v.numpy()))
@@ -1055,7 +1043,7 @@ class TestSchedule(unittest.TestCase):
with Context(FUSE_ATTENTION=1): with Context(FUSE_ATTENTION=1):
out = Tensor.scaled_dot_product_attention(q,k,v) out = Tensor.scaled_dot_product_attention(q,k,v)
run_schedule(check_schedule(out, 1)) run_schedule(check_schedule(out, 4)) # TODO: should be 1?
if getenv("CHECK", 1): if getenv("CHECK", 1):
import torch import torch
compare = torch.nn.functional.scaled_dot_product_attention(torch.tensor(q.numpy()),torch.tensor(k.numpy()),torch.tensor(v.numpy())) compare = torch.nn.functional.scaled_dot_product_attention(torch.tensor(q.numpy()),torch.tensor(k.numpy()),torch.tensor(v.numpy()))
@@ -1068,7 +1056,7 @@ class TestSchedule(unittest.TestCase):
c = Tensor.randn(4, 32).realize() c = Tensor.randn(4, 32).realize()
out = (c * a.sum(-1, keepdim=True)).sum(-1) + (b * a.sum(-1, keepdim=True)).sum(-1) # a.sum has >1 children but should still fuse out = (c * a.sum(-1, keepdim=True)).sum(-1) + (b * a.sum(-1, keepdim=True)).sum(-1) # a.sum has >1 children but should still fuse
# run_schedule(check_schedule(out, 1)) # run_schedule(check_schedule(out, 1))
run_schedule(check_schedule(out, 3)) run_schedule(check_schedule(out, 2))
np.testing.assert_allclose(out.numpy(), \ np.testing.assert_allclose(out.numpy(), \
(c.numpy()*a.numpy().sum(axis=-1,keepdims=True)).sum(-1) + (b.numpy()*a.numpy().sum(axis=-1,keepdims=True)).sum(-1), atol=1e-4, rtol=1e-4) (c.numpy()*a.numpy().sum(axis=-1,keepdims=True)).sum(-1) + (b.numpy()*a.numpy().sum(axis=-1,keepdims=True)).sum(-1), atol=1e-4, rtol=1e-4)
@@ -1113,8 +1101,7 @@ class TestSchedule(unittest.TestCase):
x = Tensor.randn(4, 32).realize() x = Tensor.randn(4, 32).realize()
y = Tensor.randn(4, 32).realize() y = Tensor.randn(4, 32).realize()
out = y.sum(axis=-1) + x.sum(axis=-1) out = y.sum(axis=-1) + x.sum(axis=-1)
# run_schedule(check_schedule(out, 1)) run_schedule(check_schedule(out, 1))
run_schedule(check_schedule(out, 2))
np.testing.assert_allclose(out.numpy(), y.numpy().sum(axis=-1) + x.numpy().sum(axis=-1), atol=1e-4, rtol=1e-4) np.testing.assert_allclose(out.numpy(), y.numpy().sum(axis=-1) + x.numpy().sum(axis=-1), atol=1e-4, rtol=1e-4)
def test_multireduce_fusion_sequential(self): def test_multireduce_fusion_sequential(self):
@@ -1131,7 +1118,7 @@ class TestSchedule(unittest.TestCase):
y = Tensor.randn(4, 32).realize() y = Tensor.randn(4, 32).realize()
out = x.std(-1) + y.std(-1) out = x.std(-1) + y.std(-1)
# run_schedule(check_schedule(out, 1)) # run_schedule(check_schedule(out, 1))
run_schedule(check_schedule(out, 4)) run_schedule(check_schedule(out, 3))
np.testing.assert_allclose(out.numpy(), x.numpy().std(axis=-1, ddof=1) + y.numpy().std(axis=-1, ddof=1), atol=1e-4, rtol=1e-4) np.testing.assert_allclose(out.numpy(), x.numpy().std(axis=-1, ddof=1) + y.numpy().std(axis=-1, ddof=1), atol=1e-4, rtol=1e-4)
def test_multireduce_diffops_sequential(self): def test_multireduce_diffops_sequential(self):
@@ -1147,8 +1134,7 @@ class TestSchedule(unittest.TestCase):
x = Tensor.randn(4, 32).realize() x = Tensor.randn(4, 32).realize()
y = Tensor.randn(4, 32).realize() y = Tensor.randn(4, 32).realize()
out = x.sum(-1) + y.max(-1) out = x.sum(-1) + y.max(-1)
# run_schedule(check_schedule(out, 1)) run_schedule(check_schedule(out, 1))
run_schedule(check_schedule(out, 2))
np.testing.assert_allclose(out.numpy(), x.numpy().sum(axis=-1) + y.numpy().max(axis=-1), atol=1e-4, rtol=1e-4) np.testing.assert_allclose(out.numpy(), x.numpy().sum(axis=-1) + y.numpy().max(axis=-1), atol=1e-4, rtol=1e-4)
def test_multireduce_fusion_sequential_and_parallel(self): def test_multireduce_fusion_sequential_and_parallel(self):
@@ -1160,7 +1146,7 @@ class TestSchedule(unittest.TestCase):
np_mu = (x.numpy() - x.numpy().max(axis=-1, keepdims=True)).mean(axis=-1, keepdims=True) + \ np_mu = (x.numpy() - x.numpy().max(axis=-1, keepdims=True)).mean(axis=-1, keepdims=True) + \
(y.numpy() - y.numpy().max(axis=-1, keepdims=True)).mean(axis=-1, keepdims=True) (y.numpy() - y.numpy().max(axis=-1, keepdims=True)).mean(axis=-1, keepdims=True)
# run_schedule(check_schedule(out, 1)) # run_schedule(check_schedule(out, 1))
run_schedule(check_schedule(out, 6)) run_schedule(check_schedule(out, 5))
np.testing.assert_allclose(out[0].numpy(), np.sqrt(np.square(x.numpy() - np_mu).sum(-1)/x.shape[-1]), atol=1e-4, rtol=1e-4) np.testing.assert_allclose(out[0].numpy(), np.sqrt(np.square(x.numpy() - np_mu).sum(-1)/x.shape[-1]), atol=1e-4, rtol=1e-4)
np.testing.assert_allclose(out[1].numpy(), np.sqrt(np.square(y.numpy() - np_mu).sum(-1)/y.shape[-1]), atol=1e-4, rtol=1e-4) np.testing.assert_allclose(out[1].numpy(), np.sqrt(np.square(y.numpy() - np_mu).sum(-1)/y.shape[-1]), atol=1e-4, rtol=1e-4)
@@ -1169,8 +1155,7 @@ class TestSchedule(unittest.TestCase):
a,b = Tensor.randn(4, 64).realize(), Tensor.rand(64,8).realize() a,b = Tensor.randn(4, 64).realize(), Tensor.rand(64,8).realize()
c,d = Tensor.randn(4, 64).realize(), Tensor.rand(64,8).realize() c,d = Tensor.randn(4, 64).realize(), Tensor.rand(64,8).realize()
out = a@b + c@d out = a@b + c@d
# run_schedule(check_schedule(out, 1)) run_schedule(check_schedule(out, 1))
run_schedule(check_schedule(out, 2))
np.testing.assert_allclose(out.numpy(), a.numpy()@b.numpy() + c.numpy()@d.numpy(), atol=1e-4, rtol=1e-4) np.testing.assert_allclose(out.numpy(), a.numpy()@b.numpy() + c.numpy()@d.numpy(), atol=1e-4, rtol=1e-4)
def test_softmax_fusion(self): def test_softmax_fusion(self):
@@ -1181,17 +1166,15 @@ class TestSchedule(unittest.TestCase):
expected = (x_exp:=np.exp(x.numpy()-x.numpy().max(-1, keepdims=True)))/x_exp.sum(-1, keepdims=True) expected = (x_exp:=np.exp(x.numpy()-x.numpy().max(-1, keepdims=True)))/x_exp.sum(-1, keepdims=True)
np.testing.assert_allclose(out.numpy(), expected, atol=1e-4, rtol=1e-4) np.testing.assert_allclose(out.numpy(), expected, atol=1e-4, rtol=1e-4)
# TODO: rangeify stores the output in float32
@unittest.skipUnless(is_dtype_supported(dtypes.half), "need half") @unittest.skipUnless(is_dtype_supported(dtypes.half), "need half")
@unittest.expectedFailure
def test_softmax_upcast(self): def test_softmax_upcast(self):
# input half, softmax in float # input half, softmax in float
Tensor.manual_seed(0) Tensor.manual_seed(0)
x = Tensor.randn(4, 12, 64, 64, dtype=dtypes.half).realize() x = Tensor.randn(4, 12, 64, 64, dtype=dtypes.half).realize()
out = x.softmax(dtype=dtypes.float) out = x.softmax(dtype=dtypes.float)
sched = out.schedule() sched = out.schedule()
self.assertEqual(len(sched), 2) self.assertEqual(len(sched), 3)
self.assertEqual(sched[0].bufs[0].dtype, dtypes.half) self.assertEqual(sched[0].bufs[0].dtype, dtypes.float)
# input float, softmax in float # input float, softmax in float
Tensor.manual_seed(0) Tensor.manual_seed(0)
@@ -1223,12 +1206,12 @@ class TestSchedule(unittest.TestCase):
def test_scaled_dot_product_attention_fusion(self): def test_scaled_dot_product_attention_fusion(self):
x, y, z, m = (Tensor.empty(32, 8, 16, 16) for _ in range(4)) x, y, z, m = (Tensor.empty(32, 8, 16, 16) for _ in range(4))
out = Tensor.scaled_dot_product_attention(x, y, z, attn_mask=m) out = Tensor.scaled_dot_product_attention(x, y, z, attn_mask=m)
check_schedule(out, 5) check_schedule(out, 4)
def test_scaled_dot_product_attention_causal_fusion(self): def test_scaled_dot_product_attention_causal_fusion(self):
x, y, z = (Tensor.empty(32, 8, 16, 16) for _ in range(3)) x, y, z = (Tensor.empty(32, 8, 16, 16) for _ in range(3))
out = Tensor.scaled_dot_product_attention(x, y, z, is_causal=True) out = Tensor.scaled_dot_product_attention(x, y, z, is_causal=True)
check_schedule(out, 5) check_schedule(out, 4)
def test_adam_step_fusion(self): def test_adam_step_fusion(self):
with Tensor.train(): with Tensor.train():
@@ -1258,7 +1241,7 @@ class TestSchedule(unittest.TestCase):
opt = nn.optim.Adam(nn.state.get_parameters([c1, c2]), lr=1e-4) opt = nn.optim.Adam(nn.state.get_parameters([c1, c2]), lr=1e-4)
opt.zero_grad() opt.zero_grad()
c2(c1(img).relu()).relu().sum().backward() c2(c1(img).relu()).relu().sum().backward()
check_schedule(opt.schedule_step(), 20) check_schedule(opt.schedule_step(), 18)
def test_sgd_conv_fuse(self): def test_sgd_conv_fuse(self):
with Tensor.train(): with Tensor.train():
@@ -1268,7 +1251,7 @@ class TestSchedule(unittest.TestCase):
opt = nn.optim.SGD(nn.state.get_parameters(c1)) opt = nn.optim.SGD(nn.state.get_parameters(c1))
opt.zero_grad() opt.zero_grad()
c1(img).relu().sum().backward() c1(img).relu().sum().backward()
check_schedule(opt.schedule_step(), 3) check_schedule(opt.schedule_step(), 5) # TODO: 3?
def test_sgd_2convs_fuse(self): def test_sgd_2convs_fuse(self):
with Tensor.train(): with Tensor.train():
@@ -1304,7 +1287,7 @@ class TestSchedule(unittest.TestCase):
opt = nn.optim.SGD(nn.state.get_parameters([c1, c2, c3, c4])) opt = nn.optim.SGD(nn.state.get_parameters([c1, c2, c3, c4]))
opt.zero_grad() opt.zero_grad()
c4(c3(c2(c1(img).relu()).relu()).relu()).relu().sum().backward() c4(c3(c2(c1(img).relu()).relu()).relu()).relu().sum().backward()
check_schedule(opt.schedule_step(), 17) check_schedule(opt.schedule_step(), 15)
def test_sgd_4convs_fuse_conv_bw(self): def test_sgd_4convs_fuse_conv_bw(self):
with Tensor.train(): with Tensor.train():
@@ -1317,50 +1300,7 @@ class TestSchedule(unittest.TestCase):
opt = nn.optim.SGD(nn.state.get_parameters([c1, c2, c3, c4])) opt = nn.optim.SGD(nn.state.get_parameters([c1, c2, c3, c4]))
opt.zero_grad() opt.zero_grad()
c4(c3(c2(c1(img).relu()).relu()).relu()).relu().sum().backward() c4(c3(c2(c1(img).relu()).relu()).relu()).relu().sum().backward()
check_schedule(opt.schedule_step(), 14) check_schedule(opt.schedule_step(), 15)
@unittest.skipUnless(is_dtype_supported(dtypes.half), "need half")
@unittest.expectedFailure
def test_prefer_half_buffer(self):
x = Tensor.ones(4).contiguous().realize()
# y = Tensor.ones(4).contiguous().realize()
z = Tensor.ones(4, 4).contiguous().realize()
# should not create extra kernel if output will be realized anyways
dummy = x.sum().half().float()
check_schedule(dummy, 1)
dummy = x.sum().half().float().contiguous() + 1
check_schedule(dummy, 2)
# shared between two outputs
shared = x.sum().half().float()
a = shared * 2
b = shared * 3
sched = check_schedule([a, b], 3)
# store reduceop in half
self.assertEqual(sched[0].bufs[0].dtype, dtypes.half)
# fuse cast with the child kernel
self.assertEqual(sched[1].bufs[0].dtype, dtypes.float)
self.assertEqual(sched[2].bufs[0].dtype, dtypes.float)
# reduce
a = z.sum(axis=0).half().float().sum(axis=0)
sched = check_schedule(a, 2)
self.assertEqual(sched[0].bufs[0].dtype, dtypes.half)
self.assertEqual(sched[1].bufs[0].dtype, dtypes.float)
# expand
# expand will realize just after the .float(), so requires change to realize-before-expand
# normal = (x.sum().half().float().reshape(1) * y).sum()
# sched = check_schedule(normal, 2)
# for si in sched[:-1]: assert all(out.dtype == dtypes.half for out in si.outputs[:-1])
# parallel reduce
# a = x.sum().half().float() * y.sum().half().float()
# b = a + 1
# c = a + 2
# sched = check_schedule([b, c], 4)
# doesn't store either in half because it doesn't chase
def test_reduce_simple_chase(self): def test_reduce_simple_chase(self):
a = Tensor.empty(4, 4, 4) a = Tensor.empty(4, 4, 4)
@@ -1409,7 +1349,7 @@ class TestSchedule(unittest.TestCase):
c = Tensor.empty(16, ) c = Tensor.empty(16, )
r = a.sum(1) + c r = a.sum(1) + c
d = r[:4] * b d = r[:4] * b
check_schedule(d, 2) check_schedule(d, 1)
def test_multireduce_push_shrink_chase(self): def test_multireduce_push_shrink_chase(self):
Tensor.manual_seed(0) Tensor.manual_seed(0)
@@ -1419,22 +1359,20 @@ class TestSchedule(unittest.TestCase):
d = Tensor.randn(16, 16).realize() d = Tensor.randn(16, 16).realize()
r = a.sum(1) + c r = a.sum(1) + c
out = r[:4] * b + d.sum(1)[:4] out = r[:4] * b + d.sum(1)[:4]
# schedule = check_schedule(out, 2) schedule = check_schedule(out, 1)
schedule = check_schedule(out, 3)
run_schedule(schedule) run_schedule(schedule)
np.testing.assert_allclose(out.numpy(), (a.numpy().sum(1) + c.numpy())[:4] * b.numpy() + d.numpy().sum(1)[:4], atol=1e-4, rtol=1e-4) np.testing.assert_allclose(out.numpy(), (a.numpy().sum(1) + c.numpy())[:4] * b.numpy() + d.numpy().sum(1)[:4], atol=1e-4, rtol=1e-4)
def test_midreduce_nochase(self): def test_midreduce_nochase(self):
a = Tensor.empty(16, 16) a = Tensor.empty(16, 16)
b = (a.sum(0) + a.max(1)) + 2 b = (a.sum(0) + a.max(1)) + 2
check_schedule(b, 2) check_schedule(b, 1)
def test_multireduce_midreduce_nochase(self): def test_multireduce_midreduce_nochase(self):
Tensor.manual_seed(0) Tensor.manual_seed(0)
a = Tensor.randn(16, 16).realize() a = Tensor.randn(16, 16).realize()
b = (a.sum(0)+a.max(0) + a.max(1)+a.sum(1)) + 2 b = (a.sum(0)+a.max(0) + a.max(1)+a.sum(1)) + 2
# schedule = check_schedule(b, 2) schedule = check_schedule(b, 1)
schedule = check_schedule(b, 4)
run_schedule(schedule) run_schedule(schedule)
np.testing.assert_allclose(b.numpy(), a.numpy().sum(0)+a.numpy().max(0) + a.numpy().max(1)+a.numpy().sum(1)+2, atol=1e-4, rtol=1e-4) np.testing.assert_allclose(b.numpy(), a.numpy().sum(0)+a.numpy().max(0) + a.numpy().max(1)+a.numpy().sum(1)+2, atol=1e-4, rtol=1e-4)
@@ -1446,7 +1384,7 @@ class TestSchedule(unittest.TestCase):
c = a.sum() + 2 c = a.sum() + 2
d = (a.sum() - b.sum()) * 4 d = (a.sum() - b.sum()) * 4
# run_schedule(check_schedule([c, d], 1)) # run_schedule(check_schedule([c, d], 1))
run_schedule(check_schedule([c, d], 3)) run_schedule(check_schedule([c, d], 2))
np.testing.assert_allclose(c.numpy(), a.numpy().sum()+2, atol=1e-4, rtol=1e-4) np.testing.assert_allclose(c.numpy(), a.numpy().sum()+2, atol=1e-4, rtol=1e-4)
np.testing.assert_allclose(d.numpy(), (a.numpy().sum() - b.numpy().sum()) * 4, atol=1e-4, rtol=1e-4) np.testing.assert_allclose(d.numpy(), (a.numpy().sum() - b.numpy().sum()) * 4, atol=1e-4, rtol=1e-4)
@@ -1472,7 +1410,7 @@ class TestSchedule(unittest.TestCase):
e = c * d e = c * d
f = b.sum() - e f = b.sum() - e
# run_schedule(check_schedule([c, d, e, f], 1)) # run_schedule(check_schedule([c, d, e, f], 1))
run_schedule(check_schedule([c, d, e, f], 2)) run_schedule(check_schedule([c, d, e, f], 4))
np.testing.assert_allclose(c.numpy(), c_np:=a.numpy().sum()+2, atol=1e-4, rtol=1e-4) np.testing.assert_allclose(c.numpy(), c_np:=a.numpy().sum()+2, atol=1e-4, rtol=1e-4)
np.testing.assert_allclose(d.numpy(), d_np:=a.numpy().sum()*2, atol=1e-4, rtol=1e-4) np.testing.assert_allclose(d.numpy(), d_np:=a.numpy().sum()*2, atol=1e-4, rtol=1e-4)
np.testing.assert_allclose(e.numpy(), e_np:=c_np*d_np, atol=1e-4, rtol=1e-4) np.testing.assert_allclose(e.numpy(), e_np:=c_np*d_np, atol=1e-4, rtol=1e-4)
@@ -1487,7 +1425,7 @@ class TestSchedule(unittest.TestCase):
e = c * d e = c * d
f = (b - d).sum() - e f = (b - d).sum() - e
# run_schedule(check_schedule([c, d, e, f], 1)) # run_schedule(check_schedule([c, d, e, f], 1))
run_schedule(check_schedule([c, d, e, f], 5)) run_schedule(check_schedule([c, d, e, f], 4))
np.testing.assert_allclose(c.numpy(), c_np:=a.numpy().sum()+2, atol=1e-4, rtol=1e-4) np.testing.assert_allclose(c.numpy(), c_np:=a.numpy().sum()+2, atol=1e-4, rtol=1e-4)
np.testing.assert_allclose(d.numpy(), d_np:=a.numpy().sum()*2, atol=1e-4, rtol=1e-4) np.testing.assert_allclose(d.numpy(), d_np:=a.numpy().sum()*2, atol=1e-4, rtol=1e-4)
np.testing.assert_allclose(e.numpy(), e_np:=c_np*d_np, atol=1e-4, rtol=1e-4) np.testing.assert_allclose(e.numpy(), e_np:=c_np*d_np, atol=1e-4, rtol=1e-4)
@@ -1506,8 +1444,7 @@ class TestSchedule(unittest.TestCase):
a = Tensor.randn(3, 4, 5).realize() a = Tensor.randn(3, 4, 5).realize()
b = Tensor.randn(3, 4, 5).realize() b = Tensor.randn(3, 4, 5).realize()
out = (a.pad(((0, 1), (0, 1), (0, 1)), value=1.0).sum(keepdim=True)+b.pad(((0, 1), (0, 1), (0, 1)), value=1.0).sum()).contiguous() out = (a.pad(((0, 1), (0, 1), (0, 1)), value=1.0).sum(keepdim=True)+b.pad(((0, 1), (0, 1), (0, 1)), value=1.0).sum()).contiguous()
# run_schedule(check_schedule(out, 1)) run_schedule(check_schedule(out, 1))
run_schedule(check_schedule(out, 2))
np.testing.assert_allclose(out.numpy(), np.pad(a.numpy(), ((0, 1), (0, 1), (0, 1)), constant_values=1.0).sum(keepdims=True) + \ np.testing.assert_allclose(out.numpy(), np.pad(a.numpy(), ((0, 1), (0, 1), (0, 1)), constant_values=1.0).sum(keepdims=True) + \
np.pad(b.numpy(), ((0, 1), (0, 1), (0, 1)), constant_values=1.0).sum(), atol=1e-4, rtol=1e-4) np.pad(b.numpy(), ((0, 1), (0, 1), (0, 1)), constant_values=1.0).sum(), atol=1e-4, rtol=1e-4)
@@ -1515,7 +1452,7 @@ class TestSchedule(unittest.TestCase):
Tensor.manual_seed(0) Tensor.manual_seed(0)
a = Tensor.rand(3, 4, 5).realize() a = Tensor.rand(3, 4, 5).realize()
out = a.log2().pad(((0, 1), (0, 1), (0, 1)), value=1.0).sum().contiguous() out = a.log2().pad(((0, 1), (0, 1), (0, 1)), value=1.0).sum().contiguous()
run_schedule(check_schedule(out, 2)) run_schedule(check_schedule(out, 1))
np.testing.assert_allclose(out.numpy(), np.pad(np.log2(a.numpy()), ((0, 1), (0, 1), (0, 1)), constant_values=1.0).sum(), atol=1e-5, rtol=1e-6) np.testing.assert_allclose(out.numpy(), np.pad(np.log2(a.numpy()), ((0, 1), (0, 1), (0, 1)), constant_values=1.0).sum(), atol=1e-5, rtol=1e-6)
def test_multireduce_pad_reduce_unsafe(self): def test_multireduce_pad_reduce_unsafe(self):
@@ -1524,7 +1461,7 @@ class TestSchedule(unittest.TestCase):
b = Tensor.randn(3, 4, 5).abs().realize() b = Tensor.randn(3, 4, 5).abs().realize()
out = (a.log2().pad(((0, 1), (0, 1), (0, 1)), value=1.0).sum()+b).abs().log2().pad(((0, 1), (0, 1), (0, 1)), value=1.0).sum().contiguous() out = (a.log2().pad(((0, 1), (0, 1), (0, 1)), value=1.0).sum()+b).abs().log2().pad(((0, 1), (0, 1), (0, 1)), value=1.0).sum().contiguous()
# run_schedule(check_schedule(out, 1)) # run_schedule(check_schedule(out, 1))
run_schedule(check_schedule(out, 4)) run_schedule(check_schedule(out, 2))
np.testing.assert_allclose(out.numpy(), np.pad(np.log2(np.abs(np.pad(np.log2(a.numpy()), ((0, 1), (0, 1), (0, 1)), constant_values=1.0).sum() + \ np.testing.assert_allclose(out.numpy(), np.pad(np.log2(np.abs(np.pad(np.log2(a.numpy()), ((0, 1), (0, 1), (0, 1)), constant_values=1.0).sum() + \
b.numpy())), ((0, 1), (0, 1), (0, 1)), constant_values=1.0).sum(), atol=3e-4, rtol=1e-5) b.numpy())), ((0, 1), (0, 1), (0, 1)), constant_values=1.0).sum(), atol=3e-4, rtol=1e-5)
@@ -1538,7 +1475,7 @@ class TestSchedule(unittest.TestCase):
def test_shrink_pad_unsafe(self): def test_shrink_pad_unsafe(self):
a = Tensor.ones((3, )).contiguous().realize() a = Tensor.ones((3, )).contiguous().realize()
out = a.exp2().shrink(((0, 1),)).pad(((0, 1),)).contiguous() out = a.exp2().shrink(((0, 1),)).pad(((0, 1),)).contiguous()
run_schedule(check_schedule(out, 2)) run_schedule(check_schedule(out, 1))
np.testing.assert_equal(out.numpy(), [2, 0]) np.testing.assert_equal(out.numpy(), [2, 0])
def test_base_change_shrink_pad(self): def test_base_change_shrink_pad(self):
@@ -1546,7 +1483,7 @@ class TestSchedule(unittest.TestCase):
b = a.exp2() b = a.exp2()
c = b[:-1, :-1] c = b[:-1, :-1]
d = c.pad(((0, 1), (0, 1))) * 2 d = c.pad(((0, 1), (0, 1))) * 2
run_schedule(check_schedule(d, 2)) run_schedule(check_schedule(d, 1))
np.testing.assert_equal(d.numpy(), np.pad(np.exp2(a.numpy())[:-1, :-1], ((0, 1), (0, 1)))*2) np.testing.assert_equal(d.numpy(), np.pad(np.exp2(a.numpy())[:-1, :-1], ((0, 1), (0, 1)))*2)
def test_base_change_expand_pad(self): def test_base_change_expand_pad(self):
@@ -1554,14 +1491,14 @@ class TestSchedule(unittest.TestCase):
b = a.exp2() b = a.exp2()
c = b[:, None, :] c = b[:, None, :]
d = c.pad(((0, 0), (1, 1), (0, 0))) * 2 d = c.pad(((0, 0), (1, 1), (0, 0))) * 2
run_schedule(check_schedule(d, 2)) run_schedule(check_schedule(d, 1))
np.testing.assert_equal(d.numpy(), np.pad(np.exp2(a.numpy())[:, None, :], ((0, 0), (1, 1), (0, 0)))*2) np.testing.assert_equal(d.numpy(), np.pad(np.exp2(a.numpy())[:, None, :], ((0, 0), (1, 1), (0, 0)))*2)
def test_fuse_arange_pad_replicate_mode(self): def test_fuse_arange_pad_replicate_mode(self):
x = Tensor.empty(3,3,3,3, requires_grad=True) x = Tensor.empty(3,3,3,3, requires_grad=True)
y = x.pad((-1,2,2,-1), mode="replicate") y = x.pad((-1,2,2,-1), mode="replicate")
dx = y.sum().gradient(x)[0] dx = y.sum().gradient(x)[0]
sched = check_schedule(dx, 3) sched = check_schedule(dx, 1)
run_schedule(sched) run_schedule(sched)
np.testing.assert_allclose(dx.numpy(), [[[[0.,3.,9.],[0,1.,3.],[0.,0.,0.]]]*3]*3) np.testing.assert_allclose(dx.numpy(), [[[[0.,3.,9.],[0,1.,3.],[0.,0.,0.]]]*3]*3)
@@ -1571,7 +1508,7 @@ class TestSchedule(unittest.TestCase):
a = Tensor.ones(4, 4).contiguous().realize() a = Tensor.ones(4, 4).contiguous().realize()
b = a.cast(dtypes.half).expand(2, 4, 4) b = a.cast(dtypes.half).expand(2, 4, 4)
c = b.cast(dtypes.int).expand(2, 2, 4, 4) c = b.cast(dtypes.int).expand(2, 2, 4, 4)
run_schedule(check_schedule(c, 2)) run_schedule(check_schedule(c, 1))
np.testing.assert_equal(c.numpy(), np.ones(((2, 2, 4, 4)), dtype=np.int32)) np.testing.assert_equal(c.numpy(), np.ones(((2, 2, 4, 4)), dtype=np.int32))
def test_base_change_pad_expand(self): def test_base_change_pad_expand(self):
@@ -1579,7 +1516,7 @@ class TestSchedule(unittest.TestCase):
b = Tensor.full((4, 4), 2.).contiguous().realize() b = Tensor.full((4, 4), 2.).contiguous().realize()
c = (a + b).pad(((1, 1), (1, 1))) c = (a + b).pad(((1, 1), (1, 1)))
d = c.cast(dtypes.int).expand((2, 6, 6)) * 4 d = c.cast(dtypes.int).expand((2, 6, 6)) * 4
run_schedule(check_schedule(d, 2)) run_schedule(check_schedule(d, 1))
c_np = np.pad((np.full((4, 4), 2., dtype=np.float32) + np.full((4, 4), 1., dtype=np.float32)), ((1, 1), (1, 1)), constant_values=0.0) c_np = np.pad((np.full((4, 4), 2., dtype=np.float32) + np.full((4, 4), 1., dtype=np.float32)), ((1, 1), (1, 1)), constant_values=0.0)
np.testing.assert_equal(d.numpy(), np.broadcast_to(c_np.astype(np.half), (2, *c_np.shape)) * 4) np.testing.assert_equal(d.numpy(), np.broadcast_to(c_np.astype(np.half), (2, *c_np.shape)) * 4)
@@ -1678,7 +1615,7 @@ class TestSchedule(unittest.TestCase):
self._test_fusion([(4, 4), (1, 4)], lambda a,b:a.sum(1).reshape(b.shape)+b, 1) self._test_fusion([(4, 4), (1, 4)], lambda a,b:a.sum(1).reshape(b.shape)+b, 1)
def test_late_fusion_post_permute(self): def test_late_fusion_post_permute(self):
self._test_fusion([(4, 6, 4), (4, 4, 1)], lambda a,b:a.sum(1, keepdim=True).permute((2, 0, 1))+b, 2) self._test_fusion([(4, 6, 4), (4, 4, 1)], lambda a,b:a.sum(1, keepdim=True).permute((2, 0, 1))+b, 1)
def test_late_fusion_double_transpose(self): def test_late_fusion_double_transpose(self):
self._test_fusion([(32, 16, 1)], self._test_fusion([(32, 16, 1)],
@@ -1716,6 +1653,7 @@ class TestSchedule(unittest.TestCase):
self.assertListEqual(realized_const_view.tolist(), [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]) self.assertListEqual(realized_const_view.tolist(), [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]])
@given(strat.sampled_from(dtypes.all), strat.sampled_from(dtypes.all)) @given(strat.sampled_from(dtypes.all), strat.sampled_from(dtypes.all))
@unittest.skip("kernel count depends on input")
def test_cast_padded_const(self, dt1, dt2): def test_cast_padded_const(self, dt1, dt2):
assume(is_dtype_supported(dt1) and is_dtype_supported(dt2)) assume(is_dtype_supported(dt1) and is_dtype_supported(dt2))
a = Tensor(1, dtype=dt1).reshape(1, 1).pad(((1, 1), None)) a = Tensor(1, dtype=dt1).reshape(1, 1).pad(((1, 1), None))
@@ -1729,7 +1667,7 @@ class TestSchedule(unittest.TestCase):
X = Tensor.randn(10, 10).realize() X = Tensor.randn(10, 10).realize()
idxs = Tensor([0, 2]).realize() idxs = Tensor([0, 2]).realize()
xt = X[idxs] xt = X[idxs]
run_schedule(check_schedule(xt, 2)) run_schedule(check_schedule(xt, 1))
np.testing.assert_equal(xt.numpy(), X.numpy()[idxs.numpy()]) np.testing.assert_equal(xt.numpy(), X.numpy()[idxs.numpy()])
def test_simple_indexing_alt(self): def test_simple_indexing_alt(self):
@@ -1747,7 +1685,7 @@ class TestSchedule(unittest.TestCase):
def test_advanced_indexing_alt(self): def test_advanced_indexing_alt(self):
X = Tensor.arange(6).reshape(3, 2)+1 X = Tensor.arange(6).reshape(3, 2)+1
xt = X[[Tensor([2]), Tensor([1])]] xt = X[[Tensor([2]), Tensor([1])]]
run_schedule(check_schedule(xt, 3)) run_schedule(check_schedule(xt, 1))
np.testing.assert_equal(xt.numpy(), 6) np.testing.assert_equal(xt.numpy(), 6)
def test_advanced_simple_indexing_combined(self): def test_advanced_simple_indexing_combined(self):
@@ -1795,7 +1733,7 @@ class TestSchedule(unittest.TestCase):
x = Tensor.full((2,2), 16) x = Tensor.full((2,2), 16)
y = x.idiv(Tensor.linspace(2, 8, steps=4, dtype=dtypes.int).reshape(2,2)).pad(((1,1), (1,1))) y = x.idiv(Tensor.linspace(2, 8, steps=4, dtype=dtypes.int).reshape(2,2)).pad(((1,1), (1,1)))
out = y.sum(axis=1) out = y.sum(axis=1)
run_schedule(check_schedule(out, 2)) run_schedule(check_schedule(out, 1))
self.assertListEqual(out.tolist(), [0, 12, 4, 0]) self.assertListEqual(out.tolist(), [0, 12, 4, 0])
def test_arange_transposed_descendants(self): def test_arange_transposed_descendants(self):
@@ -1828,7 +1766,7 @@ class TestSchedule(unittest.TestCase):
x = Tensor.randn(5, 2).realize() x = Tensor.randn(5, 2).realize()
a = Tensor.arange(10).contiguous() a = Tensor.arange(10).contiguous()
out = (x + a[2]).sum() out = (x + a[2]).sum()
run_schedule(check_schedule(out, 3)) run_schedule(check_schedule(out, 2))
np.testing.assert_allclose(out.numpy(), (x.numpy()+np.arange(10)[2]).sum(), atol=1e-5, rtol=1e-6) np.testing.assert_allclose(out.numpy(), (x.numpy()+np.arange(10)[2]).sum(), atol=1e-5, rtol=1e-6)
def test_arange_index_child(self): def test_arange_index_child(self):
@@ -1844,7 +1782,7 @@ class TestSchedule(unittest.TestCase):
x = Tensor.randn(5, 2).realize() x = Tensor.randn(5, 2).realize()
a = (Tensor.arange(10)+1).contiguous() a = (Tensor.arange(10)+1).contiguous()
out = (x + a[2]).sum() out = (x + a[2]).sum()
run_schedule(check_schedule(out, 3)) run_schedule(check_schedule(out, 2))
np.testing.assert_allclose(out.numpy(), (x.numpy()+(np.arange(10)+1)[2]).sum(), atol=1e-5, rtol=1e-6) np.testing.assert_allclose(out.numpy(), (x.numpy()+(np.arange(10)+1)[2]).sum(), atol=1e-5, rtol=1e-6)
@unittest.skip("BUFFER_VIEW no longer supported on non-disk devices") @unittest.skip("BUFFER_VIEW no longer supported on non-disk devices")
@@ -1859,10 +1797,10 @@ class TestSchedule(unittest.TestCase):
from extra.models.llama import precompute_freqs_cis from extra.models.llama import precompute_freqs_cis
args = {"dim":32 if CI else 128, "end":2048 if CI else 8192, "theta":10000} args = {"dim":32 if CI else 128, "end":2048 if CI else 8192, "theta":10000}
fused = precompute_freqs_cis(**args) fused = precompute_freqs_cis(**args)
run_schedule(check_schedule(fused, 3)) run_schedule(check_schedule(fused, 1))
if getenv("CHECK", 1): if getenv("CHECK", 1):
ref = precompute_freqs_cis(**args) ref = precompute_freqs_cis(**args)
run_schedule(check_schedule(ref, 3)) run_schedule(check_schedule(ref, 1))
np.testing.assert_equal(fused.numpy(), ref.numpy()) np.testing.assert_equal(fused.numpy(), ref.numpy())
def test_fuse_assign_contiguous(self): def test_fuse_assign_contiguous(self):
@@ -1904,7 +1842,7 @@ class TestSchedule(unittest.TestCase):
X = Tensor([[0, 2, 3], [1, 2, 3]]).realize() X = Tensor([[0, 2, 3], [1, 2, 3]]).realize()
Y = Tensor([1, 2]).realize() Y = Tensor([1, 2]).realize()
loss = X.sparse_categorical_crossentropy(Y) loss = X.sparse_categorical_crossentropy(Y)
run_schedule(check_schedule(loss, 4)) run_schedule(check_schedule(loss, 3))
np.testing.assert_allclose(loss.item(), 0.878309, atol=1e-5, rtol=1e-6) np.testing.assert_allclose(loss.item(), 0.878309, atol=1e-5, rtol=1e-6)
def test_const_folding_alt(self): def test_const_folding_alt(self):
@@ -1925,7 +1863,7 @@ class TestSchedule(unittest.TestCase):
yt = Tensor.randn(BS, 10).realize() yt = Tensor.randn(BS, 10).realize()
with Context(SPLIT_REDUCEOP=0): with Context(SPLIT_REDUCEOP=0):
loss = yt.sparse_categorical_crossentropy(Y_train[samples]) loss = yt.sparse_categorical_crossentropy(Y_train[samples])
run_schedule(check_schedule(loss, 6)) run_schedule(check_schedule(loss, 4))
loss_fused = loss.numpy() loss_fused = loss.numpy()
loss_ref = torch.nn.CrossEntropyLoss()(torch.tensor(yt.numpy()), torch.tensor(Y_train.numpy())[torch.tensor(samples.numpy())]) loss_ref = torch.nn.CrossEntropyLoss()(torch.tensor(yt.numpy()), torch.tensor(Y_train.numpy())[torch.tensor(samples.numpy())])
np.testing.assert_allclose(loss_fused, loss_ref.numpy(), atol=1e-6, rtol=1e-6) np.testing.assert_allclose(loss_fused, loss_ref.numpy(), atol=1e-6, rtol=1e-6)
@@ -1935,7 +1873,7 @@ class TestSchedule(unittest.TestCase):
r = (X+Tensor.arange(16).reshape(4, 4)).sum() r = (X+Tensor.arange(16).reshape(4, 4)).sum()
out0 = r+2 out0 = r+2
out1 = r+3 out1 = r+3
run_schedule(check_schedule([out0, out1], 1)) run_schedule(check_schedule([out0, out1], 2)) # TODO: 1?
r_ref = (X.numpy()+np.arange(16).reshape(4, 4)).sum() r_ref = (X.numpy()+np.arange(16).reshape(4, 4)).sum()
np.testing.assert_allclose(out0.numpy(), r_ref+2, rtol=2e-7) np.testing.assert_allclose(out0.numpy(), r_ref+2, rtol=2e-7)
np.testing.assert_allclose(out1.numpy(), r_ref+3, rtol=2e-7) np.testing.assert_allclose(out1.numpy(), r_ref+3, rtol=2e-7)
@@ -1977,8 +1915,7 @@ class TestSwizzle(unittest.TestCase):
a = Tensor.randint(32, 32).realize() a = Tensor.randint(32, 32).realize()
r = (a+a).sum(1).sum(0) r = (a+a).sum(1).sum(0)
# double reduce collapses to a single reduce # double reduce collapses to a single reduce
with Context(DONT_GROUP_REDUCES=1): run_schedule(check_schedule(r, 1))
run_schedule(check_schedule(r, 1))
self.assertEqual(r.numpy(), (a.numpy()+a.numpy()).sum(1).sum(0)) self.assertEqual(r.numpy(), (a.numpy()+a.numpy()).sum(1).sum(0))
def test_single_swizzle(self): def test_single_swizzle(self):
@@ -1998,33 +1935,29 @@ class TestSwizzle(unittest.TestCase):
b = Tensor.randint(4,).realize() b = Tensor.randint(4,).realize()
# parallel reduce! # parallel reduce!
add = a.sum(0)+b.sum(0) add = a.sum(0)+b.sum(0)
with Context(DONT_GROUP_REDUCES=1): run_schedule(check_schedule(add, 1))
run_schedule(check_schedule(add, 1))
self.assertEqual(add.numpy(), a.numpy().sum(0)+b.numpy().sum(0)) self.assertEqual(add.numpy(), a.numpy().sum(0)+b.numpy().sum(0))
@unittest.skip("TODO: how do we express the norm")
def test_softmax_one_kernel(self): def test_softmax_one_kernel(self):
Tensor.manual_seed(0) Tensor.manual_seed(0)
with Context(DEBUG=0, TRACK_MATCH_STATS=0): with Context(DEBUG=0, TRACK_MATCH_STATS=0):
a = Tensor.randn(32, 32).realize() a = Tensor.randn(32, 32).realize()
t = a.softmax() t = a.softmax()
with Context(DONT_GROUP_REDUCES=1, DONT_REALIZE_EXPAND=1): check_schedule(t, 3) # TODO: 1?
check_schedule(t, 1)
def test_argmax_one_kernel(self): def test_argmax_one_kernel(self):
Tensor.manual_seed(0) Tensor.manual_seed(0)
with Context(DEBUG=0, TRACK_MATCH_STATS=0): with Context(DEBUG=0, TRACK_MATCH_STATS=0):
a = Tensor.randn(10, 20).realize() a = Tensor.randn(10, 20).realize()
t = a.argmax(0) t = a.argmax(0)
with Context(DONT_GROUP_REDUCES=1, DONT_REALIZE_EXPAND=1): t.realize() check_schedule(t, 2) # TODO: 1?
def test_swizzle_reduceop(self): def test_swizzle_reduceop(self):
Tensor.manual_seed(0) Tensor.manual_seed(0)
x = Tensor.randn(4,4).realize() x = Tensor.randn(4,4).realize()
y = Tensor.randn(4,4,4).realize() y = Tensor.randn(4,4,4).realize()
out = x.reshape(4,4,1).expand(4,4,4).sum(axis=(1,))+y out = x.reshape(4,4,1).expand(4,4,4).sum(axis=(1,))+y
with Context(DONT_REALIZE_EXPAND=1, DONT_GROUP_REDUCES=1): run_schedule(check_schedule(out, 2)) # TODO: 1?
run_schedule(check_schedule(out, 1))
np.testing.assert_allclose(out.numpy(), np.tile(x.numpy().reshape(4,4,1), (1,1,4)).sum(axis=1)+y.numpy()) np.testing.assert_allclose(out.numpy(), np.tile(x.numpy().reshape(4,4,1), (1,1,4)).sum(axis=1)+y.numpy())
def test_permute_rewrite(self): def test_permute_rewrite(self):
@@ -2032,7 +1965,7 @@ class TestSwizzle(unittest.TestCase):
y = Tensor.randn(4, 1, 16).realize() y = Tensor.randn(4, 1, 16).realize()
z = Tensor.randn(4, 4, 1).realize() z = Tensor.randn(4, 4, 1).realize()
t = (x*y).sum(axis=(0, 2)).reshape(1, 4, 1).permute(0, 2, 1)+z t = (x*y).sum(axis=(0, 2)).reshape(1, 4, 1).permute(0, 2, 1)+z
with Context(DONT_GROUP_REDUCES=1, DONT_REALIZE_EXPAND=1): run_schedule(check_schedule(t, 1)) run_schedule(check_schedule(t, 2)) # TODO: 1?
t_np = (x.numpy()*y.numpy()).sum(axis=(0, 2)).reshape(1, 4, 1).transpose(0, 2, 1)+z.numpy() t_np = (x.numpy()*y.numpy()).sum(axis=(0, 2)).reshape(1, 4, 1).transpose(0, 2, 1)+z.numpy()
np.testing.assert_allclose(t.numpy(), t_np, atol=1e-6, rtol=1e-3) np.testing.assert_allclose(t.numpy(), t_np, atol=1e-6, rtol=1e-3)
@@ -2043,14 +1976,14 @@ class TestSwizzle(unittest.TestCase):
a_reduce = a.sum(axis=(2,), keepdim=True).sum(axis=(1,)) a_reduce = a.sum(axis=(2,), keepdim=True).sum(axis=(1,))
b_reduce = b.sum(axis=(0,)) b_reduce = b.sum(axis=(0,))
t = a_reduce+b_reduce t = a_reduce+b_reduce
with Context(DONT_GROUP_REDUCES=1, DONT_REALIZE_EXPAND=1): run_schedule(check_schedule(t, 1)) run_schedule(check_schedule(t, 1))
def test_parallel_reduce_possible(self): def test_parallel_reduce_possible(self):
Tensor.manual_seed(0) Tensor.manual_seed(0)
x = Tensor.randn(4, 2, 2).realize() x = Tensor.randn(4, 2, 2).realize()
y = Tensor.randn(4, 2, 2).realize() y = Tensor.randn(4, 2, 2).realize()
t = x.sum(axis=1)+y.sum(axis=1) t = x.sum(axis=1)+y.sum(axis=1)
with Context(DONT_GROUP_REDUCES=1): run_schedule(check_schedule(t, 1)) run_schedule(check_schedule(t, 1))
np.testing.assert_allclose(t.numpy(), x.numpy().sum(axis=1)+y.numpy().sum(axis=1), atol=1e-6, rtol=1e-3) np.testing.assert_allclose(t.numpy(), x.numpy().sum(axis=1)+y.numpy().sum(axis=1), atol=1e-6, rtol=1e-3)
# kernels can only have 1 or n in each dim # kernels can only have 1 or n in each dim
@@ -2059,7 +1992,7 @@ class TestSwizzle(unittest.TestCase):
x = Tensor.randn(4, 2, 2).realize() x = Tensor.randn(4, 2, 2).realize()
y = Tensor.randn(4, 3, 2).realize() y = Tensor.randn(4, 3, 2).realize()
t = x.sum(axis=1)+y.sum(axis=1) t = x.sum(axis=1)+y.sum(axis=1)
with Context(DONT_GROUP_REDUCES=1): run_schedule(check_schedule(t, 1)) run_schedule(check_schedule(t, 1))
np.testing.assert_allclose(t.numpy(), x.numpy().sum(axis=1)+y.numpy().sum(axis=1), atol=1e-6, rtol=1e-3) np.testing.assert_allclose(t.numpy(), x.numpy().sum(axis=1)+y.numpy().sum(axis=1), atol=1e-6, rtol=1e-3)
def test_unsafe_pad(self): def test_unsafe_pad(self):
@@ -2143,6 +2076,11 @@ class TestCopyFolding(unittest.TestCase):
check_schedule(b, 0, filter_sink=False) check_schedule(b, 0, filter_sink=False)
assert b.item() == 1 assert b.item() == 1
def test_one_hot_with_copy(self):
y = Tensor([1, 2, 3]).to("CPU")
x = y.one_hot(10)
check_schedule(x, 3, filter_sink=False)
def test_const_copy_multi(self): def test_const_copy_multi(self):
x = Tensor.ones(1, device="CPU").to_(["CPU", "CPU:1"]) x = Tensor.ones(1, device="CPU").to_(["CPU", "CPU:1"])
check_schedule(x, 0, filter_sink=False) check_schedule(x, 0, filter_sink=False)
@@ -2160,8 +2098,8 @@ class TestCopyFolding(unittest.TestCase):
a = Tensor.ones((4,)).to("CPU") a = Tensor.ones((4,)).to("CPU")
b = Tensor.empty(4, device="CPU") b = Tensor.empty(4, device="CPU")
add = a+b add = a+b
add.kernelize()
assert all_same([x.device for x in add.uop.src]), f"ALU has different devices! {[x.device for x in add.src]}" assert all_same([x.device for x in add.uop.src]), f"ALU has different devices! {[x.device for x in add.src]}"
add.kernelize()
def test_alu_before_copy(self): def test_alu_before_copy(self):
buf = Tensor.ones(1).contiguous().realize() buf = Tensor.ones(1).contiguous().realize()
@@ -2172,12 +2110,12 @@ class TestCopyFolding(unittest.TestCase):
def test_copy_to_same_device(self): def test_copy_to_same_device(self):
a = Tensor.empty(4).uop a = Tensor.empty(4).uop
b = a.copy_to_device(a.device) b = a.copy_to_device(a.device)
check_schedule(b, 0, filter_sink=False) check_schedule(b, 1, filter_sink=False) # TODO: 0?
def test_copy_to_same_device_alt(self): def test_copy_to_same_device_alt(self):
a = Tensor.empty(4, 4).uop a = Tensor.empty(4, 4).uop
b = a.copy_to_device(a.device) b = a.copy_to_device(a.device)
check_schedule(b, 0, filter_sink=False) check_schedule(b, 1, filter_sink=False) # TODO: 0?
def test_copy_to_same_device_sched(self): def test_copy_to_same_device_sched(self):
a = Tensor.ones(4).contiguous().realize().uop.as_buf() a = Tensor.ones(4).contiguous().realize().uop.as_buf()
@@ -2192,13 +2130,11 @@ class TestCopyFolding(unittest.TestCase):
a = Tensor.empty(4) a = Tensor.empty(4)
check_schedule(a.clone(), 1, filter_sink=False) check_schedule(a.clone(), 1, filter_sink=False)
# NOTE: moving copy before view might change this
def test_shrink_copy(self): def test_shrink_copy(self):
a = Tensor.arange(4) a = Tensor.arange(4)
view = a.shrink(((0, 2),)) view = a.shrink(((0, 2),))
b = view.clone() b = view.clone()
# NOTE: this was sort of a bug making this 2 run_schedule(check_schedule(b, 1, filter_sink=False))
run_schedule(check_schedule(b, 2, filter_sink=False))
self.assertEqual(b.uop.base.buffer.size, 2) self.assertEqual(b.uop.base.buffer.size, 2)
self.assertEqual(b.uop.size, 2) self.assertEqual(b.uop.size, 2)
self.assertListEqual(b.tolist(), [0, 1]) self.assertListEqual(b.tolist(), [0, 1])
@@ -2207,7 +2143,7 @@ class TestCopyFolding(unittest.TestCase):
a = Tensor.arange(2) a = Tensor.arange(2)
view = a.reshape(2, 1).expand(2, 2) view = a.reshape(2, 1).expand(2, 2)
b = view.clone() b = view.clone()
run_schedule(check_schedule(b, 2, filter_sink=False)) run_schedule(check_schedule(b, 1, filter_sink=False))
self.assertEqual(b.uop.base.buffer.size, 4) self.assertEqual(b.uop.base.buffer.size, 4)
self.assertEqual(b.uop.size, 4) self.assertEqual(b.uop.size, 4)
self.assertListEqual(b.tolist(), [[0, 0], [1, 1]]) self.assertListEqual(b.tolist(), [[0, 0], [1, 1]])
@@ -2251,7 +2187,7 @@ class TestBufferUOp(unittest.TestCase):
def test_buffer_has_buffer(self): def test_buffer_has_buffer(self):
buf = Tensor.empty(10) buf = Tensor.empty(10)
self.assertIsNotNone(buf.uop.buffer) self.assertIsNotNone(buf.uop.buffer)
self.assertEqual(buf.uop.st, ShapeTracker.from_shape((10,))) self.assertEqual(buf.uop.shape, (10,))
# the device Buffer remains unallocated until it's we run the schedule # the device Buffer remains unallocated until it's we run the schedule
self.assertFalse(buf.uop.buffer.is_allocated()) self.assertFalse(buf.uop.buffer.is_allocated())
add = buf+1 add = buf+1
@@ -2330,7 +2266,7 @@ class TestContiguous(unittest.TestCase):
def test_double_contiguous_realizes_once(self): def test_double_contiguous_realizes_once(self):
a = Tensor.empty(4, 1) a = Tensor.empty(4, 1)
b = a.expand((4, 4)).contiguous().contiguous() b = a.expand((4, 4)).contiguous().contiguous()
check_schedule(b, 1) check_schedule(b, 2) # TODO: should be 1?
def test_view_does_not_realize(self): def test_view_does_not_realize(self):
a = Tensor.empty(4) a = Tensor.empty(4)
@@ -2466,10 +2402,6 @@ class TestUOpBecome(unittest.TestCase):
c = (a.reshape(1, 1, 4, 4)+0).shrink(((0, 1), (0, 1), (0, 3), (0, 3)))+0 c = (a.reshape(1, 1, 4, 4)+0).shrink(((0, 1), (0, 1), (0, 3), (0, 3)))+0
check_schedule([b, c], 0) check_schedule([b, c], 0)
assert all_same([x.uop.base.realized for x in [a,b,c]]) assert all_same([x.uop.base.realized for x in [a,b,c]])
# these movement ops result in the same ShapeTracker
assert b.uop.st == c.uop.st
assert b.uop is c.uop
assert UPat(Ops.VIEW, src=(UPat(Ops.BUFFER),)).match(c.uop, {})
def test_setitem_becomes_subbuffer(self): def test_setitem_becomes_subbuffer(self):
a = Tensor.full((4,), 2.).contiguous().realize() a = Tensor.full((4,), 2.).contiguous().realize()
-1
View File
@@ -52,7 +52,6 @@ class TestSetitem(unittest.TestCase):
def test_setitem_into_noncontiguous(self): def test_setitem_into_noncontiguous(self):
t = Tensor.ones(4) t = Tensor.ones(4)
self.assertFalse(t.uop.st.contiguous)
with self.assertRaises(RuntimeError): t[1] = 5 with self.assertRaises(RuntimeError): t[1] = 5
@unittest.skip("TODO: flaky") @unittest.skip("TODO: flaky")
+5 -10
View File
@@ -165,8 +165,7 @@ class TestSoftmaxFusion(unittest.TestCase):
sout.realize() sout.realize()
print("*** single kernel softmax ***") print("*** single kernel softmax ***")
# NOTE: DONT_GROUP_REDUCES is required here with Context(NOOPT=1, DEBUG=max(DEBUG.value, 2)):
with Context(NOOPT=1, DEBUG=max(DEBUG.value, 2), DONT_GROUP_REDUCES=1):
out = single_kernel_softmax(self.test) out = single_kernel_softmax(self.test)
out.realize() out.realize()
@@ -186,7 +185,6 @@ class TestSoftmaxFusion(unittest.TestCase):
np.testing.assert_allclose(sout.numpy(), out.numpy(), atol=3e-7) np.testing.assert_allclose(sout.numpy(), out.numpy(), atol=3e-7)
@unittest.skip("recursion error no longer raised")
def test_softmax_bw(self): def test_softmax_bw(self):
print("*** softmax bw ***") print("*** softmax bw ***")
self.test.requires_grad_() self.test.requires_grad_()
@@ -197,14 +195,11 @@ class TestSoftmaxFusion(unittest.TestCase):
self.test.grad = None self.test.grad = None
print("*** single kernel softmax bw ***") print("*** single kernel softmax bw ***")
# NOTE: DONT_GROUP_REDUCES is required here with Context(NOOPT=1, DEBUG=max(DEBUG.value, 2)):
# TODO: fix RecursionError with DONT_GROUP_REDUCES single_kernel_softmax(self.test).sum().backward()
with self.assertRaises(RecursionError): g = self.test.grad.realize()
with Context(NOOPT=1, DEBUG=max(DEBUG.value, 2), DONT_GROUP_REDUCES=1):
single_kernel_softmax(self.test).sum().backward()
g = self.test.grad.realize()
np.testing.assert_allclose(sg.numpy(), g.numpy(), atol=1e-7) np.testing.assert_allclose(sg.numpy(), g.numpy(), atol=1e-7)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
+3 -12
View File
@@ -1,6 +1,5 @@
import unittest import unittest
from tinygrad import Tensor, Variable, GlobalCounters from tinygrad import Tensor, Variable, GlobalCounters
from tinygrad.shape.shapetracker import View
from tinygrad.uop.ops import sym_infer from tinygrad.uop.ops import sym_infer
from tinygrad.dtype import dtypes from tinygrad.dtype import dtypes
from tinygrad.device import is_dtype_supported from tinygrad.device import is_dtype_supported
@@ -64,14 +63,6 @@ class TestSymbolicOps(unittest.TestCase):
self.test_attention(imin=4, imax=5, use_symbolic=False) self.test_attention(imin=4, imax=5, use_symbolic=False)
self.test_attention(imin=4, imax=5, use_symbolic=True) self.test_attention(imin=4, imax=5, use_symbolic=True)
# until this works, symbolic single kernel softmax won't
@unittest.expectedFailure
def test_attention_simple_view(self):
i = Variable("i", 2, 10)
v1 = View.create((2,4,1,i,i), ((i*4),i,0,0,1))
v2 = View.create((2,4,1,i,i,i), (((i*i)*4),(i*i),0,0,i,1))
self.assertIsNotNone(v1+v2)
def test_attention_training(self): def test_attention_training(self):
with Tensor.train(): with Tensor.train():
self.test_attention(dropout_p=0.0) self.test_attention(dropout_p=0.0)
@@ -296,7 +287,6 @@ class TestSymbolicOps(unittest.TestCase):
symbolic = symbolic_result[:].numpy() symbolic = symbolic_result[:].numpy()
np.testing.assert_allclose(symbolic, expected, atol=1e-6, rtol=0) np.testing.assert_allclose(symbolic, expected, atol=1e-6, rtol=0)
@unittest.expectedFailure
def test_conv2d_ceildiv_edge_case(self): def test_conv2d_ceildiv_edge_case(self):
v = Variable('v', 11, 50_000) v = Variable('v', 11, 50_000)
val = 39601 val = 39601
@@ -304,9 +294,10 @@ class TestSymbolicOps(unittest.TestCase):
weight = Tensor.randn(256, 22, 12) weight = Tensor.randn(256, 22, 12)
result = x.conv2d(weight=weight, groups=1, stride=6, dilation=1, padding=(3, 3)) result = x.conv2d(weight=weight, groups=1, stride=6, dilation=1, padding=(3, 3))
var_val = {v: val} var_val = {v.expr: val}
shape = tuple(sym_infer(s, var_val) for s in result.shape) shape = tuple(sym_infer(s, var_val) for s in result.shape)
self.assertEqual(shape, (1, 256, 6600)) # TODO: fails if ceildiv is incorrect with self.assertRaises(AssertionError):
self.assertEqual(shape, (1, 256, 6600)) # TODO: fails if ceildiv is incorrect
# TODO: test output is correct # TODO: test output is correct
if __name__ == '__main__': if __name__ == '__main__':
+16 -21
View File
@@ -9,6 +9,7 @@ from hypothesis import given, settings, strategies as strat
from tinygrad.device import is_dtype_supported from tinygrad.device import is_dtype_supported
from tinygrad.uop.ops import Ops, UOp from tinygrad.uop.ops import Ops, UOp
from tinygrad.renderer.ptx import PTXRenderer from tinygrad.renderer.ptx import PTXRenderer
from tinygrad.renderer.nir import NIRRenderer
from tinygrad.codegen import full_rewrite from tinygrad.codegen import full_rewrite
from tinygrad.dtype import DType from tinygrad.dtype import DType
@@ -526,6 +527,7 @@ class TestTinygrad(unittest.TestCase):
self.assertListEqual(t.shrink_to(16).tolist(), list(range(16))) self.assertListEqual(t.shrink_to(16).tolist(), list(range(16)))
t = t.reshape(4, 8).contiguous().realize() t = t.reshape(4, 8).contiguous().realize()
self.assertListEqual(t.shrink_to(2, 2).tolist(), [[0, 1], [8, 9]]) self.assertListEqual(t.shrink_to(2, 2).tolist(), [[0, 1], [8, 9]])
self.assertListEqual(t.shrink_to(None, 2).tolist(), t.shrink_to(4, 2).tolist())
with self.assertRaises(ValueError): t.shrink_to(2) with self.assertRaises(ValueError): t.shrink_to(2)
with self.assertRaises(ValueError): t.shrink_to(2, 2, 2) with self.assertRaises(ValueError): t.shrink_to(2, 2, 2)
@@ -568,22 +570,6 @@ class TestMoveTensor(unittest.TestCase):
np.testing.assert_equal(x.grad.numpy(), [[2,2,2],[0,0,0],[-2,-2,-2]]) np.testing.assert_equal(x.grad.numpy(), [[2,2,2],[0,0,0],[-2,-2,-2]])
class TestZeroShapeTensor(unittest.TestCase): class TestZeroShapeTensor(unittest.TestCase):
def test_shape_is_expanded(self):
t = Tensor.empty(3, 2, 0)
assert t.shape == (3, 2, 0)
# numpy has stride 0, 0, 0; torch has stride 2, 1, 1
assert t.uop.st.is_expanded() == (True, True, True)
t = Tensor.empty(3, 0, 2)
assert t.shape == (3, 0, 2)
# numpy has stride 0, 0, 0; torch has stride 2, 2, 1
assert t.uop.st.is_expanded() == (True, True, True)
t = Tensor.empty(0, 0, 0)
assert t.shape == (0, 0, 0)
# numpy has stride 0, 0, 0; torch has stride 1, 1, 1
assert t.uop.st.is_expanded() == (True, True, True)
def test_rand(self): def test_rand(self):
t = Tensor.rand(3, 2, 0) t = Tensor.rand(3, 2, 0)
assert t.shape == (3, 2, 0) assert t.shape == (3, 2, 0)
@@ -635,8 +621,10 @@ class TestZeroShapeTensor(unittest.TestCase):
np.testing.assert_equal(Tensor([1, 2]).pad_to(4).numpy(), [1, 2, 0, 0]) np.testing.assert_equal(Tensor([1, 2]).pad_to(4).numpy(), [1, 2, 0, 0])
np.testing.assert_equal(Tensor([[1, 2]]).pad_to(2, 3).numpy(), [[1, 2, 0], [0, 0, 0]]) np.testing.assert_equal(Tensor([[1, 2]]).pad_to(2, 3).numpy(), [[1, 2, 0], [0, 0, 0]])
with self.assertRaises(TypeError): Tensor([1, 2]).pad_to(2, 3) np.testing.assert_equal(Tensor([[1, 2]]).pad_to(1, 3).numpy(), [[1, 2, 0]])
with self.assertRaises(TypeError): Tensor([[1, 2]]).pad_to(3) np.testing.assert_equal(Tensor([[1, 2]]).pad_to(None, 3).numpy(), [[1, 2, 0]])
with self.assertRaises(ValueError): Tensor([1, 2]).pad_to(2, 3)
with self.assertRaises(ValueError): Tensor([[1, 2]]).pad_to(3)
def test_shrink_into_zero(self): def test_shrink_into_zero(self):
t = Tensor.rand(3, 4).realize() t = Tensor.rand(3, 4).realize()
@@ -822,6 +810,13 @@ class TestTensorMetadata(unittest.TestCase):
self.assertEqual(len(si.metadata), 1) self.assertEqual(len(si.metadata), 1)
self.assertEqual(si.metadata[0].name, "relu") self.assertEqual(si.metadata[0].name, "relu")
def test_assign(self):
x = Tensor.empty(10, 10).realize()
x.assign(Tensor.ones(10, 10).contiguous())
si = x.schedule()[-1]
self.assertEqual(len(si.metadata), 1)
self.assertEqual(si.metadata[0].name, "assign")
def test_complex(self): def test_complex(self):
x = Tensor.rand(3, requires_grad=True) x = Tensor.rand(3, requires_grad=True)
y = Tensor.rand(3, requires_grad=True) y = Tensor.rand(3, requires_grad=True)
@@ -833,7 +828,6 @@ class TestTensorMetadata(unittest.TestCase):
self.assertEqual(len(si.metadata), 3) self.assertEqual(len(si.metadata), 3)
self.assertEqual(set(m.name for m in si.metadata), {"relu", "sigmoid", "__mul__"}) self.assertEqual(set(m.name for m in si.metadata), {"relu", "sigmoid", "__mul__"})
@unittest.skip("not accurate")
def test_complex_backward(self): def test_complex_backward(self):
x = Tensor.rand(3, requires_grad=True).realize() x = Tensor.rand(3, requires_grad=True).realize()
y = Tensor.rand(3, requires_grad=True).realize() y = Tensor.rand(3, requires_grad=True).realize()
@@ -871,7 +865,8 @@ class TestIdxUpcast(unittest.TestCase):
store = next(uop for uop in uops if uop.op is Ops.STORE) store = next(uop for uop in uops if uop.op is Ops.STORE)
assert store.op is Ops.STORE assert store.op is Ops.STORE
idx = self._find_op(store, Ops.INDEX) idx = self._find_op(store, Ops.INDEX)
if idx is not None: # PTX turns Ops.INDEX into pointer arithmetic earlier than cstyle, plus it's already cast to int64 # PTX and NIR turn Ops.INDEX into pointer arithmetic earlier than cstyle, plus it's already cast to int64
if not isinstance(Device[Device.DEFAULT].renderer, (PTXRenderer, NIRRenderer)):
assert idx.op is Ops.INDEX assert idx.op is Ops.INDEX
idx_val = idx.src[1] idx_val = idx.src[1]
assert idx_val.dtype is dtype assert idx_val.dtype is dtype
@@ -895,7 +890,7 @@ class TestIdxUpcast(unittest.TestCase):
def test_regular_sym(self): def test_regular_sym(self):
self.do_op_then_assert(dtypes.int, 2048, 2048, UOp.variable("dim3", 1, 64).bind(32)) self.do_op_then_assert(dtypes.int, 2048, 2048, UOp.variable("dim3", 1, 64).bind(32))
@unittest.skipIf(isinstance(Device[Device.DEFAULT].renderer, PTXRenderer), "PTX always convert Ops.INDEX to int64") @unittest.skipIf(isinstance(Device[Device.DEFAULT].renderer, (PTXRenderer, NIRRenderer)), "PTX and NIR always converts Ops.INDEX to int64")
def test_symfold(self): def test_symfold(self):
# This would cause an overflow, but after sym fold it's within int32 # This would cause an overflow, but after sym fold it's within int32
a = Tensor.arange(65535) a = Tensor.arange(65535)
+2 -9
View File
@@ -3,7 +3,7 @@ import numpy as np
import unittest import unittest
from tinygrad import Tensor, Device, dtypes from tinygrad import Tensor, Device, dtypes
from tinygrad.engine.realize import run_schedule from tinygrad.engine.realize import run_schedule
from tinygrad.uop.ops import Ops, UOp, UPat from tinygrad.uop.ops import UOp
from tinygrad.helpers import SPLIT_REDUCEOP from tinygrad.helpers import SPLIT_REDUCEOP
class TestTensorUOp(unittest.TestCase): class TestTensorUOp(unittest.TestCase):
@@ -11,8 +11,7 @@ class TestTensorUOp(unittest.TestCase):
def helper(a: np.ndarray): def helper(a: np.ndarray):
print(a.shape, a.strides, a.flags.c_contiguous) print(a.shape, a.strides, a.flags.c_contiguous)
b = Tensor(a).uop b = Tensor(a).uop
#assert b.st.contiguous == a.flags.c_contiguous assert b.shape == a.shape
assert b.st.shape == a.shape
np.testing.assert_equal(a, Tensor(b).numpy()) np.testing.assert_equal(a, Tensor(b).numpy())
for ndims in range(1, 4): for ndims in range(1, 4):
@@ -94,7 +93,6 @@ class TestTensorUOp(unittest.TestCase):
out.realize() out.realize()
self.assertEqual(out.tolist(), Tensor.zeros(4, 8).tolist()) self.assertEqual(out.tolist(), Tensor.zeros(4, 8).tolist())
reduce_kernel = UPat(Ops.SINK, src=(UPat(Ops.STORE, allow_any_len=True, src=(UPat(), UPat((Ops.REDUCE_AXIS, Ops.REDUCE))))))
@unittest.skipUnless(SPLIT_REDUCEOP, "only for SPLIT_REDUCEOP") @unittest.skipUnless(SPLIT_REDUCEOP, "only for SPLIT_REDUCEOP")
class TestReduceOp(unittest.TestCase): class TestReduceOp(unittest.TestCase):
def test_no_split_reduce_kernel(self): def test_no_split_reduce_kernel(self):
@@ -102,23 +100,18 @@ class TestReduceOp(unittest.TestCase):
a = a.sum() a = a.sum()
sched = a.schedule() sched = a.schedule()
assert len(sched) == 1 assert len(sched) == 1
assert reduce_kernel.match(sched[0].ast, {})
def test_split_reduce_kernel_dim0(self): def test_split_reduce_kernel_dim0(self):
a = Tensor.rand(256, 255).realize() a = Tensor.rand(256, 255).realize()
a = a.sum() a = a.sum()
sched = a.schedule() sched = a.schedule()
assert len(sched) == 2 assert len(sched) == 2
for s in sched:
assert reduce_kernel.match(s.ast, {})
def test_split_reduce_kernel_dim1(self): def test_split_reduce_kernel_dim1(self):
a = Tensor.rand(255, 256).realize() a = Tensor.rand(255, 256).realize()
a = a.sum() a = a.sum()
sched = a.schedule() sched = a.schedule()
assert len(sched) == 2 assert len(sched) == 2
for s in sched:
assert reduce_kernel.match(s.ast, {})
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
+1 -1
View File
@@ -93,7 +93,7 @@ class TestTensorVariable(unittest.TestCase):
vb = v.bind(3) vb = v.bind(3)
t = Tensor.empty(3, vb) t = Tensor.empty(3, vb)
assert t.uop.base.buffer.size == 30 assert t.uop.base.buffer.size == 30
assert t.uop.st.shape == (3, vb) assert t.uop.shape == (3, vb)
if __name__ == '__main__': if __name__ == '__main__':
+1
View File
@@ -149,6 +149,7 @@ class TestTranscendentalVectorized(unittest.TestCase):
for vec_size in [1,2,3,4,5,127,128]: self._test_vectorized_op(Tensor.log2, np.log2, (0.001, 200), vec_size) for vec_size in [1,2,3,4,5,127,128]: self._test_vectorized_op(Tensor.log2, np.log2, (0.001, 200), vec_size)
@unittest.skipIf(getenv("DSP"), "requires int division") @unittest.skipIf(getenv("DSP"), "requires int division")
@unittest.skipIf(getenv("NV_NAK"), "MUFU.SIN is not accurate enough")
def test_sin_vectorized(self): def test_sin_vectorized(self):
for vec_size in [1,2,3,4,5,127,128]: self._test_vectorized_op(Tensor.sin, np.sin, (-100, 100), vec_size) for vec_size in [1,2,3,4,5,127,128]: self._test_vectorized_op(Tensor.sin, np.sin, (-100, 100), vec_size)
+27 -51
View File
@@ -1,12 +1,12 @@
from typing import List
import unittest, pytest import unittest, pytest
from tinygrad import dtypes, Variable from tinygrad import dtypes, Variable
from tinygrad.dtype import AddrSpace from tinygrad.dtype import AddrSpace
from tinygrad.helpers import DEBUG, Context from tinygrad.helpers import DEBUG, Context
from tinygrad.uop.ops import Ops, UOp, UPat, PatternMatcher, track_rewrites, graph_rewrite, GroupOp, KernelInfo from tinygrad.uop.ops import Ops, UOp, UPat, PatternMatcher, track_rewrites, graph_rewrite, GroupOp, AxisType
from tinygrad.uop.symbolic import sym from tinygrad.uop.symbolic import sym
from tinygrad.codegen import full_rewrite, full_rewrite_to_sink from tinygrad.codegen import full_rewrite_to_sink
from tinygrad.codegen.late.expander import expander from tinygrad.codegen.late.expander import expander
from test.test_uops import to_uops_list
simple_pm = PatternMatcher([ simple_pm = PatternMatcher([
(UPat.cvar('x', dtypes.int), lambda x: UOp.const(dtypes.float, 1.0) + UOp.const(dtypes.float, 2.0)), (UPat.cvar('x', dtypes.int), lambda x: UOp.const(dtypes.float, 1.0) + UOp.const(dtypes.float, 2.0)),
@@ -15,12 +15,6 @@ simple_pm = PatternMatcher([
((UPat.var('x') + UPat.cvar('c1')) + UPat.cvar('c2'), lambda x,c1,c2: x + (c1.arg+c2.arg)), ((UPat.var('x') + UPat.cvar('c1')) + UPat.cvar('c2'), lambda x,c1,c2: x + (c1.arg+c2.arg)),
]) ])
def to_uops_list(u:List[UOp]) -> List[UOp]:
# we strip the SINK here for legacy reasons
ret = full_rewrite(UOp.sink(*u, arg=KernelInfo(opts_to_apply=())))
assert ret[-1].op is Ops.SINK
return ret[:-1]
class TestGraphRewriteConst(unittest.TestCase): class TestGraphRewriteConst(unittest.TestCase):
def test_gep_const(self): def test_gep_const(self):
v1 = UOp.const(dtypes.int.vec(3), (0,1,2)) v1 = UOp.const(dtypes.int.vec(3), (0,1,2))
@@ -459,30 +453,30 @@ class TestUOpGraph(unittest.TestCase):
idx = d0.index(ridx0) idx = d0.index(ridx0)
ld = idx.load() ld = idx.load()
val = (ridx0<50).where(5, ld) val = (ridx0<50).where(5, ld)
st = idx.store(val, ridx0) st = idx.store(val).end(ridx0)
uops = to_uops_list([st]) uops = to_uops_list([st])
for u in uops: for u in uops:
assert u.op is not Ops.WHERE assert u.op is not Ops.WHERE
if u.op is Ops.STORE: assert u.src[1].arg==5 if u.op is Ops.STORE: assert u.src[1].arg==5
def test_load_idx_becomes_int(self): def test_load_idx_becomes_int(self):
# These loads wont overflow int since we know from the gate that the value is bounded # mnist indexing with split reduceop
r0 = UOp.range(10, 0) # Make sure we are not doign math on the loaded index, which would promote it to long
d0 = UOp(Ops.DEFINE_GLOBAL, dtypes.long.ptr(), (), 0) c0 = UOp(Ops.DEFINE_GLOBAL, dtypes.uchar.ptr(128000), arg=0, src=())
d1 = UOp(Ops.DEFINE_GLOBAL, dtypes.long.ptr(), (), 1) c1 = UOp.range(UOp.const(dtypes.index, 512), 1, AxisType.LOOP)
l0 = UOp(Ops.LOAD, dtypes.long, (d0.index(UOp.const(dtypes.int, 0)),)).cast(dtypes.index) c2 = UOp.range(UOp.const(dtypes.index, 250), 2, AxisType.LOOP)
idx = l0 * 600 c3 = UOp(Ops.DEFINE_GLOBAL, dtypes.int.ptr(512), arg=1, src=())
valid = (l0<-1).ne(True)&(l0<3000) c4 = c3.index(c1).load()
l1 = UOp(Ops.LOAD, dtypes.long, (d1.index(idx.valid(valid)),)) c5 = UOp.range(UOp.const(dtypes.index, 240), 0, AxisType.REDUCE)
uops = to_uops_list([l1]) c6 = ((c2*UOp.const(dtypes.index, 240))+c5)
c7 = UOp(Ops.DEFINE_GLOBAL, dtypes.uchar.ptr(60000), arg=2, src=())
c8 = c7.index(c6).load()
c9 = ((c4<0).where((c4+60000), c4)!=c6.cast(dtypes.int)).where(0, c8.cast(dtypes.uint).cast(dtypes.uchar)).reduce(c5, arg=Ops.ADD)
c10 = c0.index(((c1*UOp.const(dtypes.index, 250))+c2)).store(c9).end(c1, c2)
ast = c10.sink()
uops = to_uops_list([ast])
for u in uops: for u in uops:
if u.op is Ops.INDEX: self.assertEqual(u.src[1].dtype, dtypes.int) self.assertNotEqual(u.dtype, dtypes.long)
valid = (10*r0<5-l0).ne(True)&(l0<3000)
l2 = UOp(Ops.LOAD, dtypes.long, (d1.index(idx.valid(valid)),))
uops = to_uops_list([l2])
for u in uops:
if u.op is Ops.INDEX: self.assertEqual(u.src[1].dtype, dtypes.int)
def test_in_out_of_bounds_access(self): def test_in_out_of_bounds_access(self):
with Context(IGNORE_OOB=0): with Context(IGNORE_OOB=0):
@@ -518,6 +512,7 @@ class TestUOpGraph(unittest.TestCase):
st1 = UOp(Ops.STORE, dtypes.void, (glbl0.index(v), v, v<20)) st1 = UOp(Ops.STORE, dtypes.void, (glbl0.index(v), v, v<20))
with self.assertRaises(RuntimeError): to_uops_list([st1]) with self.assertRaises(RuntimeError): to_uops_list([st1])
@unittest.skip("if not allowed in graph")
def test_in_bounds_access_gated_local(self): def test_in_bounds_access_gated_local(self):
with Context(IGNORE_OOB=0): with Context(IGNORE_OOB=0):
# Define buffers # Define buffers
@@ -579,7 +574,7 @@ class TestUOpGraph(unittest.TestCase):
def test_in_out_bounds_access_with_mask(self): def test_in_out_bounds_access_with_mask(self):
with Context(IGNORE_OOB=0): with Context(IGNORE_OOB=0):
glbl0 = UOp(Ops.DEFINE_GLOBAL, dtypes.int.ptr(16), (), 0) glbl0 = UOp(Ops.DEFINE_GLOBAL, dtypes.int.ptr(16), (), 0)
gidx0 = UOp(Ops.SPECIAL, dtypes.index, (UOp.const(dtypes.index, 42),), "gidx0") gidx0 = UOp.range(42, 0, AxisType.GLOBAL)
ld0 = UOp(Ops.LOAD, dtypes.int, (glbl0.index(gidx0, (5<gidx0)&(gidx0<16)),)) ld0 = UOp(Ops.LOAD, dtypes.int, (glbl0.index(gidx0, (5<gidx0)&(gidx0<16)),))
ld1 = UOp(Ops.LOAD, dtypes.int, (glbl0.index(gidx0, gidx0<16),)) ld1 = UOp(Ops.LOAD, dtypes.int, (glbl0.index(gidx0, gidx0<16),))
to_uops_list([ld0, ld1]) to_uops_list([ld0, ld1])
@@ -603,7 +598,7 @@ class TestUOpGraph(unittest.TestCase):
with Context(IGNORE_OOB=0): with Context(IGNORE_OOB=0):
glbl0 = UOp(Ops.DEFINE_GLOBAL, dtypes.int.ptr(16), (), 0) glbl0 = UOp(Ops.DEFINE_GLOBAL, dtypes.int.ptr(16), (), 0)
glbl1 = UOp(Ops.DEFINE_GLOBAL, dtypes.int.ptr(64), (), 0) glbl1 = UOp(Ops.DEFINE_GLOBAL, dtypes.int.ptr(64), (), 0)
gidx0 = UOp(Ops.SPECIAL, dtypes.index, (UOp.const(dtypes.index, 42),), "gidx0") gidx0 = UOp.range(42, 0, AxisType.GLOBAL)
ld0 = UOp(Ops.LOAD, dtypes.int, (glbl0.index(gidx0, gidx0<8),)).cast(dtypes.index) ld0 = UOp(Ops.LOAD, dtypes.int, (glbl0.index(gidx0, gidx0<8),)).cast(dtypes.index)
ld1 = UOp(Ops.LOAD, dtypes.int, (glbl1.index(ld0*2, (ld0>=0)&(ld0<32)),)) ld1 = UOp(Ops.LOAD, dtypes.int, (glbl1.index(ld0*2, (ld0>=0)&(ld0<32)),))
to_uops_list([ld1]) to_uops_list([ld1])
@@ -638,13 +633,13 @@ class TestUOpGraph(unittest.TestCase):
lidx = UOp(Ops.SPECIAL, dtypes.int, (UOp.const(dtypes.int, 16),), "lidx0") lidx = UOp(Ops.SPECIAL, dtypes.int, (UOp.const(dtypes.int, 16),), "lidx0")
st = UOp(Ops.STORE, dtypes.void, (smem.index(lidx), UOp.load(glbl0.index(lidx), dtype=dtypes.int))) st = UOp(Ops.STORE, dtypes.void, (smem.index(lidx), UOp.load(glbl0.index(lidx), dtype=dtypes.int)))
barrier = UOp(Ops.BARRIER, dtypes.void, (st, )) barrier = UOp(Ops.BARRIER, dtypes.void, (st, ))
ld0 = UOp(Ops.LOAD, dtypes.int, (smem.index(UOp.invalid()), barrier)) ld0 = UOp(Ops.LOAD, dtypes.int, (smem.after(barrier).index(UOp.invalid()),))
ld1 = UOp(Ops.LOAD, dtypes.int, (smem.index(lidx+2, UOp.const(dtypes.bool, True)), barrier)) ld1 = UOp(Ops.LOAD, dtypes.int, (smem.after(barrier).index(lidx+2, UOp.const(dtypes.bool, True)),))
uops = to_uops_list([UOp(Ops.STORE, dtypes.void, (glbl0.index(lidx), ld1+ld0))]) uops = to_uops_list([UOp(Ops.STORE, dtypes.void, (glbl0.index(lidx), ld1+ld0))])
ld0 = uops[-1].src[-1] ld0 = uops[-1].src[-1]
# the gate and invalid value are deleted from ld1 # the gate and invalid value are deleted from ld1
self.assertEqual(ld0.src[0], smem.index(lidx+2)) self.assertEqual(ld0.src[0], smem.after(barrier).index(lidx+2))
def test_fold_gated_store(self): def test_fold_gated_store(self):
glbl = UOp(Ops.DEFINE_GLOBAL, dtypes.int.ptr(), (), 0) glbl = UOp(Ops.DEFINE_GLOBAL, dtypes.int.ptr(), (), 0)
@@ -665,19 +660,6 @@ class TestUOpGraph(unittest.TestCase):
bad_gate = UOp.const(dtypes.int, 1) bad_gate = UOp.const(dtypes.int, 1)
with self.assertRaises(AssertionError): to_uops_list([UOp(Ops.STORE, dtypes.void, (glbl0, idx, UOp.const(dtypes.int, 42), bad_gate))]) with self.assertRaises(AssertionError): to_uops_list([UOp(Ops.STORE, dtypes.void, (glbl0, idx, UOp.const(dtypes.int, 42), bad_gate))])
def test_switched_range_order(self):
glbl = UOp(Ops.DEFINE_GLOBAL, dtypes.int.ptr(), (), 0)
cf = UOp.const(dtypes.float, 0.0)
r1 = UOp.range(2, 0)
r2 = UOp.range(2, 1)
alu = UOp(Ops.MUL, dtypes.int, (r2, r1))
store = UOp(Ops.STORE, dtypes.void, (glbl.index(alu), cf))
uops = to_uops_list([store])
ranges = [x for x in uops if x.op is Ops.RANGE]
endranges = [x for x in uops if x.op is Ops.ENDRANGE]
# ranges are closed in the right order
self.assertEqual(endranges[-1].src[0], ranges[0])
@track_rewrites() @track_rewrites()
def expander_rewrite(sink): return graph_rewrite(sink, sym + expander) def expander_rewrite(sink): return graph_rewrite(sink, sym + expander)
@@ -845,8 +827,6 @@ class TestIFUOps(unittest.TestCase):
if_uops = [u for u in sink.toposort() if u.op is Ops.IF] if_uops = [u for u in sink.toposort() if u.op is Ops.IF]
self.assertEqual(len(if_uops), 1) self.assertEqual(len(if_uops), 1)
self.assertEqual(if_uops[0].src[0], gate) self.assertEqual(if_uops[0].src[0], gate)
for st in sink.src:
self.assertEqual(len(st.src), 2)
def test_expand_ifs_one_gate(self): def test_expand_ifs_one_gate(self):
gbuf = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(), (), 0) gbuf = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(), (), 0)
@@ -854,7 +834,7 @@ class TestIFUOps(unittest.TestCase):
valid = UOp(Ops.SPECIAL, dtypes.int, (UOp.const(dtypes.int, 4),), "gidx0")<1 valid = UOp(Ops.SPECIAL, dtypes.int, (UOp.const(dtypes.int, 4),), "gidx0")<1
lidx = UOp(Ops.SPECIAL, dtypes.int, (UOp.const(dtypes.int, 16),), "lidx0") lidx = UOp(Ops.SPECIAL, dtypes.int, (UOp.const(dtypes.int, 16),), "lidx0")
gate = valid&(lidx.ne(2)) gate = valid&(lidx.ne(2))
st = UOp(Ops.STORE, dtypes.void, (sbuf, lidx, UOp.const(dtypes.float, 42))) st = UOp(Ops.STORE, dtypes.void, (sbuf.index(lidx), UOp.const(dtypes.float, 42)))
barrier = UOp(Ops.BARRIER, dtypes.void, (st,)) barrier = UOp(Ops.BARRIER, dtypes.void, (st,))
lbufs = [UOp(Ops.LOAD, dtypes.float, (sbuf.index(UOp.const(dtypes.int, i)), barrier)) for i in range(4)] lbufs = [UOp(Ops.LOAD, dtypes.float, (sbuf.index(UOp.const(dtypes.int, i)), barrier)) for i in range(4)]
stores = [UOp(Ops.STORE, dtypes.void, (gbuf.index(UOp.const(dtypes.int, i), gate), lbufs[i])) for i in range(4)] stores = [UOp(Ops.STORE, dtypes.void, (gbuf.index(UOp.const(dtypes.int, i), gate), lbufs[i])) for i in range(4)]
@@ -863,8 +843,6 @@ class TestIFUOps(unittest.TestCase):
if_uops = [u for u in sink.toposort() if u.op is Ops.IF] if_uops = [u for u in sink.toposort() if u.op is Ops.IF]
self.assertEqual(len(if_uops), 1) self.assertEqual(len(if_uops), 1)
self.assertEqual(if_uops[0].src[0], gate) self.assertEqual(if_uops[0].src[0], gate)
for st in sink.src:
self.assertEqual(len(st.src), 2)
# this will be fixed with the merge gated stores bounty # this will be fixed with the merge gated stores bounty
@unittest.expectedFailure @unittest.expectedFailure
@@ -879,8 +857,6 @@ class TestIFUOps(unittest.TestCase):
if_uops = [u for u in sink.toposort() if u.op is Ops.IF] if_uops = [u for u in sink.toposort() if u.op is Ops.IF]
self.assertEqual(len(if_uops), 1) self.assertEqual(len(if_uops), 1)
self.assertEqual(if_uops[0].src[0], gate) self.assertEqual(if_uops[0].src[0], gate)
for st in sink.src:
self.assertEqual(len(st.src), 2)
class TestUOpTags(unittest.TestCase): class TestUOpTags(unittest.TestCase):
def test_inc_by_one(self): def test_inc_by_one(self):
+23 -16
View File
@@ -6,7 +6,7 @@ from tinygrad.helpers import CI, DEBUG, getenv, Timing
from tinygrad.dtype import dtypes, DType, AddrSpace from tinygrad.dtype import dtypes, DType, AddrSpace
from tinygrad.device import Buffer, Device from tinygrad.device import Buffer, Device
from tinygrad.uop.ops import Ops, UOp, UPat, KernelInfo, exec_alu # noqa F401 from tinygrad.uop.ops import Ops, UOp, UPat, KernelInfo, exec_alu # noqa F401
from tinygrad.uop.spec import spec from tinygrad.uop.spec import shared_spec
from tinygrad.renderer import ProgramSpec from tinygrad.renderer import ProgramSpec
from tinygrad.engine.realize import CompiledRunner, get_program from tinygrad.engine.realize import CompiledRunner, get_program
from tinygrad.codegen import full_rewrite from tinygrad.codegen import full_rewrite
@@ -15,10 +15,16 @@ from tinygrad.device import is_dtype_supported
from tinygrad.codegen.opt import Opt, OptOps from tinygrad.codegen.opt import Opt, OptOps
from tinygrad.renderer.ptx import PTXRenderer from tinygrad.renderer.ptx import PTXRenderer
def to_uops_list(u:list[UOp], opts=None, skip_check=False) -> list[UOp]: return full_rewrite(UOp.sink(*u), opts) def to_uops_list(u:list[UOp], ren=None) -> list[UOp]:
sink = UOp.group(*u)
for r in sink.ranges: sink = sink.end(r)
# we strip the SINK here for legacy reasons
ret = full_rewrite(sink.sink(arg=KernelInfo(opts_to_apply=())), ren)
assert ret[-1].op is Ops.SINK
return ret[:-1]
def _uops_to_prg(uops_list): def _uops_to_prg(uops_list):
uops = full_rewrite(ast:=UOp.sink(*uops_list), opts=Device[Device.DEFAULT].renderer) uops = full_rewrite(ast:=UOp.sink(*uops_list), ren=Device[Device.DEFAULT].renderer)
src = Device[Device.DEFAULT].renderer.render(uops) src = Device[Device.DEFAULT].renderer.render(uops)
has_local = Device[Device.DEFAULT].renderer.has_local has_local = Device[Device.DEFAULT].renderer.has_local
return CompiledRunner(ProgramSpec(uops[-1].arg.name if uops[-1].arg is not None else "test", src, Device.DEFAULT, ast, uops=uops, return CompiledRunner(ProgramSpec(uops[-1].arg.name if uops[-1].arg is not None else "test", src, Device.DEFAULT, ast, uops=uops,
@@ -302,6 +308,7 @@ class TestGatedStoreRewrite(unittest.TestCase):
self.assertIs(gated_uops[-1].op, Ops.STORE) self.assertIs(gated_uops[-1].op, Ops.STORE)
# scaled down version of TestLinearizerDumb.test_unmerged_ifs # scaled down version of TestLinearizerDumb.test_unmerged_ifs
@unittest.skip("we don't merge ifs anymore")
def test_merge_ifs_alt(self): def test_merge_ifs_alt(self):
gmem0 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(), (), 0) gmem0 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(), (), 0)
gmem1 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(), (), 1) gmem1 = UOp(Ops.DEFINE_GLOBAL, dtypes.float.ptr(), (), 1)
@@ -331,7 +338,7 @@ class TestLocalAccess(unittest.TestCase):
smem = uop(uops, Ops.DEFINE_LOCAL, dtypes.float32.ptr(size=16, addrspace=AddrSpace.LOCAL), (), 'smem') smem = uop(uops, Ops.DEFINE_LOCAL, dtypes.float32.ptr(size=16, addrspace=AddrSpace.LOCAL), (), 'smem')
st = uop(uops, Ops.STORE, dtypes.void, (smem.index(uop(uops, Ops.CONST, dtypes.int32, (), 0)), uop(uops, Ops.CONST, dtypes.float32, (), 42.0))) st = uop(uops, Ops.STORE, dtypes.void, (smem.index(uop(uops, Ops.CONST, dtypes.int32, (), 0)), uop(uops, Ops.CONST, dtypes.float32, (), 42.0)))
barr = uop(uops, Ops.BARRIER, dtypes.void, (st,)) barr = uop(uops, Ops.BARRIER, dtypes.void, (st,))
sres = uop(uops, Ops.LOAD, dtypes.float32, (smem.index(uop(uops, Ops.CONST, dtypes.int32, (), 0)), barr)) sres = uop(uops, Ops.LOAD, dtypes.float32, (smem.after(barr).index(uop(uops, Ops.CONST, dtypes.int32, (), 0)),))
self.assertEqual(_test_uops_result(dtypes.float32, uops, sres), 42) self.assertEqual(_test_uops_result(dtypes.float32, uops, sres), 42)
# NOTE: webgpu specific, since only webgpu performs bitpacking # NOTE: webgpu specific, since only webgpu performs bitpacking
@@ -341,7 +348,7 @@ class TestLocalAccess(unittest.TestCase):
smem = uop(uops, Ops.DEFINE_LOCAL, dtypes.uint8.ptr(size=16, addrspace=AddrSpace.LOCAL), (), 'smem') smem = uop(uops, Ops.DEFINE_LOCAL, dtypes.uint8.ptr(size=16, addrspace=AddrSpace.LOCAL), (), 'smem')
st = uop(uops, Ops.STORE, dtypes.void, (smem.index(uop(uops, Ops.CONST, dtypes.int32, (), 0)), uop(uops, Ops.CONST, dtypes.uint8, (), 42))) st = uop(uops, Ops.STORE, dtypes.void, (smem.index(uop(uops, Ops.CONST, dtypes.int32, (), 0)), uop(uops, Ops.CONST, dtypes.uint8, (), 42)))
barr = uop(uops, Ops.BARRIER, dtypes.void, (st,)) barr = uop(uops, Ops.BARRIER, dtypes.void, (st,))
sres = uop(uops, Ops.LOAD, dtypes.uint8, (smem.index(uop(uops, Ops.CONST, dtypes.int32, (), 0)), barr)) sres = uop(uops, Ops.LOAD, dtypes.uint8, (smem.after(barr).index(uop(uops, Ops.CONST, dtypes.int32, (), 0)),))
self.assertEqual(_test_uops_result(dtypes.uint8, uops, sres), 42) self.assertEqual(_test_uops_result(dtypes.uint8, uops, sres), 42)
# NOTE: webgpu specific, since only webgpu performs bitpacking # NOTE: webgpu specific, since only webgpu performs bitpacking
@@ -351,7 +358,7 @@ class TestLocalAccess(unittest.TestCase):
size = 16 size = 16
for dtype in _dtypes: for dtype in _dtypes:
temp = UOp(Ops.DEFINE_LOCAL, dtype.ptr(size=size, addrspace=AddrSpace.LOCAL), (), 'smem') temp = UOp(Ops.DEFINE_LOCAL, dtype.ptr(size=size, addrspace=AddrSpace.LOCAL), (), 'smem')
uops = to_uops_list([temp], opts=Device[Device.DEFAULT].renderer) uops = to_uops_list([temp], ren=Device[Device.DEFAULT].renderer)
out = Device[Device.DEFAULT].renderer.render(uops) out = Device[Device.DEFAULT].renderer.render(uops)
# half is supported in wgsl, so it doesn't have to be packed # half is supported in wgsl, so it doesn't have to be packed
corrected_size = size//(4//dtype.itemsize) if dtype != dtypes.half else size corrected_size = size//(4//dtype.itemsize) if dtype != dtypes.half else size
@@ -378,7 +385,7 @@ class TestAssembly(unittest.TestCase):
l1 = UOp(Ops.LOAD, dtypes.int, (g1.index(c1),)) l1 = UOp(Ops.LOAD, dtypes.int, (g1.index(c1),))
a1 = UOp(Ops.MUL, dtypes.int, (l1, c1)) a1 = UOp(Ops.MUL, dtypes.int, (l1, c1))
a2 = UOp(Ops.MUL, dtypes.int, (l1, c2)) a2 = UOp(Ops.MUL, dtypes.int, (l1, c2))
uops = to_uops_list([a1,a2], opts=Device[Device.DEFAULT].renderer) uops = to_uops_list([a1,a2], ren=Device[Device.DEFAULT].renderer)
Device[Device.DEFAULT].renderer.render(uops) Device[Device.DEFAULT].renderer.render(uops)
ops = [x.op for x in uops] ops = [x.op for x in uops]
self.assertIn(Ops.SHL, ops) self.assertIn(Ops.SHL, ops)
@@ -390,7 +397,7 @@ class TestAssembly(unittest.TestCase):
c = UOp(Ops.CONST, dt, (), 2) c = UOp(Ops.CONST, dt, (), 2)
l = UOp(Ops.LOAD, dt, (g.index(c),)) l = UOp(Ops.LOAD, dt, (g.index(c),))
a = UOp(Ops.IDIV, dt, (l, c)) a = UOp(Ops.IDIV, dt, (l, c))
uops = to_uops_list([a], opts=Device[Device.DEFAULT].renderer) uops = to_uops_list([a], ren=Device[Device.DEFAULT].renderer)
Device[Device.DEFAULT].renderer.render(uops) Device[Device.DEFAULT].renderer.render(uops)
ops = [x.op for x in uops] ops = [x.op for x in uops]
self.assertIn(Ops.SHR, ops, f"For dtype={dt} divison by power of two did not simplify to shift") self.assertIn(Ops.SHR, ops, f"For dtype={dt} divison by power of two did not simplify to shift")
@@ -401,14 +408,14 @@ class TestAssembly(unittest.TestCase):
c = UOp(Ops.CONST, dtypes.uint, (), 3) c = UOp(Ops.CONST, dtypes.uint, (), 3)
l = UOp(Ops.LOAD, dtypes.uint, (g.index(c),)) l = UOp(Ops.LOAD, dtypes.uint, (g.index(c),))
a = UOp(Ops.IDIV, dtypes.uint, (l, c)) a = UOp(Ops.IDIV, dtypes.uint, (l, c))
uops = to_uops_list([a], opts=Device[Device.DEFAULT].renderer) uops = to_uops_list([a], ren=Device[Device.DEFAULT].renderer)
Device[Device.DEFAULT].renderer.render(uops) Device[Device.DEFAULT].renderer.render(uops)
ops = [x.op for x in uops] ops = [x.op for x in uops]
self.assertIn(Ops.SHR, ops) self.assertIn(Ops.SHR, ops)
self.assertNotIn(Ops.IDIV, ops) self.assertNotIn(Ops.IDIV, ops)
b = UOp(Ops.MOD, dtypes.uint, (l, c)) b = UOp(Ops.MOD, dtypes.uint, (l, c))
uops = to_uops_list([b], opts=Device[Device.DEFAULT].renderer) uops = to_uops_list([b], ren=Device[Device.DEFAULT].renderer)
Device[Device.DEFAULT].renderer.render(uops) Device[Device.DEFAULT].renderer.render(uops)
ops = [x.op for x in uops] ops = [x.op for x in uops]
self.assertIn(Ops.SHR, ops) self.assertIn(Ops.SHR, ops)
@@ -421,7 +428,7 @@ class TestAssembly(unittest.TestCase):
c = UOp(Ops.CONST, dtypes.uint, (), 7) c = UOp(Ops.CONST, dtypes.uint, (), 7)
l = UOp(Ops.LOAD, dtypes.uint, (g.index(c),)) l = UOp(Ops.LOAD, dtypes.uint, (g.index(c),))
a = UOp(Ops.IDIV, dtypes.uint, (l, c)) a = UOp(Ops.IDIV, dtypes.uint, (l, c))
uops = to_uops_list([a], opts=Device[Device.DEFAULT].renderer) uops = to_uops_list([a], ren=Device[Device.DEFAULT].renderer)
Device[Device.DEFAULT].renderer.render(uops) Device[Device.DEFAULT].renderer.render(uops)
ops = [x.op for x in uops] ops = [x.op for x in uops]
self.assertIn(Ops.SHR, ops) self.assertIn(Ops.SHR, ops)
@@ -429,7 +436,7 @@ class TestAssembly(unittest.TestCase):
def test_fast_idiv_remove_powers_of_two(self): def test_fast_idiv_remove_powers_of_two(self):
ridx = UOp.range(2**20, 0) ridx = UOp.range(2**20, 0)
uops = to_uops_list([ridx//(7*64)], opts=Device[Device.DEFAULT].renderer) uops = to_uops_list([ridx//(7*64)], ren=Device[Device.DEFAULT].renderer)
ops = [x.op for x in uops] ops = [x.op for x in uops]
# this requires shifting out the powers of two before doing fast_idiv # this requires shifting out the powers of two before doing fast_idiv
# (((ridx0>>6)*18725)>>17) instead of (int)((((long)(ridx0)*1198373)>>29)) # (((ridx0>>6)*18725)>>17) instead of (int)((((long)(ridx0)*1198373)>>29))
@@ -453,7 +460,7 @@ class TestAssembly(unittest.TestCase):
c = UOp(Ops.CONST, dtypes.uint, (), 7) c = UOp(Ops.CONST, dtypes.uint, (), 7)
l = UOp(Ops.LOAD, dtypes.uint, (g.index(c),)) l = UOp(Ops.LOAD, dtypes.uint, (g.index(c),))
comp = l.ne(c).ne(True) comp = l.ne(c).ne(True)
uops = to_uops_list([comp], opts=Device[Device.DEFAULT].renderer) uops = to_uops_list([comp], ren=Device[Device.DEFAULT].renderer)
Device[Device.DEFAULT].renderer.render(uops) Device[Device.DEFAULT].renderer.render(uops)
ops = [x.op for x in uops] ops = [x.op for x in uops]
self.assertIn(Ops.CMPEQ, ops) self.assertIn(Ops.CMPEQ, ops)
@@ -512,7 +519,7 @@ class TestUOpStr(unittest.TestCase):
class TestUPatHelpers(unittest.TestCase): class TestUPatHelpers(unittest.TestCase):
def test_location(self): def test_location(self):
self.assertEqual(sym.patterns[-1][0].location[0].replace("\\", "/").split("/")[-1], "symbolic.py") self.assertEqual(sym.patterns[-1][0].location[0].replace("\\", "/").split("/")[-1], "symbolic.py")
self.assertEqual(spec.patterns[0][0].location[0].replace("\\", "/").split("/")[-1], "spec.py") self.assertEqual(shared_spec.patterns[0][0].location[0].replace("\\", "/").split("/")[-1], "spec.py")
test_upat = UPat(Ops.CONST, dtypes.bool) test_upat = UPat(Ops.CONST, dtypes.bool)
self.assertEqual(test_upat.location[0].split("/")[-1], __file__.replace("\\", "/").split("/")[-1]) self.assertEqual(test_upat.location[0].split("/")[-1], __file__.replace("\\", "/").split("/")[-1])
test_upat_named = test_upat.named("test_name") test_upat_named = test_upat.named("test_name")
@@ -540,10 +547,10 @@ class TestUopsObject(unittest.TestCase):
class TestUOpRender(unittest.TestCase): class TestUOpRender(unittest.TestCase):
def test_render_vectorize_same(self): def test_render_vectorize_same(self):
u = UOp(Ops.VECTORIZE, src=(UOp.const(dtypes.int, 0), UOp.const(dtypes.int, 0), UOp.const(dtypes.int, 0))) u = UOp(Ops.VECTORIZE, dtype=dtypes.int.vec(3), src=(UOp.const(dtypes.int, 0), UOp.const(dtypes.int, 0), UOp.const(dtypes.int, 0)))
self.assertEqual(u.render(), "{0, ...}") self.assertEqual(u.render(), "{0, ...}")
def test_render_vectorize_different(self): def test_render_vectorize_different(self):
u = UOp(Ops.VECTORIZE, src=(UOp.const(dtypes.int, 0), UOp.const(dtypes.int, 1), UOp.const(dtypes.int, 2))) u = UOp(Ops.VECTORIZE, dtype=dtypes.int.vec(3), src=(UOp.const(dtypes.int, 0), UOp.const(dtypes.int, 1), UOp.const(dtypes.int, 2)))
self.assertEqual(u.render(), "{0,1,2}") self.assertEqual(u.render(), "{0,1,2}")
if __name__ == '__main__': if __name__ == '__main__':
-76
View File
@@ -1,76 +0,0 @@
import unittest, random
from tinygrad.dtype import dtypes
from tinygrad.uop.ops import print_uops, UOp, Ops
from tinygrad.codegen.late.linearize import block_reorder
from tinygrad.renderer.cstyle import OpenCLRenderer
def is_toposorted(lst:list[UOp]):
seen = set()
for u in lst:
if any(p not in seen for p in u.src): return False
seen.add(u)
return True
class TestBlockReorder(unittest.TestCase):
def _test_randomize(self, golden:list[UOp]):
# test random order is always same
for _ in range(50):
# shuffle and form a valid toposort
lst = golden[:]
random.shuffle(lst)
topolst = []
for u in lst:
for p in u.toposort():
if p not in topolst: topolst.append(p)
assert is_toposorted(topolst)
for x,y in zip(golden, this_order:=block_reorder(topolst)):
if x is not y:
print_uops(golden)
print_uops(this_order)
self.assertIs(x, y)
def _test_render(self, golden:list[UOp]):
return OpenCLRenderer().render(golden)
def test_loads(self):
a = UOp(Ops.DEFINE_GLOBAL, dtype=dtypes.float.ptr(), arg=0)
b = UOp(Ops.DEFINE_GLOBAL, dtype=dtypes.float.ptr(), arg=1)
c = UOp(Ops.DEFINE_GLOBAL, dtype=dtypes.float.ptr(), arg=2)
v1 = UOp(Ops.SPECIAL, dtype=dtypes.int, src=(UOp.const(dtypes.int, 4),), arg="gidx0")
v2 = UOp(Ops.SPECIAL, dtype=dtypes.int, src=(UOp.const(dtypes.int, 4),), arg="gidx1")
v1 = v1*27
v2 = v2*4
loads = [
a.index(v1).load(dtype=dtypes.float),
a.index(v1+1).load(dtype=dtypes.float),
a.index(v1+2).load(dtype=dtypes.float),
a.index(v1+3).load(dtype=dtypes.float),
b.index(v2).load(dtype=dtypes.float),
b.index(v2+1).load(dtype=dtypes.float),
b.index(v2+2).load(dtype=dtypes.float),
b.index(v2+3).load(dtype=dtypes.float)]
#random.shuffle(loads)
sink = c.store(sum(loads)).sink()
# determine golden order
golden = block_reorder(list(sink.toposort()))
# render for test
print(self._test_render(golden))
#print_uops(golden)
# assert the loads are in this order
self.assertListEqual([g.src[0].src[1].render() for g in golden if g.op is Ops.LOAD],
['(gidx1*4)', '((gidx1*4)+1)', '((gidx1*4)+2)', '((gidx1*4)+3)',
'(gidx0*27)', '((gidx0*27)+1)', '((gidx0*27)+2)', '((gidx0*27)+3)'])
# assert math is after loads
first_math = [i for i,g in enumerate(golden) if g.op is Ops.ADD and g.dtype == dtypes.float][0]
assert not any(x.op is Ops.LOAD for x in golden[first_math:])
# confirm the sort is stable
self._test_randomize(golden)
if __name__ == '__main__':
unittest.main()
+25
View File
@@ -418,5 +418,30 @@ class TestPathTensor(unittest.TestCase):
Tensor(pathlib.Path(test_file)).tolist() Tensor(pathlib.Path(test_file)).tolist()
os.chmod(test_file, 0o644) os.chmod(test_file, 0o644)
assert Tensor(pathlib.Path(test_file)).tolist(), list(range(10)) assert Tensor(pathlib.Path(test_file)).tolist(), list(range(10))
class TestDiskTensorMovement(unittest.TestCase):
def setUp(self):
self.fn = pathlib.Path(temp("custom_disk_range"))
self.fn.unlink(missing_ok=True)
Tensor.arange(100, dtype=dtypes.uint8).to(f"disk:{str(self.fn)}").realize()
def test_simple_read(self):
t = Tensor(self.fn)
self.assertTrue(Tensor.all(t.to(None) == Tensor.arange(100, dtype=dtypes.uint8)).item())
def test_slice_read(self):
t = Tensor(self.fn)
self.assertListEqual(t[16:18].tolist(), [16,17])
def test_slice_read_cat(self):
t = Tensor(self.fn)
with self.assertRaises(AssertionError):
self.assertListEqual(Tensor.cat(t[16:18], t[20:22]).tolist(), [16,17,20,21])
def test_slice_sum(self):
t = Tensor(self.fn)
with self.assertRaises(AssertionError):
self.assertListEqual((t[16:18]+t[20:22]).tolist(), [16+20,17+21])
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
+9
View File
@@ -24,6 +24,15 @@ class TestElfLoader(unittest.TestCase):
''' '''
with self.assertRaisesRegex(RuntimeError, 'evil_external_function'): with self.assertRaisesRegex(RuntimeError, 'evil_external_function'):
ClangJITCompiler().compile(src) ClangJITCompiler().compile(src)
def test_link(self):
src = '''
float powf(float, float); // from libm
float test(float x, float y) { return powf(x, y); }
'''
args = ('-x', 'c', '-c', '-target', f'{platform.machine()}-none-unknown-elf', '-march=native', '-fPIC', '-O2', '-ffreestanding', '-nostdlib')
obj = subprocess.check_output(('clang',) + args + ('-', '-o', '-'), input=src.encode())
with self.assertRaisesRegex(RuntimeError, 'powf'): elf_loader(obj)
elf_loader(obj, link_libs=['m'])
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
+22 -7
View File
@@ -1,11 +1,12 @@
import unittest, math import unittest, math
from tinygrad import dtypes from tinygrad import dtypes
from tinygrad.helpers import all_same from tinygrad.helpers import all_same, Context
from tinygrad.uop.ops import GroupOp, UOp, Ops, exec_alu, PatternMatcher, TrackedPatternMatcher, UPat from tinygrad.uop.ops import GroupOp, UOp, Ops, exec_alu, PatternMatcher, TrackedPatternMatcher, UPat
from tinygrad.codegen import full_rewrite_to_sink from tinygrad.codegen import full_rewrite_to_sink
from hypothesis import given, strategies as strat from hypothesis import given, strategies as strat
# Helper function to apply the graph rewrite # Helper function to apply the graph rewrite
@Context(SPEC=0)
def apply_rewrite(expr): def apply_rewrite(expr):
return full_rewrite_to_sink(expr.sink()).src[0] return full_rewrite_to_sink(expr.sink()).src[0]
@@ -305,19 +306,19 @@ class TestRecurse(unittest.TestCase):
graph_rewrite(a, pm, bottom_up=True) graph_rewrite(a, pm, bottom_up=True)
def test_inf_loop(self): def test_inf_loop(self):
a = UOp.variable('a', 0, 10) a = UOp.const(dtypes.int, 3)
pm = PatternMatcher([ pm = PatternMatcher([
(UPat(Ops.DEFINE_VAR, name="x"), lambda x: x.replace(op=Ops.CONST)), (UPat(Ops.CONST, arg=3, name="x"), lambda x: x.replace(arg=4)),
(UPat(Ops.CONST, name="x"), lambda x: x.replace(op=Ops.DEFINE_VAR)), (UPat(Ops.CONST, arg=4, name="x"), lambda x: x.replace(arg=3)),
]) ])
with self.assertRaises(RuntimeError): with self.assertRaises(RuntimeError):
graph_rewrite(a, pm) graph_rewrite(a, pm)
def test_inf_loop_bottom_up(self): def test_inf_loop_bottom_up(self):
a = UOp.variable('a', 0, 10) a = UOp.const(dtypes.int, 3)
pm = PatternMatcher([ pm = PatternMatcher([
(UPat(Ops.DEFINE_VAR, name="x"), lambda x: x.replace(op=Ops.CONST)), (UPat(Ops.CONST, arg=3, name="x"), lambda x: x.replace(arg=4)),
(UPat(Ops.CONST, name="x"), lambda x: x.replace(op=Ops.DEFINE_VAR)), (UPat(Ops.CONST, arg=4, name="x"), lambda x: x.replace(arg=3)),
]) ])
with self.assertRaises(RuntimeError): with self.assertRaises(RuntimeError):
graph_rewrite(a, pm, bottom_up=True) graph_rewrite(a, pm, bottom_up=True)
@@ -334,5 +335,19 @@ class TestBidirectional(unittest.TestCase):
graph_rewrite(c, pm, ctx=ctx_list, bpm=bpm) graph_rewrite(c, pm, ctx=ctx_list, bpm=bpm)
self.assertListEqual(ctx_list, [('+', True), (1, True), (1, False), (2, True), (2, False), ('+', False)]) self.assertListEqual(ctx_list, [('+', True), (1, True), (1, False), (2, True), (2, False), ('+', False)])
class TestStopEarly(unittest.TestCase):
def test_stop_early(self):
a = UOp.const(dtypes.int, 3)
b = UOp.const(dtypes.int, 4)
c = a+b
cn = UOp.const(dtypes.int, 7)
d = UOp.const(dtypes.int, 2)
def visit_const(c:UOp):
print(f"visit {c.arg}")
assert c.arg not in (3,4)
pm_cvisit = PatternMatcher([(UPat(Ops.CONST, name="c"), visit_const),])
ret = (c+d).substitute({c:cn}, extra_pm=pm_cvisit)
assert ret == cn+d
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
+49
View File
@@ -3,6 +3,8 @@ import hashlib, random, unittest
from tinygrad import Tensor, Device, getenv, dtypes from tinygrad import Tensor, Device, getenv, dtypes
from tinygrad.device import is_dtype_supported from tinygrad.device import is_dtype_supported
from tinygrad.helpers import CI from tinygrad.helpers import CI
from tinygrad.uop.ops import UOp
from tinygrad.engine.jit import TinyJit
@unittest.skipUnless(is_dtype_supported(dtypes.uint8) and is_dtype_supported(dtypes.uint64), "Device must support uint8 and uint64") @unittest.skipUnless(is_dtype_supported(dtypes.uint8) and is_dtype_supported(dtypes.uint64), "Device must support uint8 and uint64")
@unittest.skipIf(getenv("MOCKGPU") and Device.DEFAULT == "NV", "crashes in NV CI") @unittest.skipIf(getenv("MOCKGPU") and Device.DEFAULT == "NV", "crashes in NV CI")
@@ -72,5 +74,52 @@ class TestKeccak(unittest.TestCase):
data = b"\x00" * 1000 data = b"\x00" * 1000
self.assertEqual(bytes(Tensor(data).keccak("shake_128").tolist()), hashlib.shake_128(data).digest(16)) self.assertEqual(bytes(Tensor(data).keccak("shake_128").tolist()), hashlib.shake_128(data).digest(16))
def test_variable_bs(self):
data = Tensor([b"abc", b"abc", b"abc"], dtype=dtypes.uint8).repeat(2048, 1)
bs = UOp.variable("bs", 1, 4096).bind(1)
out = data.shrink_to(bs, data.shape[-1]).keccak().shrink_to(1, 32)
self.assertEqual(bytes(out[0].tolist()), bytearray.fromhex("3a985da74fe225b2 045c172d6bd390bd 855f086e3e9d525b 46bfe24511431532"))
bs = UOp.variable("bs", 1, 4096).bind(2)
out = data.shrink_to(bs, data.shape[-1]).keccak().shrink_to(2, 32)
self.assertEqual(bytes(out[0].tolist()), bytearray.fromhex("3a985da74fe225b2 045c172d6bd390bd 855f086e3e9d525b 46bfe24511431532"))
self.assertEqual(bytes(out[1].tolist()), bytearray.fromhex("3a985da74fe225b2 045c172d6bd390bd 855f086e3e9d525b 46bfe24511431532"))
bs = UOp.variable("bs", 1, 4096).bind(3)
data = Tensor([b"abc", b"abc", b"def"], dtype=dtypes.uint8).repeat(2048, 1)
out = data.shrink_to(bs, data.shape[-1]).keccak().shrink_to(3, 32)
self.assertEqual(bytes(out[0].tolist()), bytearray.fromhex("3a985da74fe225b2 045c172d6bd390bd 855f086e3e9d525b 46bfe24511431532"))
self.assertEqual(bytes(out[1].tolist()), bytearray.fromhex("3a985da74fe225b2 045c172d6bd390bd 855f086e3e9d525b 46bfe24511431532"))
self.assertEqual(bytes(out[2].tolist()), bytearray.fromhex("8e0d8f672252acb0 ffc5093db8653b18 1513bf9a2097e737 b4f73533dcaf46df"))
def test_variable_bs_jit(self):
def f(data):
return data.keccak()
jit_f = TinyJit(f)
data = Tensor([b"abc", b"abc", b"abc"], dtype=dtypes.uint8).repeat(2048, 1)
# initialize jit
for _ in range(3):
bs = UOp.variable("bs", 1, 4096).bind(4096)
_ = jit_f(data.shrink_to(bs, data.shape[-1]))
bs = UOp.variable("bs", 1, 4096).bind(1)
out = jit_f(data.shrink_to(bs, data.shape[-1])).shrink_to(1, 32)
self.assertEqual(bytes(out[0].tolist()), bytearray.fromhex("3a985da74fe225b2 045c172d6bd390bd 855f086e3e9d525b 46bfe24511431532"))
bs = UOp.variable("bs", 1, 4096).bind(2)
out = jit_f(data.shrink_to(bs, data.shape[-1])).shrink_to(2, 32)
self.assertEqual(bytes(out[0].tolist()), bytearray.fromhex("3a985da74fe225b2 045c172d6bd390bd 855f086e3e9d525b 46bfe24511431532"))
self.assertEqual(bytes(out[1].tolist()), bytearray.fromhex("3a985da74fe225b2 045c172d6bd390bd 855f086e3e9d525b 46bfe24511431532"))
bs = UOp.variable("bs", 1, 4096).bind(3)
data = Tensor([b"abc", b"abc", b"def"], dtype=dtypes.uint8).repeat(2048, 1)
out = jit_f(data.shrink_to(bs, data.shape[-1])).shrink_to(3, 32)
self.assertEqual(bytes(out[0].tolist()), bytearray.fromhex("3a985da74fe225b2 045c172d6bd390bd 855f086e3e9d525b 46bfe24511431532"))
self.assertEqual(bytes(out[1].tolist()), bytearray.fromhex("3a985da74fe225b2 045c172d6bd390bd 855f086e3e9d525b 46bfe24511431532"))
self.assertEqual(bytes(out[2].tolist()), bytearray.fromhex("8e0d8f672252acb0 ffc5093db8653b18 1513bf9a2097e737 b4f73533dcaf46df"))
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
+2 -6
View File
@@ -5,8 +5,6 @@ import numpy as np
from tinygrad import Tensor, dtypes, Device, TinyJit from tinygrad import Tensor, dtypes, Device, TinyJit
from tinygrad.device import is_dtype_supported from tinygrad.device import is_dtype_supported
from tinygrad.shape.shapetracker import ShapeTracker
from tinygrad.shape.view import View
from tinygrad.helpers import CI, all_same, prod from tinygrad.helpers import CI, all_same, prod
random.seed(42) random.seed(42)
@@ -22,11 +20,13 @@ def consec(shape, start=1):
# creates strided tensor with base set to reference tensor's base, equivalent to torch.set_() # creates strided tensor with base set to reference tensor's base, equivalent to torch.set_()
def set_(reference: Tensor, shape, strides, offset): def set_(reference: Tensor, shape, strides, offset):
raise NotImplementedError("need to implement without calling uop.view") raise NotImplementedError("need to implement without calling uop.view")
"""
if reference.uop.base.realized is None: reference.realize() if reference.uop.base.realized is None: reference.realize()
assert reference.uop.base.realized, "base has to be realized before setting it to strided's base" assert reference.uop.base.realized, "base has to be realized before setting it to strided's base"
strided = Tensor(reference.uop.view(ShapeTracker((View.create(shape=shape, strides=strides, offset=offset),)))) strided = Tensor(reference.uop.view(ShapeTracker((View.create(shape=shape, strides=strides, offset=offset),))))
assert strided.uop.st.real_strides() == strides, "real_strides should equal strides for strided" assert strided.uop.st.real_strides() == strides, "real_strides should equal strides for strided"
return strided return strided
"""
def clone(original:Tensor): return original.clone() def clone(original:Tensor): return original.clone()
def copy_(src:Tensor, other:Tensor) -> Tensor: return src.clone() def copy_(src:Tensor, other:Tensor) -> Tensor: return src.clone()
@@ -501,10 +501,6 @@ class TestIndexing(unittest.TestCase):
y = x[:, :, :, 1] y = x[:, :, :, 1]
z = y[:, 1:1, :] z = y[:, 1:1, :]
numpy_testing_assert_equal_helper((2, 0, 4), z.shape) numpy_testing_assert_equal_helper((2, 0, 4), z.shape)
# this isn't technically necessary, but matches NumPy stride calculations.
# NOTE: this is empty and shouldn't have strides
numpy_testing_assert_equal_helper((True, True, True), z.uop.st.is_expanded())
self.assertTrue(z.uop.st.contiguous)
@unittest.skip("bool indexing not supported") @unittest.skip("bool indexing not supported")
def test_index_getitem_copy_bools_slices(self): def test_index_getitem_copy_bools_slices(self):
+3 -3
View File
@@ -20,8 +20,8 @@ class TestKernelize(unittest.TestCase):
self.assertEqual(len([s for s in a0.uop.toposort() if s.op is Ops.KERNEL]), 2) self.assertEqual(len([s for s in a0.uop.toposort() if s.op is Ops.KERNEL]), 2)
self.assertIs(a1.uop.base.op, Ops.REDUCE_AXIS) self.assertIs(a1.uop.base.op, Ops.REDUCE_AXIS)
# input Tensor and user contiguous kernelize # input Tensor and user contiguous kernelize
self.assertIs(a0.uop.base.op, Ops.ASSIGN) self.assertIs(a0.uop.base.op, Ops.AFTER)
self.assertIs(a.uop.base.op, Ops.ASSIGN) self.assertIs(a.uop.base.op, Ops.AFTER)
def test_two_reduce_w_add(self): def test_two_reduce_w_add(self):
a = Tensor.ones(16,16).contiguous() a = Tensor.ones(16,16).contiguous()
@@ -31,7 +31,7 @@ class TestKernelize(unittest.TestCase):
# NOTE: the +1 is fused with a1, so a1 is not kernelized # NOTE: the +1 is fused with a1, so a1 is not kernelized
self.assertIs(a1.uop.base.op, Ops.REDUCE_AXIS) self.assertIs(a1.uop.base.op, Ops.REDUCE_AXIS)
# the input to the REDUCE_AXIS is an ASSIGN though # the input to the REDUCE_AXIS is an ASSIGN though
self.assertIs(a1.uop.base.src[0].base.op, Ops.ASSIGN) self.assertIs(a1.uop.base.src[0].base.op, Ops.AFTER)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

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