MindSpunk
Joined 4 years ago
Joined 4 years ago
.NET can generate the code, but your game won't pass certification if it runs machine code generated by something other than the officially provided C/C++ tool chain.
True, but for the problems that Git LFS solves P4 is usually the other option. P4 handles large binaries very well, caveat being the rest of P4. In some projects the caveats are worth it. I work in video games and our in house engine has an unfiltered checkout size over a 1TB.
I've found LFS very brittle when bouncing between branches and moving around in history. There's been many times I've had my local check out blow up through rebases or mistakes I've made. Many times the recovery is to nuke and re-clone. With a 1TB repo that's not a good option.
It's been a while since I've used it, and I'm sure some of my problems were skill issues, but I never found it worth the pain at any scale I've tried to use it.
I don't really see what the article is talking about as the future, but if P4 or Git LFS is the best we can do as a species then we're doomed. All VCS options suck for one reason or another, I hope we don't stop trying to make something better. If only to save me from perforce.
Separating the hot path into a prefix before calling into a separate cold function should still generate better code. Your prefix only needs to allocate registers and stack space for just that single path. You would only pay the spilling costs in the old code off the hot path rather than every instruction. And I would expect the branch prediction accuracy of that prefix check to be higher than having the hot and cold paths all dispatching through the same tree of branches.
However it's speculation until you measure so I could be wrong.
Good article in any case, I enjoyed reading along.
The old, enum based value type used a single big match statement to dispatch between all possible type combinations. Their assembler output looks like the match gets compiled to something like a big stack of nested if statements.
The new code uses an explicit fast path check with a dispatch into a tagged 'cold' path when the common case isn't hit. The generated code is a single upfront branch for the fast path that exits immediately, with a dispatch into the slow path in a separate function.
This would be contributing significantly to the performance improvements. The old path requires taking several branches even on the hot path. The new code has a single, highly predictable branch that skips all the messy dispatch for the other types.
This could have been implemented for the enum based value type, and I would expect to see a jump in performance there too even without the new compact value type. There will be a much higher branch predictor hit rate with the explicit fast path.
It's depressing how bad the drivers are. They have 0 market pressure to improve the quality, unlike PC where the whiniest group on the internet (gamers on reddit) will crucify you for driver problems. I've spent too much time investigating and implementing workarounds. Though it does pay my bills...
And then Google has the gall to charge basically flagship prices for their under powered junk.
Personally I don't buy flagship phones because I don't use their full compute capabilities anyway, but a Pixel 11 would cost 2x what I paid for my Pixel 8. For 0 GPU performance improvement.
An add instruction on an AMD GPU adds two scalar values. If they're in a VGPR then each thread will add two values unique to that thread. A SIMD ISA as is common on a CPU is different because an add instruction explicitly adds a vector of values. xmm1 stores 128-bits of data. VGPR[1] stores 32-bits of data vectored over 32-64 threads in a thread group.
Without special instructions a thread can't access the VGPR values stored in other threads.
If you want to get more pedantic you also need to look at your target hardware and their specific micro-architectural quirks and features to get the best performance. AMD specifically benefits a lot from exploiting the scalar unit over the vector unit, you save loads of register file space if you can keep data in SGPRs over VGPRs. There's lots of traps you can fall into where you can load data from buffers into SGPRs but they get promoted to VGPRs because the scalar unit lacks an opcode for like one math operation you did to the value somewhere.
While each lane isn't truly a thread because it doesn't have its own PC the programming model definitely tries to make it seem that way. The threads can terminate at different points too. And again, the ISA isn't a vector ISA. Your register values are scalar.
If you go look at AMD's ISA docs (they're public) you'll see you don't have the equivalent of a __mm256 register like on x86. Each 'thread' just deals with single scalar values like int32 of float32. The hardware, however, groups 32 or 64 threads together which all run the same program and runs them together. Each 'thread' loosely maps to a SIMD lane. The SIMD is implicit, not explicit.
The main difference is that the 'SIMD' execution is somewhat opaque to the program. You just write plain scalar code and the hardware model dispatches it efficiently to SIMD execution units. It's not really an abstraction because to extract maximum performance you have to understand how it works. You can use this kind of programming model on a CPU too, Intel did it with [0] ISPC. It's a C-like language that has execution semantics similar to GPU shader languages but compiles to regular CPU code, and maps threads to your CPUs SIMD lanes like a GPU.
Vast majority of titles use deferred rendering, and lighting is done off screen too. Usually the only thing done to the "screen buffer" is a final post-process pass or a copy.