mirror of
https://github.com/tinygrad/tinygrad.git
synced 2026-08-21 06:26:06 +00:00
Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fabe7c9849 | ||
|
|
8be7844b2e | ||
|
|
3aa2277b8f | ||
|
|
a069a45d14 | ||
|
|
a498ec9c18 | ||
|
|
8f740e07ff | ||
|
|
533f18b22c | ||
|
|
af4479c169 | ||
|
|
e7c057d5dc |
@@ -56,7 +56,7 @@ class TinyGPUViewModel: NSObject {
|
||||
}
|
||||
#endif
|
||||
|
||||
private let dextIdentifier: String = Bundle.main.bundleIdentifier! + ".Driver"
|
||||
private let dextIdentifier: String = "org.tinygrad.tinygpu.edriver"
|
||||
|
||||
public var dextLoadingState: String {
|
||||
switch state {
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
<string>IOUserService</string>
|
||||
<key>IOMatchCategory</key>
|
||||
<string>TinyGPUDriver</string>
|
||||
<key>IOPCIPrimaryMatch</key>
|
||||
<string>0x70001002&0xF000FFFF</string>
|
||||
<key>IOPCIClassMatch</key>
|
||||
<string>0x03000000</string>
|
||||
<key>IOPCITunnelCompatible</key>
|
||||
<true/>
|
||||
<key>IOProviderClass</key>
|
||||
|
||||
@@ -87,7 +87,7 @@ kern_return_t TinyGPUDriver::Start_Impl(IOService* in_provider)
|
||||
}
|
||||
off = next;
|
||||
}
|
||||
ivars->pci->Reset(0);
|
||||
ivars->pci->Reset(kIOPCIDeviceResetTypeHotReset);
|
||||
#endif
|
||||
|
||||
uint16_t commandRegister;
|
||||
@@ -221,3 +221,39 @@ error:
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,11 @@
|
||||
<array>
|
||||
<dict>
|
||||
<key>IOPCIMatch</key>
|
||||
<string>0x70001002&0xF000FFFF</string>
|
||||
<string>0x00001002&0x0000FFFF</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IOPCIMatch</key>
|
||||
<string>0x000010de&0x0000FFFF</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>com.apple.developer.driverkit.allow-any-userclient-access</key>
|
||||
|
||||
@@ -28,6 +28,11 @@ public:
|
||||
|
||||
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 */
|
||||
|
||||
@@ -62,8 +62,41 @@ kern_return_t TinyGPUDriverUserClient::Stop_Impl(IOService* in_provider)
|
||||
return Stop(in_provider, SUPERDISPATCH);
|
||||
}
|
||||
|
||||
kern_return_t TinyGPUDriverUserClient::ExternalMethod(uint64_t in_selector, IOUserClientMethodArguments* in_arguments, const IOUserClientMethodDispatch* in_dispatch, OSObject* in_target, void* in_reference)
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,13 @@
|
||||
|
||||
#include <DriverKit/IOUserClient.iig>
|
||||
|
||||
enum TinyGPURPC
|
||||
{
|
||||
ReadCfg,
|
||||
WriteCfg,
|
||||
Reset
|
||||
};
|
||||
|
||||
class TinyGPUDriverUserClient : public IOUserClient
|
||||
{
|
||||
public:
|
||||
|
||||
+16
-12
@@ -1,6 +1,6 @@
|
||||
import unittest
|
||||
from tinygrad import Tensor, nn
|
||||
from tinygrad.helpers import Context, GlobalCounters
|
||||
from tinygrad.helpers import Context, GlobalCounters, CI
|
||||
from tinygrad.uop.ops import graph_rewrite, PatternMatcher, UPat, Ops
|
||||
|
||||
class TestRangeifyAssign(unittest.TestCase):
|
||||
@@ -17,8 +17,22 @@ class TestRangeifyAssign(unittest.TestCase):
|
||||
self.assertListEqual(lst, lst3)
|
||||
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)
|
||||
|
||||
# *** non CI rangeify tests below this line ***
|
||||
|
||||
N = 256
|
||||
|
||||
@unittest.skipIf(CI, "useless in CI, doesn't test anything")
|
||||
class TestRangeifyOpt(unittest.TestCase):
|
||||
def test_randperm(self):
|
||||
Tensor.randperm(10000).realize()
|
||||
@@ -54,6 +68,7 @@ class TestRangeifyOpt(unittest.TestCase):
|
||||
A = Tensor.empty(8,8,8,8).permute(1,0,3,2).flatten()
|
||||
A.sum().realize()
|
||||
|
||||
@unittest.skipIf(CI, "useless in CI, doesn't test anything")
|
||||
class TestRangeify(unittest.TestCase):
|
||||
def test_groupnorm(self):
|
||||
# ranges 1 and 3 are merging
|
||||
@@ -280,16 +295,5 @@ class TestRangeifyPM(unittest.TestCase):
|
||||
b = self.base.pad(((0,1),(0,0))).pad(((0,0),(0,1)))
|
||||
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__':
|
||||
unittest.main()
|
||||
|
||||
@@ -810,6 +810,13 @@ class TestTensorMetadata(unittest.TestCase):
|
||||
self.assertEqual(len(si.metadata), 1)
|
||||
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):
|
||||
x = Tensor.rand(3, requires_grad=True)
|
||||
y = Tensor.rand(3, requires_grad=True)
|
||||
|
||||
+16
-7
@@ -442,7 +442,7 @@ class TestVizMemoryLayout(BaseTestViz):
|
||||
profile_ret = load_profile(Buffer.profile_events)
|
||||
ret = profile_ret["layout"][f"{a.device} Memory"]
|
||||
self.assertEqual(ret["peak"], 2)
|
||||
self.assertEqual(len(ret["events"]), 2)
|
||||
self.assertEqual(len(ret["events"]), 4)
|
||||
|
||||
def test_del_once(self):
|
||||
a = _alloc(1)
|
||||
@@ -451,7 +451,7 @@ class TestVizMemoryLayout(BaseTestViz):
|
||||
profile_ret = load_profile(Buffer.profile_events)
|
||||
ret = profile_ret["layout"][f"{b.device} Memory"]
|
||||
self.assertEqual(ret["peak"], 1)
|
||||
self.assertEqual(len(ret["events"]), 3)
|
||||
self.assertEqual(len(ret["events"]), 4)
|
||||
|
||||
def test_alloc_free(self):
|
||||
a = _alloc(1)
|
||||
@@ -461,7 +461,7 @@ class TestVizMemoryLayout(BaseTestViz):
|
||||
profile_ret = load_profile(Buffer.profile_events)
|
||||
ret = profile_ret["layout"][f"{c.device} Memory"]
|
||||
self.assertEqual(ret["peak"], 2)
|
||||
self.assertEqual(len(ret["events"]), 4)
|
||||
self.assertEqual(len(ret["events"]), 6)
|
||||
|
||||
def test_free_last(self):
|
||||
bufs = []
|
||||
@@ -480,15 +480,24 @@ class TestVizMemoryLayout(BaseTestViz):
|
||||
self.assertEqual(len(profile["markers"]), 6)
|
||||
|
||||
def test_producer_simple(self):
|
||||
a = Tensor.empty(10, device="NULL")
|
||||
Tensor.realize(a.add(1), a.add(2))
|
||||
b = Tensor.empty(10, device="NULL")
|
||||
Tensor.realize(b.add(1))
|
||||
a = Tensor.ones(10, device="NULL")
|
||||
Tensor.realize(a.add(1).contiguous())
|
||||
b = Tensor.ones(10, device="NULL")
|
||||
Tensor.realize(b.add(1).contiguous())
|
||||
profile = load_profile(cpu_events+Buffer.profile_events)
|
||||
buffers = profile["layout"]["NULL Memory"]["events"]
|
||||
programs = profile["layout"]["NULL"]["events"]
|
||||
user_cnt = [len(b["arg"]["users"]) for b in buffers if b["arg"].get("users")]
|
||||
self.assertEqual(len(user_cnt), len(programs))
|
||||
|
||||
def test_inflight_buf(self):
|
||||
a = Tensor.empty(1, device="NULL")
|
||||
n = 4
|
||||
for i in range(n): (a+i).realize()
|
||||
profile = load_profile(cpu_events+Buffer.profile_events)
|
||||
buffers = profile["layout"]["NULL Memory"]["events"]
|
||||
user_cnt = [len(b["arg"]["users"]) for b in buffers if b["arg"].get("users")]
|
||||
self.assertEqual(max(user_cnt), n)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -97,7 +97,7 @@ class Scheduler:
|
||||
new_rng = UOp.range(amount, self.maxarg+1, new_type) if input_new_rng is None else input_new_rng
|
||||
replaced_rng = rng.replace(src=(UOp.const(dtypes.int, old_sz),))
|
||||
sub_axis = (new_rng * old_sz + replaced_rng) if top else (replaced_rng * amount + new_rng)
|
||||
self.ast = self.ast.substitute({rng:sub_axis}, name=f"shift {rng.arg[0]} {amount} {str(new_type).split('.')[1].lower()}")
|
||||
self.ast = self.ast.substitute({rng:sub_axis}, name=f"shift {rng.arg[:-1]} {amount} {str(new_type).split('.')[1].lower()}")
|
||||
return replaced_rng, new_rng
|
||||
|
||||
def ranges_of(self, *axis_type:AxisType) -> list[UOp]: return [r for r in self.rngs if r.arg[-1] in axis_type]
|
||||
@@ -200,13 +200,14 @@ class Scheduler:
|
||||
self.ast = self.ast.substitute(replaces, f"padto {rng.arg[:-1]} {opt.arg}")
|
||||
elif opt.op is OptOps.SWAP:
|
||||
try:
|
||||
altrng = self.rngs[opt.arg]
|
||||
altrng:UOp = self.rngs[opt.arg]
|
||||
except IndexError:
|
||||
raise KernelOptError
|
||||
check(rng.arg[-1] == AxisType.GLOBAL and altrng.arg[-1] == AxisType.GLOBAL, "swap only for globals")
|
||||
self.ast = self.ast.substitute({rng:rng.replace(arg=(*altrng.arg[0:-1], rng.arg[-1]), tag=1),
|
||||
altrng:altrng.replace(arg=(*rng.arg[0:-1], altrng.arg[-1]), tag=1)})
|
||||
self.ast = graph_rewrite(self.ast, remove_tags)
|
||||
altrng:altrng.replace(arg=(*rng.arg[0:-1], altrng.arg[-1]), tag=1)},
|
||||
name=f"swap {rng.arg[:-1]} {altrng.arg[:-1]}")
|
||||
self.ast = graph_rewrite(self.ast, remove_tags, name="swap remove tags")
|
||||
else:
|
||||
raise KernelOptError(f"unsupported opt {opt.op}")
|
||||
|
||||
|
||||
@@ -97,14 +97,12 @@ pm_reduce_collapse = PatternMatcher([
|
||||
((UPat.var("x")+UPat.var("y")).reduce(arg=Ops.ADD, allow_any_len=True, name="r"),
|
||||
lambda x,y,r: x.reduce(*r.src[1:], arg=Ops.ADD) + y.reduce(*r.src[1:],arg=Ops.ADD)),
|
||||
# MUL casted bool
|
||||
((UPat.var("x") * UPat.var("gate", dtype=dtypes.bool).cast().or_broadcasted(name="b")),
|
||||
lambda x,gate,b=None: gate.broadcast(x.dtype.count).where(x, 0) if b is not None else gate.where(x, 0)),
|
||||
((UPat.var("x") * UPat.var("gate", dtype=dtypes.bool).cast()), lambda x,gate: gate.where(x, 0)),
|
||||
# reduce on gated load becomes can substitute the range and remove the reduce
|
||||
((UPat.var("idx")!=(UPat(Ops.RANGE, name="r").or_casted())).where(0, UPat.var("expr")).reduce(UPat.var("r"), arg=Ops.ADD),
|
||||
lambda r,idx,expr: (v:=(idx.cast(r.dtype) >= 0) & (idx.cast(r.dtype) < r.src[0])).where(expr.substitute({r:idx.cast(r.dtype).valid(v)}),0)),
|
||||
# AND on WHERE
|
||||
((UPat.any(UPat(Ops.DEFINE_VAR, name="x"), UPat(Ops.DEFINE_VAR).gep(name="x")) & UPat.var("y")) \
|
||||
.where(UPat.cvar("c"), 0).reduce(arg=Ops.ADD, allow_any_len=True, name="r"),
|
||||
((UPat(Ops.DEFINE_VAR, name="x") & UPat.var("y")).where(UPat.cvar("c"), 0).reduce(arg=Ops.ADD, allow_any_len=True, name="r"),
|
||||
lambda x,y,c,r: y.where(c, 0).reduce(*r.src[1:], arg=Ops.ADD)*x.cast(c.dtype)),
|
||||
# remove REDUCEs that no longer have a RANGE in the src
|
||||
(UPat(Ops.REDUCE, name="red"), reduce_rangeless),
|
||||
|
||||
+11
-7
@@ -4,13 +4,13 @@ from tinygrad.uop.ops import UOp, PatternMatcher, UPat, Ops, all_metadata
|
||||
from tinygrad.helpers import argsort
|
||||
|
||||
def reduce_gradient(ctx:UOp, ret:UOp):
|
||||
def to_inp_shape(x): return x.reshape(x.shape+(1,)*(len(ret.src[0].shape)-len(x.shape))).expand(ret.src[0].shape)
|
||||
if ret.arg[0] == Ops.ADD: return (to_inp_shape(ctx),)
|
||||
def broadcast_to_input(x): return x.reshape(x.shape+(1,)*(len(ret.src[0].shape)-len(x.shape))).expand(ret.src[0].shape)
|
||||
if ret.arg[0] == Ops.ADD: return (broadcast_to_input(ctx),)
|
||||
if ret.arg[0] == Ops.MAX:
|
||||
max_is_1s = ret.src[0].eq(to_inp_shape(ret)).cast(ctx.dtype)
|
||||
div = to_inp_shape(max_is_1s.r(Ops.ADD, ret.arg[1]))
|
||||
return ((max_is_1s/div) * to_inp_shape(ctx),)
|
||||
if ret.arg[0] == Ops.MUL: return (to_inp_shape(ctx * ret) / ret.src[0],)
|
||||
mask = ret.src[0].eq(broadcast_to_input(ret)).cast(ctx.dtype)
|
||||
count = mask.r(Ops.ADD, ret.arg[1])
|
||||
return ((mask/broadcast_to_input(count)) * broadcast_to_input(ctx),)
|
||||
if ret.arg[0] == Ops.MUL: return (broadcast_to_input(ctx * ret) / ret.src[0],)
|
||||
|
||||
# ctx is grad_output
|
||||
pm_gradient = PatternMatcher([
|
||||
@@ -60,5 +60,9 @@ def compute_gradient(root:UOp, root_grad:UOp, targets:set[UOp]) -> dict[UOp, UOp
|
||||
if v is None: continue
|
||||
if k in grads: grads[k] = grads[k] + v
|
||||
else: grads[k] = v
|
||||
if len(forward_metadata:=all_metadata.get(t0, ())): all_metadata[v] = tuple(dataclasses.replace(x, backward=True) for x in forward_metadata)
|
||||
if len(forward_metadata:=all_metadata.get(t0, ())):
|
||||
backward_metadata = tuple(dataclasses.replace(x, backward=True) for x in forward_metadata)
|
||||
# we add the backward metadata to everything new in the graph
|
||||
for bw_uop in v.toposort(lambda x: x not in (t0, *t0.src, grads[t0])):
|
||||
all_metadata[bw_uop] = all_metadata.get(bw_uop, ())+backward_metadata
|
||||
return grads
|
||||
|
||||
@@ -50,7 +50,7 @@ class Optimizer:
|
||||
if self.fused:
|
||||
# optimizer fusion just concatenates all the buffers, runs the _step, then splits them back up
|
||||
out, extra = self._step([Tensor.cat(*[t.flatten() for t in self.params], dim=0)],
|
||||
[Tensor.cat(*[unwrap(t.grad).flatten() for t in self.params], dim=0)])
|
||||
[Tensor.cat(*[unwrap(t.grad).contiguous().flatten() for t in self.params], dim=0)])
|
||||
updated_params = [out[0][self.pos_params[i]:self.pos_params[i+1]].reshape(tt.shape) for i, tt in enumerate(self.params)]
|
||||
else:
|
||||
updated_params, extra = self._step(self.params, [unwrap(t.grad) for t in self.params])
|
||||
|
||||
@@ -111,7 +111,7 @@ class NVCommandQueue(HWQueue[HCQSignal, 'NVDevice', 'NVProgram', 'NVArgsState'])
|
||||
def _submit_to_gpfifo(self, dev:NVDevice, gpfifo:GPFifo):
|
||||
if dev == self.binded_device: cmdq_addr = self.hw_page.va_addr
|
||||
else:
|
||||
cmdq_addr = dev.cmdq_allocator.alloc(len(self._q) * 4)
|
||||
cmdq_addr = dev.cmdq_allocator.alloc(len(self._q) * 4, 16)
|
||||
cmdq_wptr = (cmdq_addr - dev.cmdq_page.va_addr) // 4
|
||||
dev.cmdq[cmdq_wptr : cmdq_wptr + len(self._q)] = array.array('I', self._q)
|
||||
|
||||
@@ -156,10 +156,14 @@ class NVComputeQueue(NVCommandQueue):
|
||||
for i in range(2):
|
||||
if self.active_qmd.read(f'release{i}_enable') == 0:
|
||||
self.active_qmd.write(**{f'release{i}_enable': 1})
|
||||
self.bind_sints_to_mem(signal.value_addr, mem=self.active_qmd_buf.cpu_view(), fmt='Q', mask=0xfffffffff,
|
||||
offset=self.active_qmd.field_offset(f'release{i}_address_lower' if self.active_qmd.ver<4 else f'release_semaphore{i}_addr_lower'))
|
||||
self.bind_sints_to_mem(value, mem=self.active_qmd_buf.cpu_view(), fmt='Q',
|
||||
offset=self.active_qmd.field_offset(f'release{i}_payload_lower' if self.active_qmd.ver<4 else f'release_semaphore{i}_payload_lower'))
|
||||
|
||||
addr_off = self.active_qmd.field_offset(f'release{i}_address_lower' if self.active_qmd.ver<4 else f'release_semaphore{i}_addr_lower')
|
||||
self.bind_sints_to_mem(signal.value_addr & 0xffffffff, mem=self.active_qmd_buf.cpu_view(), fmt='I', offset=addr_off)
|
||||
self.bind_sints_to_mem(signal.value_addr >> 32, mem=self.active_qmd_buf.cpu_view(), fmt='I', mask=0xf, offset=addr_off+4)
|
||||
|
||||
val_off = self.active_qmd.field_offset(f'release{i}_payload_lower' if self.active_qmd.ver<4 else f'release_semaphore{i}_payload_lower')
|
||||
self.bind_sints_to_mem(value & 0xffffffff, mem=self.active_qmd_buf.cpu_view(), fmt='I', offset=val_off)
|
||||
self.bind_sints_to_mem(value >> 32, mem=self.active_qmd_buf.cpu_view(), fmt='I', offset=val_off+4)
|
||||
return self
|
||||
|
||||
self.nvm(0, nv_gpu.NVC56F_SEM_ADDR_LO, *data64_le(signal.value_addr), *data64_le(value),
|
||||
@@ -384,7 +388,7 @@ class NVKIface:
|
||||
if made.params.status != 0: raise RuntimeError(f"_gpu_map_to_cpu returned {get_error_str(made.params.status)}")
|
||||
return fd_dev.mmap(target, size, mmap.PROT_READ|mmap.PROT_WRITE, mmap.MAP_SHARED | (MAP_FIXED if target is not None else 0), 0)
|
||||
|
||||
def alloc(self, size:int, host=False, uncached=False, cpu_access=False, contiguous=False, map_flags=0, cpu_addr=None) -> HCQBuffer:
|
||||
def alloc(self, size:int, host=False, uncached=False, cpu_access=False, contiguous=False, map_flags=0, cpu_addr=None, **kwargs) -> HCQBuffer:
|
||||
# Uncached memory is "system". Use huge pages only for gpu memory.
|
||||
page_size = (4 << (12 if OSX else 10)) if uncached or host else ((2 << 20) if size >= (8 << 20) else (4 << (12 if OSX else 10)))
|
||||
size = round_up(size, page_size)
|
||||
@@ -455,13 +459,13 @@ class PCIIface(PCIIfaceBase):
|
||||
def __init__(self, dev, dev_id):
|
||||
super().__init__(dev, dev_id, vendor=0x10de, devices=[0x2204, 0x2684, 0x2b85], bars=[0, 1], vram_bar=1,
|
||||
va_start=NVMemoryManager.va_allocator.base, va_size=NVMemoryManager.va_allocator.size)
|
||||
System.reserve_hugepages(64)
|
||||
if not OSX: System.reserve_hugepages(64)
|
||||
|
||||
self.pci_dev.write_config(pci.PCI_COMMAND, self.pci_dev.read_config(pci.PCI_COMMAND, 2) | pci.PCI_COMMAND_MASTER, 2)
|
||||
self.dev_impl:NVDev = NVDev(self.pci_dev.pcibus, self.pci_dev.map_bar(0, fmt='I'), self.pci_dev.map_bar(1),
|
||||
self.pci_dev.read_config(pci.PCI_VENDOR_ID, 4), self.pci_dev.read_config(pci.PCI_SUBSYSTEM_VENDOR_ID, 4),
|
||||
self.pci_dev.read_config(pci.PCI_REVISION_ID, 1), self.pci_dev.bar_info)
|
||||
self.root, self.gpu_instance, self.p2p_base_addr = 0xc1000000, 0, self.pci_dev.bar_info[1][0]
|
||||
self.root, self.gpu_instance = 0xc1000000, 0
|
||||
self.rm_alloc(0, nv_gpu.NV01_ROOT, nv_gpu.NV0000_ALLOC_PARAMETERS())
|
||||
|
||||
# Setup classes for the GPU
|
||||
@@ -508,7 +512,7 @@ class NVDevice(HCQCompiled[HCQSignal]):
|
||||
channel_params = nv_gpu.NV_CHANNEL_GROUP_ALLOCATION_PARAMETERS(engineType=nv_gpu.NV2080_ENGINE_TYPE_GRAPHICS)
|
||||
channel_group = self.iface.rm_alloc(self.nvdevice, nv_gpu.KEPLER_CHANNEL_GROUP_A, channel_params)
|
||||
|
||||
gpfifo_area = self.iface.alloc(0x200000, contiguous=True, cpu_access=True, map_flags=0x10d0000)
|
||||
gpfifo_area = self.iface.alloc(0x200000, contiguous=True, cpu_access=True, force_devmem=True, map_flags=0x10d0000)
|
||||
|
||||
ctxshare_params = nv_gpu.NV_CTXSHARE_ALLOCATION_PARAMETERS(hVASpace=vaspace, flags=nv_gpu.NV_CTXSHARE_ALLOCATION_FLAGS_SUBCONTEXT_ASYNC)
|
||||
ctxshare = self.iface.rm_alloc(channel_group, nv_gpu.FERMI_CONTEXT_SHARE_A, ctxshare_params)
|
||||
|
||||
@@ -139,7 +139,7 @@ class NV_FLCN(NV_IP):
|
||||
|
||||
return System.alloc_sysmem(len(patched_image), contiguous=True, data=patched_image)
|
||||
|
||||
self.frts_image_va, self.frts_image_sysmem = __patch(0x15, bytes(frts_cmd))
|
||||
_, self.frts_image_sysmem = __patch(0x15, bytes(frts_cmd))
|
||||
|
||||
def prep_booter(self):
|
||||
image = self.nvdev.extract_fw("kgspBinArchiveBooterLoadUcode", "image_prod_data")
|
||||
@@ -150,7 +150,7 @@ class NV_FLCN(NV_IP):
|
||||
|
||||
patched_image = bytearray(image)
|
||||
patched_image[patch_loc:patch_loc+sig_len] = sig[:sig_len]
|
||||
self.booter_image_va, self.booter_image_sysmem = System.alloc_sysmem(len(patched_image), contiguous=True, data=patched_image)
|
||||
_, self.booter_image_sysmem = System.alloc_sysmem(len(patched_image), contiguous=True, data=patched_image)
|
||||
_, _, self.booter_data_off, self.booter_data_sz, _, self.booter_code_off, self.booter_code_sz, _, _ = struct.unpack("9I", header)
|
||||
|
||||
def init_hw(self):
|
||||
@@ -327,10 +327,10 @@ class NV_GSP(NV_IP):
|
||||
# Alloc queues
|
||||
pte_cnt = ((queue_pte_cnt:=(queue_size * 2) // 0x1000)) + round_up(queue_pte_cnt * 8, 0x1000) // 0x1000
|
||||
pt_size = round_up(pte_cnt * 8, 0x1000)
|
||||
queues_va, queues_sysmem = System.alloc_sysmem(pt_size + queue_size * 2, contiguous=False)
|
||||
queues_view, queues_sysmem = System.alloc_sysmem(pt_size + queue_size * 2, contiguous=False)
|
||||
|
||||
# Fill up ptes
|
||||
for i, sysmem in enumerate(queues_sysmem): to_mv(queues_va + i * 0x8, 0x8).cast('Q')[0] = sysmem
|
||||
for i, sysmem in enumerate(queues_sysmem): queues_view.view(i * 0x8, 0x8, fmt='Q')[0] = sysmem
|
||||
|
||||
# Fill up arguments
|
||||
queue_args = nv.MESSAGE_QUEUE_INIT_ARGUMENTS(sharedMemPhysAddr=queues_sysmem[0], pageTableEntryCount=pte_cnt, cmdQueueOffset=pt_size,
|
||||
@@ -338,7 +338,7 @@ class NV_GSP(NV_IP):
|
||||
_, self.rm_args_sysmem = self.nvdev._alloc_boot_struct(nv.GSP_ARGUMENTS_CACHED(bDmemStack=True, messageQueueInitArguments=queue_args))
|
||||
|
||||
# Build command queue header
|
||||
self.cmd_q_va, self.stat_q_va = queues_va + pt_size, queues_va + pt_size + queue_size
|
||||
self.cmd_q_va, self.stat_q_va = queues_view.addr + pt_size, queues_view.addr + pt_size + queue_size
|
||||
|
||||
cmd_q_tx = nv.msgqTxHeader(version=0, size=queue_size, entryOff=0x1000, msgSize=0x1000, msgCount=(queue_size - 0x1000) // 0x1000,
|
||||
writePtr=0, flags=1, rxHdrOff=ctypes.sizeof(nv.msgqTxHeader))
|
||||
@@ -348,9 +348,9 @@ class NV_GSP(NV_IP):
|
||||
|
||||
def init_libos_args(self):
|
||||
_, logbuf_sysmem = System.alloc_sysmem((2 << 20), contiguous=True)
|
||||
libos_args_va, self.libos_args_sysmem = System.alloc_sysmem(0x1000, contiguous=True)
|
||||
libos_args_view, self.libos_args_sysmem = System.alloc_sysmem(0x1000, contiguous=True)
|
||||
|
||||
libos_structs = (nv.LibosMemoryRegionInitArgument * 6).from_address(libos_args_va)
|
||||
libos_structs = (nv.LibosMemoryRegionInitArgument * 6).from_address(libos_args_view.addr)
|
||||
for i, name in enumerate(["INIT", "INTR", "RM", "MNOC", "KRNL"]):
|
||||
libos_structs[i] = nv.LibosMemoryRegionInitArgument(kind=nv.LIBOS_MEMORY_REGION_CONTIGUOUS, loc=nv.LIBOS_MEMORY_REGION_LOC_SYSMEM, size=0x10000,
|
||||
id8=int.from_bytes(bytes(f"LOG{name}", 'utf-8'), 'big'), pa=logbuf_sysmem[0] + 0x10000 * i)
|
||||
@@ -370,18 +370,18 @@ class NV_GSP(NV_IP):
|
||||
for i in range(3, 0, -1): npages[i-1] = ((npages[i] - 1) >> (nv.LIBOS_MEMORY_REGION_RADIX_PAGE_LOG2 - 3)) + 1
|
||||
|
||||
offsets = [sum(npages[:i]) * 0x1000 for i in range(4)]
|
||||
radix_va, self.gsp_radix3_sysmem = System.alloc_sysmem(offsets[-1] + len(self.gsp_image), contiguous=False)
|
||||
radix_view, self.gsp_radix3_sysmem = System.alloc_sysmem(offsets[-1] + len(self.gsp_image), contiguous=False)
|
||||
|
||||
# Copy image
|
||||
to_mv(radix_va + offsets[-1], len(self.gsp_image))[:] = self.gsp_image
|
||||
radix_view.view(offsets[-1], len(self.gsp_image))[:] = self.gsp_image
|
||||
|
||||
# Copy level and image pages.
|
||||
for i in range(0, 3):
|
||||
cur_offset = sum(npages[:i+1])
|
||||
to_mv(radix_va + offsets[i], npages[i+1] * 8).cast('Q')[:] = array.array('Q', self.gsp_radix3_sysmem[cur_offset:cur_offset+npages[i+1]])
|
||||
radix_view.view(offsets[i], npages[i+1] * 8, fmt='Q')[:] = array.array('Q', self.gsp_radix3_sysmem[cur_offset:cur_offset+npages[i+1]])
|
||||
|
||||
# Copy signature
|
||||
self.gsp_signature_va, self.gsp_signature_sysmem = System.alloc_sysmem(len(signature), contiguous=True, data=signature)
|
||||
_, self.gsp_signature_sysmem = System.alloc_sysmem(len(signature), contiguous=True, data=signature)
|
||||
|
||||
def init_boot_binary_image(self):
|
||||
self.booter_image = self.nvdev.extract_fw("kgspBinArchiveGspRmBoot", "ucode_image_prod_data")
|
||||
@@ -522,7 +522,7 @@ class NV_GSP(NV_IP):
|
||||
self.stat_q.wait_resp(nv.NV_VGPU_MSG_FUNCTION_SET_PAGE_DIRECTORY)
|
||||
|
||||
def rpc_set_gsp_system_info(self):
|
||||
def bdf_as_int(s): return (int(s[5:7],16)<<8) | (int(s[8:10],16)<<3) | int(s[-1],16)
|
||||
def bdf_as_int(s): return 0x000 if s.startswith("usb") else (int(s[5:7],16)<<8) | (int(s[8:10],16)<<3) | int(s[-1],16)
|
||||
|
||||
data = nv.GspSystemInfo(gpuPhysAddr=self.nvdev.bars[0][0], gpuPhysFbAddr=self.nvdev.bars[1][0], gpuPhysInstAddr=self.nvdev.bars[3][0],
|
||||
pciConfigMirrorBase=[0x88000, 0x92000][self.nvdev.fmc_boot], pciConfigMirrorSize=0x1000, nvDomainBusDeviceFunc=bdf_as_int(self.nvdev.devfmt),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
import ctypes, time, functools, re, gzip, struct
|
||||
from tinygrad.helpers import getenv, DEBUG, fetch, getbits, to_mv
|
||||
from tinygrad.helpers import getenv, DEBUG, fetch, getbits
|
||||
from tinygrad.runtime.support.hcq import MMIOInterface
|
||||
from tinygrad.runtime.support.memory import TLSFAllocator, MemoryManager
|
||||
from tinygrad.runtime.support.nv.ip import NV_FLCN, NV_FLCN_COT, NV_GSP
|
||||
@@ -137,9 +137,9 @@ class NVDev(PCIDevImplBase):
|
||||
self.large_bar = self.vram.nbytes >= self.vram_size
|
||||
|
||||
def _alloc_boot_struct(self, struct:ctypes.Structure) -> tuple[ctypes.Structure, int]:
|
||||
va, paddrs = System.alloc_sysmem(sz:=ctypes.sizeof(type(struct)), contiguous=True)
|
||||
to_mv(va, sz)[:] = bytes(struct)
|
||||
return type(struct).from_address(va), paddrs[0]
|
||||
view, paddrs = System.alloc_sysmem(sz:=ctypes.sizeof(type(struct)), contiguous=True)
|
||||
view[:sz] = bytes(struct)
|
||||
return type(struct).from_address(view.addr), paddrs[0]
|
||||
|
||||
def _download(self, file:str) -> str:
|
||||
url = f"https://raw.githubusercontent.com/NVIDIA/open-gpu-kernel-modules/8ec351aeb96a93a4bb69ccc12a542bf8a8df2b6f/{file}"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import os, mmap, array, functools, ctypes, select, contextlib, dataclasses, sys, errno, itertools
|
||||
from typing import cast, ClassVar
|
||||
from tinygrad.helpers import round_up, to_mv, getenv, OSX, temp
|
||||
from tinygrad.helpers import round_up, getenv, OSX, temp
|
||||
from tinygrad.runtime.autogen import libc, vfio
|
||||
from tinygrad.runtime.support.hcq import FileIOInterface, MMIOInterface, HCQBuffer
|
||||
from tinygrad.runtime.support.memory import MemoryManager, VirtMapping
|
||||
@@ -49,6 +49,18 @@ class _System:
|
||||
raise RuntimeError("IOServiceOpen failed")
|
||||
return conn
|
||||
|
||||
def iokit_pci_memmap(self, typ:int):
|
||||
if self.iokit.IOConnectMapMemory64(self.macos_tinygpu_conn, ctypes.c_uint32(typ), System.mach_task_self,
|
||||
ctypes.byref(addr:=ctypes.c_uint64(0)), ctypes.byref(size:=ctypes.c_uint64(0)), 0x1): raise RuntimeError(f"IOConnectMapMemory64({typ=}) failed")
|
||||
return MMIOInterface(addr.value, size.value)
|
||||
|
||||
def iokit_pci_rpc(self, sel:int, *args:int):
|
||||
in_scalars = (ctypes.c_uint64 * len(args))(*args) if args else ctypes.POINTER(ctypes.c_uint64)()
|
||||
if (self.iokit.IOConnectCallMethod(self.macos_tinygpu_conn, sel, in_scalars, len(args), None, ctypes.c_size_t(0),
|
||||
out_scalars:=(ctypes.c_uint64*16)(), ctypes.byref(outcnt:=ctypes.c_uint32(16)), None, ctypes.byref(ctypes.c_size_t(0)))):
|
||||
raise RuntimeError(f"IOConnectCallMethod({sel=}, {args=}) failed")
|
||||
return out_scalars[:outcnt.value]
|
||||
|
||||
def reserve_hugepages(self, cnt): os.system(f"sudo sh -c 'echo {cnt} > /proc/sys/vm/nr_hugepages'")
|
||||
|
||||
def memory_barrier(self): lib.atomic_thread_fence(__ATOMIC_SEQ_CST:=5) if (lib:=self.libsys if OSX else self.atomic_lib) is not None else None
|
||||
@@ -60,15 +72,25 @@ class _System:
|
||||
self.pagemap.seek(vaddr // mmap.PAGESIZE * 8)
|
||||
return [(x & ((1<<55) - 1)) * mmap.PAGESIZE for x in array.array('Q', self.pagemap.read(size//mmap.PAGESIZE*8, binary=True))]
|
||||
|
||||
def alloc_sysmem(self, size:int, vaddr:int=0, contiguous:bool=False, data:bytes|None=None) -> tuple[int, list[int]]:
|
||||
assert not contiguous or size <= (2 << 20), "Contiguous allocation is only supported for sizes up to 2MB"
|
||||
flags = (libc.MAP_HUGETLB if contiguous and (size:=round_up(size, mmap.PAGESIZE)) > 0x1000 else 0) | (MAP_FIXED if vaddr else 0)
|
||||
va = FileIOInterface.anon_mmap(vaddr, size, mmap.PROT_READ|mmap.PROT_WRITE, mmap.MAP_SHARED|mmap.MAP_ANONYMOUS|MAP_POPULATE|MAP_LOCKED|flags, 0)
|
||||
def alloc_sysmem(self, size:int, vaddr:int=0, contiguous:bool=False, data:bytes|None=None) -> tuple[MMIOInterface, list[int]]:
|
||||
if OSX:
|
||||
sysmem_view = System.iokit_pci_memmap(round_up(size, mmap.PAGESIZE))
|
||||
paddrs = list(itertools.takewhile(lambda p: p[1] != 0, zip(sysmem_view.view(fmt='Q')[0::2], sysmem_view.view(fmt='Q')[1::2])))
|
||||
assert not contiguous or len(paddrs) == 1, "not contiguous, but required"
|
||||
paged_paddrs = [p + i for p, sz in paddrs for i in range(0, sz, 0x1000)][:round_up(size, 0x1000)//0x1000]
|
||||
else:
|
||||
assert not contiguous or size <= (2 << 20), "Contiguous allocation is only supported for sizes up to 2MB"
|
||||
flags = (libc.MAP_HUGETLB if contiguous and (size:=round_up(size, mmap.PAGESIZE)) > 0x1000 else 0) | (MAP_FIXED if vaddr else 0)
|
||||
va = FileIOInterface.anon_mmap(vaddr, size, mmap.PROT_READ|mmap.PROT_WRITE, mmap.MAP_SHARED|mmap.MAP_ANONYMOUS|MAP_POPULATE|MAP_LOCKED|flags, 0)
|
||||
sysmem_view, paged_paddrs = MMIOInterface(va, size), self.system_paddrs(va, size)
|
||||
|
||||
if data is not None: to_mv(va, len(data))[:] = data
|
||||
return va, self.system_paddrs(va, size)
|
||||
if data is not None: sysmem_view[:len(data)] = data
|
||||
return sysmem_view, paged_paddrs
|
||||
|
||||
def pci_reset(self, gpu):
|
||||
if OSX: System.iokit_pci_rpc(__TinyGPURPCReset:=2)
|
||||
else: os.system(f"sudo sh -c 'echo 1 > /sys/bus/pci/devices/{gpu}/reset'")
|
||||
|
||||
def pci_reset(self, gpu): os.system(f"sudo sh -c 'echo 1 > /sys/bus/pci/devices/{gpu}/reset'")
|
||||
def pci_scan_bus(self, target_vendor:int, target_devices:list[int]) -> list[str]:
|
||||
result = []
|
||||
for pcibus in FileIOInterface("/sys/bus/pci/devices").listdir():
|
||||
@@ -143,14 +165,12 @@ class PCIDevice:
|
||||
return MMIOInterface(loc, sz, fmt=fmt)
|
||||
|
||||
class APLPCIDevice(PCIDevice):
|
||||
def __init__(self, pcibus:str, bars:list[int], resize_bars:list[int]|None=None): self.pcibus, self.bars = pcibus, {b: self.map_mem(b) for b in bars}
|
||||
def map_mem(self, typ:int) -> MMIOInterface:
|
||||
if System.iokit.IOConnectMapMemory64(System.macos_tinygpu_conn, ctypes.c_uint32(typ), System.mach_task_self,
|
||||
ctypes.byref(addr:=ctypes.c_uint64(0)), ctypes.byref(size:=ctypes.c_uint64(0)), 0x1): raise RuntimeError(f"IOConnectMapMemory64({typ=}) failed")
|
||||
return MMIOInterface(addr.value, size.value)
|
||||
def __init__(self, pcibus:str, bars:list[int], resize_bars:list[int]|None=None):
|
||||
self.pcibus, self.bars = pcibus, {b: System.iokit_pci_memmap(b) for b in bars}
|
||||
self.bar_info = {b:(0, self.bars[b].nbytes-1 if b in self.bars else 0, 0) for b in range(6)} # NOTE: fake bar info for nv.
|
||||
def map_bar(self, bar:int, off:int=0, addr:int=0, size:int|None=None, fmt='B') -> MMIOInterface: return self.bars[bar].view(off, size, fmt)
|
||||
def read_config(self, offset:int, size:int): return 0
|
||||
def write_config(self, offset:int, value:int, size:int): pass
|
||||
def read_config(self, offset:int, size:int): return System.iokit_pci_rpc(__TinyGPURPCReadCfg:=0, offset, size)[0]
|
||||
def write_config(self, offset:int, value:int, size:int): System.iokit_pci_rpc(__TinyGPURPCWriteCfg:=1, offset, size, value)
|
||||
|
||||
class PCIDevImplBase:
|
||||
mm: MemoryManager
|
||||
@@ -173,23 +193,23 @@ class LNXPCIIfaceBase:
|
||||
self.pci_dev, self.dev, self.vram_bar = PCIDevice(cls.gpus[dev_id], bars=bars, resize_bars=[vram_bar]), dev, vram_bar
|
||||
self.p2p_base_addr = self.pci_dev.bar_info[vram_bar][0]
|
||||
|
||||
def alloc(self, size:int, host=False, uncached=False, cpu_access=False, contiguous=False, **kwargs) -> HCQBuffer:
|
||||
if host or (uncached and cpu_access): # host or gtt-like memory.
|
||||
def alloc(self, size:int, host=False, uncached=False, cpu_access=False, contiguous=False, force_devmem=False, **kwargs) -> HCQBuffer:
|
||||
# NOTE: logic on macos is different, since bar is small
|
||||
should_use_sysmem = host or (((uncached or cpu_access) if OSX else (uncached and cpu_access)) and not force_devmem)
|
||||
if should_use_sysmem:
|
||||
vaddr = self.dev_impl.mm.alloc_vaddr(size:=round_up(size, mmap.PAGESIZE), align=mmap.PAGESIZE)
|
||||
paddrs = [(paddr, mmap.PAGESIZE) for paddr in System.alloc_sysmem(size, vaddr=vaddr, contiguous=contiguous)[1]]
|
||||
mapping = self.dev_impl.mm.map_range(vaddr, size, paddrs, system=True, snooped=True, uncached=True)
|
||||
return HCQBuffer(vaddr, size, meta=PCIAllocationMeta(mapping, has_cpu_mapping=True, hMemory=paddrs[0][0]),
|
||||
view=MMIOInterface(mapping.va_addr, size, fmt='B'), owner=self.dev)
|
||||
memview, paddrs = System.alloc_sysmem(size, vaddr=vaddr, contiguous=contiguous)
|
||||
mapping = self.dev_impl.mm.map_range(vaddr, size, [(paddr, 0x1000) for paddr in paddrs], system=True, snooped=True, uncached=True)
|
||||
return HCQBuffer(vaddr, size, meta=PCIAllocationMeta(mapping, has_cpu_mapping=True, hMemory=paddrs[0]), view=memview, owner=self.dev)
|
||||
|
||||
mapping = self.dev_impl.mm.valloc(size:=round_up(size, 4 << 10), uncached=uncached, contiguous=cpu_access)
|
||||
if cpu_access: self.pci_dev.map_bar(bar=self.vram_bar, off=mapping.paddrs[0][0], addr=mapping.va_addr, size=mapping.size)
|
||||
return HCQBuffer(mapping.va_addr, size, view=MMIOInterface(mapping.va_addr, size, fmt='B') if cpu_access else None,
|
||||
meta=PCIAllocationMeta(mapping, has_cpu_mapping=cpu_access, hMemory=mapping.paddrs[0][0]), owner=self.dev)
|
||||
barview = self.pci_dev.map_bar(bar=self.vram_bar, off=mapping.paddrs[0][0], size=mapping.size) if cpu_access else None
|
||||
return HCQBuffer(mapping.va_addr, size, view=barview, meta=PCIAllocationMeta(mapping, cpu_access, hMemory=mapping.paddrs[0][0]), owner=self.dev)
|
||||
|
||||
def free(self, b:HCQBuffer):
|
||||
for dev in b.mapped_devs[1:]: dev.iface.dev_impl.mm.unmap_range(b.va_addr, b.size)
|
||||
if not b.meta.mapping.system: self.dev_impl.mm.vfree(b.meta.mapping)
|
||||
if b.owner == self.dev and b.meta.has_cpu_mapping: FileIOInterface.munmap(b.va_addr, b.size)
|
||||
if b.owner == self.dev and b.meta.has_cpu_mapping and not OSX: FileIOInterface.munmap(b.va_addr, b.size)
|
||||
|
||||
def map(self, b:HCQBuffer):
|
||||
if b.owner is not None and b.owner._is_cpu():
|
||||
@@ -205,21 +225,6 @@ class LNXPCIIfaceBase:
|
||||
class APLPCIIfaceBase(LNXPCIIfaceBase):
|
||||
def __init__(self, dev, dev_id, vendor, devices, bars, vram_bar, va_start, va_size):
|
||||
self.pci_dev, self.dev, self.vram_bar = APLPCIDevice(pcibus=f'usb4:{dev_id}', bars=bars), dev, vram_bar
|
||||
|
||||
def alloc(self, size:int, host=False, uncached=False, cpu_access=False, contiguous=False, **kwargs) -> HCQBuffer:
|
||||
if host or uncached or cpu_access: # cpu access memory goes here, since bar is small.
|
||||
vaddr = self.dev_impl.mm.alloc_vaddr(size:=round_up(size, mmap.PAGESIZE), align=mmap.PAGESIZE)
|
||||
assert size >= mmap.PAGESIZE, "Size must be at least one page"
|
||||
|
||||
sysmem = cast(APLPCIDevice, self.pci_dev).map_mem(size).view(fmt='Q')
|
||||
paddrs = list(itertools.takewhile(lambda p: p[1] != 0, zip(sysmem[0::2], sysmem[1::2])))
|
||||
|
||||
mapping = self.dev_impl.mm.map_range(vaddr, size, paddrs, system=True, snooped=True, uncached=True)
|
||||
return HCQBuffer(vaddr, size, meta=PCIAllocationMeta(mapping, has_cpu_mapping=True), view=sysmem.view(fmt='B'), owner=self.dev)
|
||||
|
||||
mapping = self.dev_impl.mm.valloc(size:=round_up(size, 4 << 10), uncached=uncached, contiguous=cpu_access)
|
||||
return HCQBuffer(mapping.va_addr, size, view=None, meta=PCIAllocationMeta(mapping, has_cpu_mapping=False), owner=self.dev)
|
||||
|
||||
def map(self, b:HCQBuffer): raise RuntimeError(f"map failed: {b.owner} -> {self.dev}")
|
||||
|
||||
PCIIfaceBase:type = APLPCIIfaceBase if OSX else LNXPCIIfaceBase
|
||||
|
||||
@@ -272,7 +272,7 @@ def bufferize_to_store(x:UOp):
|
||||
assert assign_target.op is Ops.INDEX, f"{assign_target.op} is not index"
|
||||
# in assign, this is the buffer size, not the bufferize size
|
||||
# TODO: assign_mops here
|
||||
ret = assign_target.replace(dtype=sdtype).store(assign_src, *rngs, dtype=x.dtype)
|
||||
ret = assign_target.replace(dtype=sdtype).store(assign_src, *rngs, dtype=x.dtype).replace(tag=x.tag)
|
||||
mops = []
|
||||
walk = assign_mops
|
||||
while walk is not assign_mops.base:
|
||||
@@ -284,7 +284,7 @@ def bufferize_to_store(x:UOp):
|
||||
# NOTE: the DEFINE_LOCAL needs to be disambiguated here
|
||||
if sdtype.addrspace == AddrSpace.GLOBAL:
|
||||
buf = UOp.new_buffer(x.arg.device, size, x.dtype)
|
||||
ret = buf.reshape(shape).index(*rngs, dtype=sdtype).store(x.src[0], *rngs, dtype=x.dtype)
|
||||
ret = buf.reshape(shape).index(*rngs, dtype=sdtype).store(x.src[0], *rngs, dtype=x.dtype).replace(tag=x.tag)
|
||||
ret = ret.forced_reshape(shape)
|
||||
# TODO: is this right? what if it's offset
|
||||
if any(r.op is Ops.RANGE and r.src[0].op is not Ops.CONST for r in rngs):
|
||||
|
||||
+1
-2
@@ -294,8 +294,7 @@ class Tensor(MathTrait):
|
||||
assert self.shape == x.shape, f"assign shape mismatch {self.shape} != {x.shape}"
|
||||
assert self.device == x.device, f"assign device mismatch {self.device} != {x.device}"
|
||||
assert self.dtype == x.dtype, f"assign dtype mismatch {self.dtype} != {x.dtype}"
|
||||
self.uop = self.uop.assign(x.uop)
|
||||
return self
|
||||
return self.replace(self._apply_uop(UOp.assign, x))
|
||||
|
||||
def detach(self) -> Tensor:
|
||||
"""
|
||||
|
||||
+1
-9
@@ -370,15 +370,7 @@ class UOp(MathTrait, metaclass=UOpMetaClass):
|
||||
return UOp(Ops.RANGE, dtype=dtypes.index, src=(sint_to_uop(end),), arg=arg)
|
||||
def r(self, op:Ops, axis:tuple[int, ...]):
|
||||
axis = tuple(sorted([x for x in axis if resolve(self.shape[x] != 1)]))
|
||||
if len(axis) == 0: return self
|
||||
# move any non reduce axis before the first reduce axis
|
||||
move_early, rest = partition(range(axis[0], len(self.shape)), lambda i: i not in axis and resolve(self.shape[i] != 1))
|
||||
permaxis = tuple(range(axis[0])) + tuple(move_early) + tuple(rest)
|
||||
ret = self.permute(permaxis)
|
||||
new_axis = tuple([x for x in range(axis[0]+len(move_early), len(self.shape)) if resolve(ret.shape[x] != 1)])
|
||||
assert len(axis) == len(new_axis)
|
||||
ret = UOp(Ops.REDUCE_AXIS, self.dtype, (ret,), (op, new_axis))
|
||||
return ret.reshape(tuple([x if i not in axis else 1 for i,x in enumerate(self.shape)]))
|
||||
return UOp(Ops.REDUCE_AXIS, self.dtype, (self,), (op, axis)) if len(axis) else self
|
||||
@staticmethod
|
||||
def invalid(count=1): return UOp(Ops.CONST, dtypes.index.vec(count), src=(), arg=Invalid)
|
||||
def valid(self, cond): return self if cond.op is Ops.WHERE and cond.arg else cond.where(self, UOp.invalid(self.dtype.count))
|
||||
|
||||
@@ -272,10 +272,6 @@ async function renderProfiler() {
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const [_, v] of temp) {
|
||||
v.x.push(x);
|
||||
v.y.push(v.y.at(-1));
|
||||
}
|
||||
timestamps.push(dur);
|
||||
const height = heightScale(peak);
|
||||
const yscale = d3.scaleLinear().domain([0, peak]).range([height, 0]);
|
||||
|
||||
@@ -152,12 +152,17 @@ def timeline_layout(dev_events:list[tuple[int, int, float, DevEvent]], start_ts:
|
||||
events.append(struct.pack("<IIIfI", enum_str(name, scache), option(ref), st-start_ts, dur, enum_str(info or "", scache)))
|
||||
return struct.pack("<BI", 0, len(events))+b"".join(events) if events else None
|
||||
|
||||
def encode_mem_free(key:int, ts:int, execs:list[ProfilePointEvent], scache:dict) -> bytes:
|
||||
kernel_names = [enum_str(ei.key, scache) for ei in execs]
|
||||
return struct.pack(f"<BIII{len(kernel_names)}I", 0, ts, key, len(kernel_names), *kernel_names)
|
||||
|
||||
def mem_layout(dev_events:list[tuple[int, int, float, DevEvent]], start_ts:int, end_ts:int, peaks:list[int], dtype_size:dict[str, int],
|
||||
scache:dict[str, int]) -> bytes|None:
|
||||
peak, mem = 0, 0
|
||||
temp:dict[int, int] = {}
|
||||
events:list[bytes] = []
|
||||
buf_ei:dict[int, list[ProfilePointEvent]] = {}
|
||||
|
||||
for st,_,_,e in dev_events:
|
||||
if not isinstance(e, ProfilePointEvent): continue
|
||||
if e.name == "alloc":
|
||||
@@ -170,9 +175,9 @@ def mem_layout(dev_events:list[tuple[int, int, float, DevEvent]], start_ts:int,
|
||||
if e.name == "exec" and e.arg["bufs"]:
|
||||
for b in e.arg["bufs"]: buf_ei.setdefault(b, []).append(e)
|
||||
if e.name == "free":
|
||||
kernel_names = [enum_str(ei.key, scache) for ei in buf_ei.pop(e.key, [])]
|
||||
events.append(struct.pack(f"<BIII{len(kernel_names)}I", 0, int(e.ts) - start_ts, e.key, len(kernel_names), *kernel_names))
|
||||
events.append(encode_mem_free(e.key, int(e.ts) - start_ts, buf_ei.pop(e.key, []), scache))
|
||||
mem -= temp.pop(e.key)
|
||||
for t in temp: events.append(encode_mem_free(t, end_ts-start_ts, buf_ei.pop(t, []), scache))
|
||||
peaks.append(peak)
|
||||
return struct.pack("<BIQ", 1, len(events), peak)+b"".join(events) if events else None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user