Go back

Reiner Pope – Chip design from the bottom up

80m 30s

Reiner Pope – Chip design from the bottom up

AI chips rely on multiply-accumulate (MAC) operations as their foundational computational unit, especially for matrix multiplication in neural networks. A 4-bit × 4-bit multiplication followed by an 8-bit accumulation efficiently balances precision and performance, aligning with how matrix operations naturally accumulate errors. The circuit implementation uses full adders (3-to-2 compressors) to sum partial products, resulting in a gate count that scales quadratically with bit width—making low-precision arithmetic (like FP4) highly efficient in both area and power. This efficiency is amplified by architectural innovations such as systolic arrays, which move computation into hardware and reduce data movement from registers, a major cost in traditional designs. In prior architectures, register-to-ALU data transfers consumed most of the circuit area via complex multiplexers. Modern AI chips address this by embedding full compute loops in hardware, storing weight matrices locally and minimizing communication overhead. This shift increases compute density relative to communication. Clock cycles synchronize all operations across the chip, and logic must be designed to fit within the cycle, leading to pipelining and register insertion. FPGA technology, while more expensive per gate than ASICs, enables deterministic, low-latency execution critical for high-frequency trading by allowing dynamic, reconfigurable logic. The key insight is that both chip design and AI computation are governed by the fundamental trade-off between compute and communication: minimizing data movement maximizes performance, and this principle applies across precision, architecture, and system-level design.

Transcription

13535 Words, 70802 Characters

English
I'm back with Rainer Pope, who is the CEO of Maddox, which is a new AI chip company. Last time we were talking about what happens inside a data center, now I understand what happens inside an AI chip. How does a chip actually work? For disclosure, by the way, I am an initial investor in Maddox, so hopefully you have designed a good chip. Also, if you're listening to this on an audio platform, it's much preferable to watch this blackboard lecture on a platform where you can see what's happening. So I'll start with the very smallest fundamental unit of chip design, and we'll build up into what an overall actual production chip, what are the components of that? At the very bottom level of a chip, the primitives that we work with are logic gates, which are very simple things like end or not. And then these are connected together by wires that have to be laid out physically as metal tracers on a chip. The main function that AI chips want to compute is multiplication of matrices and really inside that is, the fundamental primitive is multiply accumulative, just like of pairs of numbers. So we're going to demonstrate what that calculation looks like by hand, and then infer what a circuit would look like for that. It'll turn out to be easiest if I do multiplication accumulator something like a per 4-bit number with another 4-bit number. And then we're going to, the actual clearest primitive is actually multiply accumulates. So there's a multiply these two terms, and then we're going to add in, so product of these two terms, and then we're going to add in an 8-bit number. I can ask a clarifying question. Why is this the natural primitive for, you know, whatever computation happens at a computer? Yeah, so there's a few reasons for this. It's a little bit more efficient, but the reason it's natural for AI chips is that if you look what's happening during a matrix multiply, the what is matrix multiply in very short, it is, there's a for loop over i and over j, and over k of output i k plus equals to input i j times other input j k. And so multiply accumulate happens at every single step of a matrix multiply. And then the other observation is that the precision will almost always be higher in the accumulation step than in the multiplication step. This is maybe specific to AI chips, but you're multiplying low precision numbers, but then when you accumulate errors accumulate quickly, and so you need more precision here. So this is why we've chosen to do a 4-bit multiplication in an 8-bit addition. Let me make sure I understood that there's two ways to understand that one is that the value will be larger than the inputs. And the other is that if it was a floating point number, it would be maybe that that part is less intuitive to me, but it's maybe this principle. It is really the same principle. I guess the sort of, I mean, I guess the separate principle is that as you are summing up this number, you are summing up a whole bunch of numbers. And so you've got a lot of rounding errors accumulating, whereas in this case, there's like there's there's only one multiplication in that chain, so there's not a lot of rounding errors accumulating in the multiplication. Why are you summing up a whole bunch of numbers? Is there just two numbers? Just, I mean, this summation happens, it's repeated. I see, I see, I see, I see, I see, I see. So how would we perform this calculation by hand? I mean, as a human, we would probably separate it as a two, but we can sort of do it all in one using long multiplication. So the multiplication term first, we're going to multiply this number, this four bit number here, by every single bit position in the other four bit number. So we write that out. First, 1, 0, 0, 1, multiplied by this bit position. That is this number itself. Then, shift it across by 1, we're multiplying by 0. I give us an old 0's number. Shift it across even one more to multiply by this one. We get 1, 0, 0, 1. And then finally, for this last bit position, we get an old 0's number again. So this sort of gives us a bunch of terms that we're going to have to add for the multiplication. And then, while we're doing that summation of this, we might as well add in the actual accumulated term as well. So we just copy that directly across. So this is the sum, it's the five way sum that we're going to want to compute. So firstly, what logic takes us to even get to this intermediate step? We needed to produce all 16 of these partial products. How do I produce one of these partial products? So let's take this number one, for example, here. It is 1, so how do we produce this number by multiplying this number by this one over here? We can actually produce that by an AND gate. This number is 1, if both this bit is 1 and this bit is 1. If either of them is 0, then the multiplication of 1 times 0 times anything is 0. So to produce all of this stuff, we ended up consuming 16 AND gates. Or in the general case, if I were doing a P bit multiply times a Q bit multiply, then this will be like P times Q, many ANDs. Exactly. Finally, I sum them. Actually, most of the work is going to happen in the summing. And so let me describe the other logic gate that we use here. And is almost the simplest logic gate that exists on a chip. It's almost the smallest. At the other extreme, typically, the very largest logic gate that you'll use is something called a full adder. And what this does is come in from software you might think that a full adder it adds 32 bit numbers together. In this case, it just adds three single bit numbers together. And so you can think of it as adding 0, 1, and 1 together. Now when I add these together, the result can be 0, 1, 2, or 3. So I can express that in binary using just two bits. So as input, it has three bits. And as output, it has two bits, which in this case, the number 2 in binary is 1, 0. So there is also known as a 3 to 2 compressor. Because it takes three bits of input and produces two bits of output. The two inputs are an x and a y value. And then some carry that came in from like the three inputs are all bits that are in the same sort of bit position, like three bits that are in a column here. And then the two outputs, I have drawn them vertically here and horizontally here to kind of match this vertical versus horizontal layout here, which is expressing that things that are in the same column are in the same bit position, whereas things that are in adjacent columns, like this is a carry out, whereas this was some. So if the inputs in the full adder are, we're saying like, 1, 0, 1, then the output would still be 1, 0. If it was 1, 1, 1, it would be 1, 1. It was 0, 0, 0, 0. It would be 0, 0, 0. It was like 0, 1, 0. It'd still be 0, 1. So yeah, it's just counting, essentially, the number of things, and expressing that in binary. So this circuit actually can capture what we as humans naturally do when we're doing something along that column. So I can show that sort of-- I'll show one iteration of using the full adder to some. The way our sum here is going to be a little bit unnatural for humans. Humans, we would sort of sum along the column and then remember the carry. But instead of remembering the carry, we'll actually just explicitly write it out. So in this, we proceed from the rightmost column towards the left on the rightmost column, we sum the 1 and the 1. And that produces like a 0 here and a carry of 1. So we sort of used this full adder circuit on these pair of bits and produced a pair of bits as output. Now, we can do the same thing with this column. We've got a column of 1, 2, 3, 4 numbers. And so maybe we'll take the first three of them, run a full adder on them, and that gives us a 0 and a 0 as output. So like some of these is 0, 0. So that's the full adder applied to all of these bits. As I've used up bits, I'm going to sort of just cross them out to indicate that I've handled them. Let's just keep going a little bit more. So we'll go here. I take these three numbers. I add them. That gives me a 1 and a 0. I've dealt with these three numbers. And now I take 1, 2, and I can even take these three numbers, for example, right now, and add them and that gives me a 1 and a 0, and I've dealt with these numbers. So. I can sort of, like, the way I should view this is that I have this whole grid of numbers that need to be added. I'm going to just keep applying full adders to all the bits that are here, constantly removing three numbers from a column and then writing out two numbers as output. Keep going with this over and over and over and again until I eventually get like some just one single number coming out here. Something like that. This is probably the wrong sum. So, so this approach that I've described here, this is called a data multiplier. And this is sort of like the standard for how you do area official multiplies using full adders. Let's try and quantify the circuit size of this just so we have got a sense of like how big things are that so we can compare to them later. How many full adders do I use? I started with how many numbers? I have the 16 partial products, which is the product of all of these terms with all of these terms. Plus the eight terms that I'm adding here. So I started off with 24 bits and then I produced eight bits on the output eventually. And in every step I was sort of crossing off three numbers and writing two numbers out as a result. And so every single use of a full adder eliminates one of the bits here. And so how many full adders it must be the 24 minus the eight so that there were 16 full adders in this circuit. In general, this is true in the general case as well. There will be P times Q many full adders in this circuit. You sure I understand the logic of that. So the input bits 24 is P times Q plus P plus Q. That's right. And the output bits is just P plus Q. And so P times Q plus P plus Q minus P plus Q equals P times Q. That's right. So I think this explains sort of at least hints that the second reason why we chose to do a multiply accumulate. First reason being that's actually what shows up in matrix multiplication. But second reason being it gave us this very slick P times Q very simple algebra. So we've sort of described like this whole procedure. Every single atomic step that I took here becomes a logic gate and then sort of the wires connected together. Like when I had these three inputs that I salvage to produce these two outputs. Like if I think of mapping this to a physical device, there would be a wire that runs sort of connecting all three of these things together into a logic gate that produced this output. Okay. So this is the main primitive at different bit widths. That is inside an AI chip. We're going to build up from here to how would you use that to run all of the other operations you might want. This might be the wrong time to ask this question. But whenever Nvidia reports that this chip can do X many FP4 or half as many FP8, it seems to imply that those circuits are fungible, that there's not as dedicated like FP4 versus FP8. But the way you're mapping it out here, it seems like you would need, if it has to be mapped out in the logic, you would need a dedicated FP4, multiply a cumulative and then a dedicated FP8 accumulate. Basically, can you can you can you find them? As drawn, they're actually not particularly fungible. This is actually one of the main choices you have to make when designing a chip, which is how much of FP4, how much of FP8 do I have. And then sometimes I'll make that consideration from the point of view of like, what do I think is the customer requirement? Another way to take an angle on that is to say, what is the power budget for equalize the power budget between FP4 and FP8. But so then when they report those numbers and they just happen to be the case that like, it does 2X as many FP4 as FP8, they just happen to choose like give equivalent die areas to all the floating points and as a result that end up being like, why is the ratio exactly? Yeah, exactly. Yeah, so part of it is, I mean, surely that wouldn't be exactly like exactly equivalent to die area. There's a data movement reason actually and we'll maybe come back to this when we sort of look through how it goes into another memory reason. There's something really nice just from a software level of the fact that I can pack two four bit numbers into the same storage as an 8-bit number. And so when I store that to a memory or something like that, it's the sizing of the the buses that I wire within the chip actually makes that work out really really nicely. Actually, come to think of it, it's not just 2X. So it would be the amount of area it takes that sounds like is quadratic. It's quadratic in fact. Yeah, with the bit length. So that's why a smaller precision is like even more favorable than you're actually saying. This is a really big reason. So in fact, Nvidia made a change historically up until B-100 or B-200 every time you have the bit precision, you double the flop count. That ratio is exactly like for the reason you said because of this quadratic scaling, that ratio is actually slightly wrong. It should be like an even bigger speed up than you might otherwise think. Nvidia's products specs have sort of started acknowledging that in B-300 and beyond where the FB-4 is three times faster than the FB-8. So it should be 4X. Yeah. What I've shown here is the simplest case of integer multiply. When you're dealing with floating point as you do in FB-4 and FB-8, there's this other term which is the exponent that just complicates this compilation. So what can we see already from this? I think the big observation you've made is that there's this quadratic scaling with bit width, which is very effective and is the single reason why low precision arithmetic has worked so well for neural nets. But the other thing we're going to do now is we're going to compare sort of the area spent on the multiplication itself with all of the circuitry that is around it. So we'll walk back in time a little bit and see how did GPU's prior to tensor cores work, which is the same way as the way that CPU's worked, in fact. So which is like, where do we stick this multiply and accumulate unit? So generically, I'll describe like a CUDA core or a CPU. You'll have some register file which stores some number of entries. Maybe it's like eight entries of like, in this case, I guess four bit numbers, but typically like 32 bit numbers or something of which is not numbers. So this is the like, inside the CUDA core, I'll have some register file of some depth. And then I will have my multiply accumulate, so I could multiply and accumulate, so I could. And what it's going to do, it's going to like, it's going to take three arbitrary registers from this register file, perform the multiply accumulate, and then right back to the register file. So it's going to make you right to this one, but it was able to read from this one, this one and another random one. So it'll take three inputs like this. So this is the core data path of many processors. Most processors look like this. You've got some set of registers, and then you've got some set of logic units or ALUs. We want to analyze the cost of the data movement from the register file to the ALU and back. So ultimately, there's going to be some circuit that says, well, I don't always have to select this guy, I might select any of the registers in any point in time. And so sort of a first question is, how can I build a circuit? The circuit that I'm going to look for is a MUX. So in this case, it's going to have eight inputs, one from each entry of the register file. And it's going to have one output, which is actually producing this output. And then like, what is the cost of this thing? It's like all we have to build it out of is AND and OR. And so how do we build it? We do the dumbest thing possible. We like form a mask saying, okay, when we want to read like the third entry, we're going to add every single entry with either one or zero based on whether that's all we want to read. And then we're going to OR all of them together. Okay, just to make sure I understand the basics, what the MUX is doing is it's just like selecting, just selecting, just selecting an input. Yeah. So like invisible to software is like, you say I want input number three, that means there's a MUX. Yeah. And so like, what is the cost of this MUX? So an N input MUX operating on P bits, well, I'm going to, so I have N rows, that's this eight rows, and I've got like each row is P bits wide. Well, I have to AND every single bit. So I get N times P many AND gates. Every single input, I have to say, am I going to like, mask it out or not? And then I'm going to OR them all together. And so there's going to be like N minus one times P many OR gates, which is saying, I've got all of these different things, almost all of them are zeros, but I need to sort of collapse them down into, like, from my eight options down into one option. And so every step I need to, or, like, one row into an existing row. - Yeah, yeah. It's actually kind of funny that you would sort of, you don't think at the level of hardware, you sort of just think, like, oh, I'll just select element three, and something as simple as that is I sort of, like, in and of itself, a quite complicated circuit. - Yeah, I mean, this is the first step of all of the hidden data movement costs at the shop. - Right. - And so, like, the thing, like, we're just gonna, like, compare, like, I have to pay this cost, and I've got one marks here, and then, in fact, I have two more copies of that for each of the three inputs to my multiply accumulate operation. And so I have this cost, which is like, like, three times n times p and gates over here, compared to this p times q, like, sort of gates in the actual circuit that is doing the thing I care about. And if we plug in actual numbers, like, this n being eight, like, I get, like, 24 times p gates over just in the data movement, compared to, like, if q is four, like, four times p gates just in the, in the adder, multiply adder. - And, sorry, where's the three coming from? - Three different inputs here. - Got it, here. So, the case, like, really just what I'm hinting at here is that, like, all of this work, which scales, like, as the size of the register file, and this is a very small register file, all of this work, just moving the data from the register file to the, to the logic unit, is many, many times more expensive than the logic unit. - In the most recent cluster max report, 70 analysis ranked almost 100 different GPU clouds. Crusoe was one of only five that made the gold tier. 70 analysis found that gold tier providers that Crusoe had a total cost of ownership, those five to 15% lower than silver tier ones. Even when they had a identical GPU pricing. This makes sense, because total cost of ownership is downstream of a bunch of different things that don't necessarily show up in the sticker price, but that Crusoe has optimized. Things like how well you detect faults, and how quickly you replace failed nodes. For example, Crusoe was one of the first clouds to adopt any recentinal, in media's own GPU monitoring and self-healing software, for enhanced GPU, uptime, utilization, and reliability. This, that's Crusoe, makes use of everything that Nvidia has learned about why chips fail across all their different fleets and deployments, so that Crusoe can catch faults earlier in the process. And once they identify failure, Crusoe can swap in a healthy node in less than 10 minutes. Because they're not running bare metal, Crusoe doesn't have to spend time installing an operating system or configuring drivers. They can just spin up a new VM on an already running and pre-qualified host. If you want to learn more about this, or the other reasons that Crusoe made gold tier, go to crusoe.ai/doorcache. It may be helpful to just see what a mux looks like, maybe like a two-bit or a four-bit mux. Yeah, great. So we'll take some inputs. We'll have maybe, like, we'll just do a two-way mux. So we've got two different numbers. We've got these two inputs, and then we have a, so these are the inputs. They're being selected between, and then we have a selector. Which says, which can either be like, I want this one, or it could be, I want the other one. So this is a one-hop encoding. So this is what we all start with. And then we, the output we want to produce, like, let's focus on this case. So this is the actual input we got. Yeah. We just want to produce this guy as the real. And so, like, sort of very laboriously, what we do is we end this bit with all of these. And so that produces, like, ending this bit with this row. And likewise, we end this bit with this row that produces all zeros. So this was the, there's four ends here. There's four ends here. And then, finally, we just order these two together. And this gives a one, we order these two together. This gives a one, we order these two together, gives a zero, we order these two together, and we'll give a one. And so this is the four ores. So, like, this actually ends up looking a little bit like addition, in fact. Like, we did exactly the set of same set of ends here. So if we've added all of these things together, but then instead of collapsing it by using these full adder circuits, we just get a very simple collapsing with the ore gates. - And I guess that doesn't look like n times p. So yeah, so this was with n equals two inputs. In the general case, we will have n, and so this is n rows, and then we'll have p bits per row. So that gives us the n times p, many end gates. So this circuit I've described here, almost all of the cost, like seven, eights of the cost is in the reading and writing the register file. And only a tiny fraction of the cost is in the logic under itself. So this is the problem to solve. This essentially was the state of play prior to the Volta generation of NVIDIA GPUs. This is what this kind of thing is what was inside the CUDA cores, and this sort of problem statement is what motivated introduction to tensor cores, which are marginarically called systolic arrays. So if we think about how are we gonna solve this problem, like we were spending almost all of our circuit area on something that we just really don't care about and is hidden to the software programmer, and the thing that we actually care about is not much of the area. Well, make this one bigger somehow, while keeping this at the same size, that's the goal. So sort of the evolution was like, we had baked this much into hardware in the stage, this single line is a multiply accumulate, and this single thing was baked into hardware. The idea of a systolic array is to go two levels of loop up and bake this entire loop out here into hardware. And so the idea being that if we have a much bigger granularity fixed function, piece of logic, maybe the taxes we pay on the input and output are much smaller. It sounds like you're suggesting that if you go up one step and the major small supply loop, that there's some, you can tilt the balance more towards compute than communication. That's right. So there's two effects that we're gonna take advantage of here. One is just that we can do more stuff before we per every trip through our register file. And then the other thing we're gonna take advantage of is in fact, in some of this loop, we can take advantage of for example some things staying fixed. So let's sort of visually we're gonna look at this matrix multiplication. So this portion of the loop corresponds to a matrix vector multiplication in fact. So we'll take a matrix and multiply it by a vector. So how do we do this? We take every column gets multiplied by the vector and then some, so we're gonna some sort of along columns. And so this 0 and 3 gets multiplied by the 3 and 7 and gets summed and then the 1 and 2 gets multiplied by the 3 and 7 and gets summed. So there is a multiply accumulate associated with every single one of these entries in the matrix. So we'll just draw out these four multiply accumulates. I just make sure I understand why there's four multiply accumulates. So if each entry in the column that corresponds to the output vector is a dot product and in this case it'll be like two multiplications and then the addition of those two multiplications so like you're accumulating. - Yeah, so the addition, so really there's only one addition per dot product but like we would like to start with the initialization of 0. So what we're gonna aim for is to have, so we've got to, we want to have quadratically more compute. We do, we have, we have got sort of X times Y as much compute as we had before. But we're gonna want to somehow aim for having only X times as much like communication and this is sort of the intention so that we get this advantage term going as Y. So we've laid down the multiplications. Bringing in, like we're gonna want to bring in a vector of size two and so that sort of already is in line with our columns target, that's fine. However, we need to somehow manage the communication of this matrix which exceeds our budget of X. And so the idea is that in an AI context, this matrix is actually gonna stay fixed for a long period of time. And so instead of like bringing it in from the outside so we've got some register file sitting over here, we don't want to have like the amount of stuff coming out of this register file. This is the term that we want to go sort of as X in some sense. We don't want to bring this full matrix in from the register file every cycle because we don't have enough that would cost us. too much in terms of wiring from the register file. And so instead we're going to store, our key trick is that this matrix can be stored locally to the systolic array. And so, where we'll store these numbers 0, 1, 2, and 3, in just like a gate called a register that like physically stores these numbers and we're going to reuse these numbers over and over again for a large number of different matrix vectors. And so the optimization here is that like the nature of a matrix multiplication is you can store this like square quadratic thing directly where the logic is happening and which is like higher dimension than the or has an extra dimension compared to the inputs which you keep swapping in and out. That's right. And the nature of what a matrix multiplication is is that you do a lot of multiplication to get one value out like a dot product is the result of a lot of multiplication. And so that optimization means that you're just like, you can stuff a lot of like multiplication in before you get some value out of it. That's right. So like just to complete the picture here of concretely how that looks, I swapped the three in the two here, 3 and 2. So just like this 0 and 3 is going to multiply by the 3 and 7. And so we're going to form a dot product sort of along columns here. So somehow we're going to feed a 3 and a 7 in here. These participate in sort of this feeds into this multiplication and also feeds into this multiplication, likewise the 3 feeds into here and also into here. And then we're going to sum sort of along here with like starting at the top of a column we feed in zeros and then coming out the bottom, we get results coming out. So sort of just to visually see what we've got, there's a dot product that is performed along columns in a matrix and that sort of maps exactly to what is done spatially in the systolic array here. So this is one dot product summed vertically and this is a second dot product also summed vertically. And then what is the data that needs to go into and out of the register file? We have x amount of data that's coming out here on the output and we also have sort of this data coming from the input, x amount of data from the input. And so with respect to the input and output vectors at least, we sort of met our goal of having only x as much data going in and out of the register file. This leaves open the question of like I said that the weight matrices, weight matrix is stored locally in the systolic array. How did it get there in the first place is sort of like at some point you need to boot your chip and populate this data and so where did that come from? The trick is just we just do it very slowly. So we are very slowly trickle feed it into the systolic array. The sort of the simplest strategy is that we sort of run this daisy chain that says like feed a number into here and then on the next clock cycle it will move down to the next entry of the systolic array. And so we can do that in every column in parallel and that gives us sort of this is also going to come from here and this is going to be another factor of the x units of bandwidth coming in. Would you mind repeating this sentence? So we know that we are going to be bringing in numbers only rarely into the matrix. And so we just want to come up with any construction at all such that the amount of wiring that actually feeds into sort of crosses this boundary of the systolic array, like this boundary right here, we just want to keep that bounded to x and not go as x. y. And so a particularly simple strategy is that we sort of bring in a number into the top row of the systolic array. That's what we can do in one clock cycle. And then for like for y consecutive clock cycles we're going to be bringing in the top row every time and then sort of shift all of the others down by one. And that keeps the sort of the wiring that needs to come from this expensive register file only down to a factor of x rather than x. I see. So there's two questions in terms of communication. There's like communication time and then there's communication bandwidth. Yes. And you're saying, since we're only going to be loading this in once, let's minimize bandwidth because bandwidth equals diarrhea. And let's just like load it in slowly over like smaller lanes because we're just going to keep this value in there for a while. Exactly. So it's interesting to me that when we were talking last time about inference across many chips, the big high level thing we're trying to optimize for is increase the amount of compute per memory bandwidth that is say per communication. And here also we're trying to increase the amount of like actual multiplies or actual additions relative to transporting information from registers to the logic. So in both cases you're trying to maximize compute relative to communication. Yeah. This turns up sort of all the way up and down the stack. This is sort of close to the bottom sort of like to the gates. There's sort of a version that's maybe even closer to the gates of just like even the precision of number format that you choose to use. We saw that same effect. There's like a square cube law or like squared versus linear term going on both in just purely the precision of this ALU, but then also in terms of the size of the matrix. Yeah. Interesting. Also, so this unit is sort of the next bigger unit we had like the multiplication circuit. And then on top of that we have a pretty large systolic ray. I drew it as 2 by 2, but in like for example, all the TPUs they were described as 128 by 128 of this circuit shown here. And this circuit ends up being, this is the most efficient known mechanism for circuit for implementing a matrix multiply. I see. We've talked about sort of, it seems obvious that you should try to maximize compute relative to communication. What are non-obvious trade-offs that actually you are, you know, keep you up at night about what should we do X or should we do Y? And it's not obvious what the answer is. Yeah. So, I mean, I think most of the decisions in chip design are sizing decisions. And so already in what we've drawn so far, like, so AI chips all have this socket in it. They have a systolic array. And then somewhere near it, a register file, providing inputs and outputs. The two sort of, like even within this scope, sizing questions that you have are, how big should I make my systolic array and how big should I make the register file? So, and then the trade-off for the size of the systolic array, actually, these two questions are coupled is one way to think of it is to say I'm going to, like, have a budget for how much, what percentage of my chip area I want to spend on data movement. So maybe I just say that I want this to be 10% and the systolic array to be 90%. And then sort of, I can size my register file. I think your register files are more flexible, they allow me to run sort of more, I can get more application level performance out, but then they sort of take away from this area spent on the systolic array. Yeah, that makes sense. I recently ran a nasty contest where I asked people to write about what I consider to be some of the biggest open questions about AI. The submission window closed last week, so I used cursor to create a couple of different interfaces to help me review the entries. One interface unautomizes submissions and hides unnecessary information. It lets me group responses by question, add notes, and record my scores. The other interface helps me review entrants who also want to be considered for the research or role that I'm hiring for. The UI puts the applicants essay right next to the resume and their personal website so that I can see everything at once. Cursor's harness is really good at helping these models see and improve their UI's. I watched it render these interfaces in the built-in browser, take screenshots, click through sections, and keep iterating. At this point, cursor is where I do most of my work, whether I'm reading and visualizing a bunch of research papers, or coding up an interface to review applications, or making flashcards for my blackboard lectures. Cursor just makes it very easy for an AI to look at whatever I'm looking at and help me understand it and work with me on it. So whatever you're working on, you should do it in cursor. Go to cursor.com/thorkech. Where does the clock cycle of a chip come in, what determines what that is? Yeah, and what is a clock cycle? So, I guess at baseline, it's sort of worth observing that chips are incredibly parallel, right? You've got 100 billion transistors in a chip. A key thing that you need to do whenever you have massive parallels is you need to synchronize between the different parallel units. In software typically, you have these very expensive synchronization methods like a mutex. So one thread will finish what it's doing, it will grab a lock somewhere stored in memory and then notify the other thread that it's done. Some chips we take a very different approach and say that every nanosecond or so, all all circuitry in the chip will kind of pause for a moment and then synchronize every. So it actually synchronizes every single nanosecond or so. And so that is the clock cycle. The entire chip typically all in sort of one fell swoop goes in lockstep to the next operation that happens. And so what this looks like in circuitry is that you will have this typically drawn. So the clock is sort of mediated by registers which are these storage devices that we've drawn elsewhere. And the way to think of it is that I have I have some storage which is storing like a bit which might be zero or one. And then I have some sort of cloud of logic which maybe is like this systolic array or this multiplier or something like that. And then I've got some and that's going to produce some output. So my inputs I've got a bunch of inputs feedings with this cloud of logic. And then eventually later there's going to be some output register that this writes to. There is a global clock signal which drives all of these registers. And it says at a certain instance in time when the clock strikes whatever value happens to be on this wire at that instant that's what's going to get stored in there. And so the sort of the challenge here is like I would like to have my clock speed run as fast as possible because if I can run at two gigahertz I can get twice as many operations done per per second then if I run at one gigahertz. But what that ends up meaning is that I am very sensitive to the delay through this cloud of logic because any computation that is going to happen in here needs to sort of finish before the next clock cycle hits. So a major point of sort of optimization on any chip then is to make this delay from here as short as possible. Interesting. And is there ever, because the constraint here seems to be that if you add too much logic then you might risk missing the clock cycle. But if you don't add enough then you're you know leaving potential compute on the table. Is there ever a situation where you're like you'd take a probabilistic chance that a compute computation finishes and or is it just like no either is going to finish a bike or a cycle or not? Yeah. In standard chip design you you margin it such that I mean there is a probability but it's like many many standard deviations like way less deviations out such that for all intensive purposes it is a reliable part. It will it will always meet the clock. There are some weird exceptions to that. There are clock domain crossings where you go from one clock to the other clock and then you actually do have to reason about this probability. But interesting. In the main path you just like you margin that such that you'll get there like 25% of the clock cycle in advance so that it's very unlikely. In this in this the clock where the clock synchronize I guess where the registers are this is not something you determine as a chip designer this is sort of just like an artifact of hey I want whatever sequence of logic and then the software you use to convert your very log into the thing you send to TSMC that just determines like hey in order to make this work you got to put a register here here and here to make sure that there's no one step that is like too long such as it makes the whole clock cycle the entire chip longer than it has to be. Yeah so this is actually a huge part of the work of designing a chip actually is inserting them. So it is done in a combination of manually and automatically. So I mean like just like to show you the very sort of dumb version of like what you can do here you can take this logic and split it in half and so like say actually instead of just one part of logic I'm gonna have two smaller clouds of logic which do the same thing but split them up by a register. Right, feeding in like this and this is like like if you split it like in the middle you can hit twice the clock frequency that's great you get twice the performance at the cost of this extra register and so at the cost of some more storage. And stepping back why do we need to synchronize the whole chip? Like if you imagine playing factorial or something there's no like global clock cycle it just should is done when it's done there's iron on the plate you can take it if you want. Yeah so taking that analogy the thing that you need to be mindful of is if I've got two different paths through some logic so I have to do a computation like F here and then computation G here and then they're going to come and meet for computation H somewhere here. And so there's going to be manufacturing variance here in some chips F will take a little longer maybe in some chips G will take a little bit longer and so if I've got some signal that's propagating through here and the result from F and G have to sort of meet up at H. What can the the thing that can go wrong is that F can get there early and it meets like the previous value of G or the next value of G. And A should used to know when to start exactly when when has this next iteration of and so this explains why different chips made at the same process node the same like TSMC technology can have different clock cycles they go yeah two chips made at 3 nanometer right at different clock cycles based on whether they were able to optimize making sure that like there's no one critical path that is so long that it's those down the whole chips clock cycle that's right this optimization that I that I showed here this is just the this is sort of pipeline register insertion it's called yeah we've inserted in the middle of the pipeline a register here this is a sort of pure tradeoff between clock speed and an area yeah this is the easy case there is a harder case too which is sort of drawn it as a pipeline of logic here but in other cases you may have some some calculation which actually feeds back in on itself so it runs some function F and then writes back to itself like this so for example this might be this addition like you've got some number that you're adding into every clock cycle and so this this could be like a this could be like a plus where adding in some number every clock cycle so this like this little circuit it essentially it's just going to solve all of the numbers that was entered on from different clock cycles and the challenge is if this plus takes too long what can I do if I like split it in if I try and put a pipeline register like right in the middle of it like like here in the middle of it this will end up changing the computation that's done instead of forming a running sum of everything that comes here I will actually have two different running sums I'll end up having a running sum of the even numbers and a running sum of the odd numbers so so this constraint where I have a loop in my logic which all chips have somewhere this is actually the thing that is the hardest thing to to address and that's the clock cycle I don't understand why it'd be a problem to have that or I'm not sure even what it would mean to have a lot of register there because it's a sort of atomic operation right yeah well so plus is not really atomic like I think as we just demonstrate yeah yeah it took a whole lot of work to do an automation and so like you can take the early parts of that work and then and then stick a register in the middle and then to take the late parts of that work yeah and I guess it's then up to CTSMC offers a PDK which says that I say here's the primitives of logic that we can grant you in the chip and it's up to them to determine that no no primitive is bigger than like the clock cycle they're hoping a process node targets but other than that is they're like what further optimize it can't you just say like hey here's all the primitives from TSMC and if keep adding registers in between the primitives as much as it needed until you get to your desired clock cycle yeah as a logic designer like the chip architect set the clock cycle so just for one example the primitives you get from TSMC are on the order of like and gates or full ladders they depends a lot on voltage and frequent and and and which library you choose and so on but generally they you can typically have about like 10 or 20 or 30 of these in in a clock cycle sequentially so these primitives are very very fast like 10 picker seconds or something like that and so as a logic designer I mean like in principle if you literally just had like like register and then and gate kind of in a loop like that you could get an insanely fast clock speed like more than four or five six gigahertz and they like that but if you take this this like really sort of like simple circuit and you look at the area you're spending here like this is maybe like one I mean this is this is called one gate equivalent in size so like you know of one in area and this thing is like unit of eight in area or something like that and so like this is just or again almost all of your cost has been this like synchronization or communication cost compared to the actual logic and so so this would be a case where you've gone too far you've made a clock speed really really fast right at the cost of spending almost all of your area on right on on pipeline registers interesting so we're So your hinting at is a dynamic where you can have really fast clock speed, but you're not getting that much work done. Yeah. Yeah. And so you can have like low latency, but low bandwidth or throughput rather. Yeah. It has to your throughput in fact, because like the throughput of your chip, you can think of as the product of how much I can get done per clock cycle, which is based on this area efficiency thing, times how many clocks I get per second. This is actually so similar to the thing we're discussing last time about like batch size, where if you have a low batch size, then you can anyone user can receive their next token really fast. But the total number of tokens at a process and say an outward will be kind of lower than could otherwise be. Yeah. Exactly. You get less parallelism out if you drive your clock speed up. I see. Language models are starting to compete against the best human forecasters. I sat down with two senior chain streeters, Ron Minsky and Dan Puentecorvo, and asked at some point this AI just do what James Street does. There's a world that we should take seriously, where we're going to build large language models or some other AI systems that are like strictly smarter than all humans on the planet and more capable at all cognitive tasks. Trading in particular feels to me as like kind of AGI complete, sort of like NP complete, because at the end of the day, trading involves figuring out what things are worth, which means making predictions about the future. But James Street isn't betting against AI. They just signed a $6 million compute deal. But Ron's view is at the edge keeps moving. I have never been more desperate to hire more engineers and more traders than I am today. You know, you have the usual thing of like the other hard parts that we don't yet know how to automate, while that ends up being where the competitive edge lies. You can find these open positions and watch the full interview at jainstreet.com/thoracash. Okay, so I remember talking to an FPGA engineer at jainstreet Clark, who actually helped me prep for the previous interview we did together. And he was explaining why they use FPGA and I imagine that for high frequency trading, through what is less important than latency and so having very specific control over the clock cycle in deterministic ways, the most important thing. We'd be interesting to talk about why you can't just achieve that within ASIC or why FPGA is the, why you might use an FPGA to have deterministic clock cycles for high frequency trading. Yeah, so I mean, firstly, let's consider the business case for an FPGA versus an ASIC. GAs and ASICs use largely the same sort of conceptual model, which is that I have a series of gates built from ads or six hours, those like very small primitives connected together with a fixed clock cycle and connected together with wires that are running in a fifth clock cycle. So anything you can express in an FPGA, you can express in an ASIC too, and it'll be about an order of magnitude cheaper and better energy efficiency on an ASIC than an FPGA. The tradeoff is that the first FPGA costs you $10,000, whereas the first ASIC you make costs you $30 million because it requires an entire tape out. So sort of the business use case for an FPGA would be that I want something that has this very deterministic latency and fast runtime and high parallelism, but I'm going to change it very frequently, change what I do every month for something like that, and so then I don't want to pay the tape out cost every time. Now how does an FPGA actually implement, it sort of emulates the ASIC programming model but in a fixed piece of hardware, and so how does that work, actually? So what it has at the base is, it's got the two components we just talked about, it's got these registers as storage devices, and then it's got these are called lots lookup tables which actually provide all of the gates, so and then we're going to see even the sort of the third component, we then have a swarm of these registers and lots, and all of these are available and then they are connected by this big set of sort of mixes, so in front of every single one of these, we've got something like one of these mixes which selects one input from everywhere else, sort of selecting from all of these things, we've got a whole bunch of different options feeding into all of these things. So what this allows is essentially a, when I program my FPGA, I can say that I'm going to take all of these components and I'm going to sort of superimpose on top of this a particular wiring which goes through this lot and then feeds into this lot and then goes to this register and then feeds into this lot, so what I've drawn in orange is how FPGA means field programmable Gatoray, this is the orange is what has been programmed in the field, whereas the white is all of the wires that must exist in the FPGA in order to actually make the device in the first place. What does it mean to be programmed to a field? Programmed in the field, so like the device is being deployed in a data center, it's sitting in the field and then you can come and program. That field is like electric field, no field is in like out there in the world, okay. And so if I see, look at the how the field programming comes out of the first look up table and goes in a second one, how is it, how, yeah, how, like where are the wires that may happen I guess, yeah, so I got a little bit like lazy and drawing all of these, every single device here has a MUX sitting in front of it, which can select from all of the like nearby like circuits that are available. And so the actual configuration of the FPGA is like amounts to it, it is the MUX control, so like in this MUX here we have the data inputs and then we have like the control that selects. And so like there's a little storage device sitting next to every single one of these MUXs saying this is where you're going to source your input from. And so programming it consists of like configuring every single one of these MUXs. So that makes sense, what is happening so the look up table? Yeah. So the purpose of the look up table, so it's going to also have a little bit of control feeding, telling it what to do as well. The purpose of the look up table is to function, to be able to configurably take the role of an AND gate or gate, XOR, any of those different things. So there's many ways you could consider doing that. The way it is done in sort of traditional FPGA is to say it will support. So it will be a look up table, it will have four bits of input, one bit of output. How many different functions are there from four bits to one bit? There are 16 different functions. And so you can actually just tabulate this as like 16 different numbers. You go to table of over 1, 1, 1, 0, 0, 1, 16 entries. And so what it does is this table is stored in this blue configuration bit. And then it views these four bits as binary, looks up the relevant row of the table, and emits that bit. So this is a truth table view of look up tables, essentially. OK, so the look up table, if you think of an AND gate or gate, NOR gate, XOR gate, these are all like take as input, those are like two inputs. Yeah. So sometimes we have like more complicated like a three input function would be a three way XOR, or a four way XOR. And in this case how many, it just depends how big it is, but typical size for lots is for input, which is sort of just a sweet spot between, there's another computer communication trade off like here. Like if it has two few inputs, then you need to use more lots. Yeah, if it is true. But basically the look up table is like a truth table. It's a truth table. And with a true table, you can program in any gate you want. That's right. And so it's a look up table just thinks like a programmable gate. That's right. And so, I mean, one of the things you can do here is you can see why, whether all the thumb that an FPGA is like an order of magnitude more expensive than an ASIC comes from, is to count how many gates would be inside this look up table. So we can view this look up table essentially as one of these moxas. And so it is a mox with has to select between 16 different values. And so it is a mox with sort of n equals 16 options, p equals 1 bits. And so what we saw way earlier is that this circle costs like n times p many gates. And so it's like, so it costs like n times p equals 16 and gates. And also 16 ores. -This circuit being the mocks. -Yeah, exactly. The mocks is the core. -The mocks that goes into the lookup table. -So the lookup table itself, you can think of as being actually a big mocks that selects from all 16 rows down to one out. -Yeah, okay. That is the lookup table. -But the way you've drawn it here, there's a mocks and then a lookup table. -It's a mocks is all the way down, so I mean, there's a second mocks that is inside here. This mocks is this mocks. -Got it, okay. And then the other mocks is just saying where it came from in this sort of mess of case. -Right. And then the second mocks is, okay, now you have one value, but that value is still a four-bit value. -Yeah, so I've selected four bits from the soup. -Right. -And then I use those four bits to select which entry in the lookup table. -Yeah. -I'm going to use. -Right, okay. So suppose in the first mocks, there's eight nearby registers as input. And so that's a total of 32 bits going in. And then out of that, four bits come out. Those four bits go into the second mocks which is inside the lookup table. -So actually, I would say in this case, these registers are single-bit registers. So if there are eight nearby registers and lookup tables, then I have eight bits total coming in in nearby. I select from eight down to four different values. So there's actually like four different mocks, one associated with each of these inputs. -Little mocks associated with each of these input bits, each of them is selecting one out of eight. -And what are those incoming from? -Nearby registers and other lots. -And each register is one bit? -Yes, yeah. -And so I guess AMD or whoever makes these FUG is still has to be opinionated about what register should connect to which registers. And then you can program in the actual gates, but they had a wire in the connection, like the communication topology, right? -Yeah, so there's the sort of like, you get flexibility in a local grain thing. There's a sort of nearby neighborhood where you can select from. But then more grossly, like more costly longer distant connections that they form an opinion on. And the reason it's 10x lower is why. -So if you look at the cost of building this look-up table, it's like 32 gates. -Yeah. -And then it can give me the equivalent of what's one an interesting thing I can do here. I can do a four-way end gate. And so that's like I'm using 32 gates of look-up table to sort of implement like a four-way end. It means like, what is a four-way end? I would do like, and, and, and, and of, and. So to implement like this is a circuit that I could implement in an ASIC directly using these three end gates. But using a lot, I can also implement it, but it's going to take like these 32 gates instead of three. -Right. And so the overhead is really coming from the like, the fact that the look-up table, the marks in the look-up table is, there's a more concise way to describe a truth table than listing out every single possible combination of inputs, which is just to like, write out the gate. -Yeah, like to, like, place down the polysilicon and the, and that's right. -That's right. That's right. -Yeah. Interesting. One important point made to me is that the reason they prefer FPGA is to CPUs is because they get deterministic clock cycles. They know when a pack will come in and go out. Why as a not a guaranteed CPUs? So you can actually design a CPU that has deterministic latency as well. And in fact, like, the, the processes that are inside a lot of AI chips actually also have deterministic latency too. Grock has advertised this. TPUs have that in the core as well. The challenge is getting sort of deterministic latency and high speed at the same time. And so, where does the non-determinism in latency come from? Non-deterministic latency comes from specific design choices in a CPU. It's actually possible to remove those design choices and make a CPU that has deterministic latency. Those are not very attractive in the market. And so people don't make those CPUs anymore. But, but, but actually, in some sense, like, deterministic latency is maybe a sort of a simpler designing starting point. And then, and then, like, some chip designers have added things into it to be non-deterministic. To take a concrete example of that, the probably the most important example is on a CPU just like the CPU cache itself. So, in a CPU, you have the CPU, this is this is the CPU die itself. And then there is a memory off on the side. This is the DDR memory off on the side. And then, you have a cache system here inside it is the cache that sort of remembers recent accesses to DDR and stores them. And so, when I'm running through my CPU instructions, every time I have an instruction that accesses memory, it first checks in cache was the data stored in cache. And then, if not, it goes, fashes out to DDR. Yep. This is a huge optimization. The cache is like two orders of magnitude faster than the DDR. If you never, if you never like use the cache, like basically all programs would run 100 times slower. So, the presence of a cache is absolutely necessary for a CPU to run at reasonable speed. But, whether or not you get a cache hit is dependent on the sort of ambient environment of the CPU. Like, what other programs are running, what has run recently, what is the random number, generator inside the cache system doing. So, that is a big source of non-determinism in the runtime of a CPU. So, this is sort of the memory system for a CPU. The big thing that you can do differently is instead of having the hardware say, I'm going to read memory and then decide the hardware decides whether or not it comes from cache or not, you can actually bake this in this decision into software. So, a different design philosophy is to, so, and you see this in maybe for example, TPUs. The TPU instead has, I mean, I'll draw the same diagram, but I'll call it a scratch pad. And so, the main difference is, so this would be like a TPU and then a HBM in this case rather than DDR, but it's still an off-chip memory. And instead of like the software saying first access, like memory and then the hardware decides, you've got some instructions that go here, this is like one kind of instruction, and then a totally different kind of instruction that goes to HBM. Yeah. And so, this style is a generically known as scratch pad instead of cache. The key distinction being that you have like one kind of instruction that says read or write scratch pad and a totally different instruction that says read or write HBM. So, this scratch pad being the cache. Yeah, this thing here is the scratch pad. So, stepping way back, people say computers have the corner code John Moynihan architecture where there's this serial processing of information, and maybe just because we've been talking about parallel accelerators, but I just don't like the FBGA super parallel, the kind to be actually the TPUs are super parallel. Even CPUs are super parallel, if you think about all the cores they have. And so, is it actually, like in what sense is modern hardware, actually the Von Neumann architecture? Is it actually a fair way to describe modern hardware? I think it's a fair way to describe CPUs. Like just the amount of parallelism, like on a CPU, the amount of parallelism you get is about 100 cores times maybe like 16 wave vector units. So, 106, about 1000 wave parallelism on a CPU. Yeah, one question is what is the, there is a die that is being used for the CPU. And if there's fewer threads, just as a matter of like transistor voltages are like switching on and off, is it just that there's like literally one control flow, like a small part of the die where like voltages are switching on and off or like in what how do you actually occupy the die area of a CPU if there's as opposed to the so few cores. Like what do you, I think there's nothing in there. Yeah, the cores are just much bigger and more complicated. So, I mean like, so I guess we should compare like a CPU core which takes up 1/100th of the die to like, I mean to a lot, like a lot is just only these 16 gates. So, like it's clear why there's so many more lots in an FPGA than cores in a CPU. But then sort of maybe the like why they're more CUDA cores, for example, than CPU cores, I think would be like why what's the difference between a CPU and a GPU or something like that would be a big difference. Inside the CPU, you have so one big use of so that sort of the top unit uses of area in a side of CPU are the cache. Very little is actually the ALUs, like mostly it's like these register files rather than the logic units. And then the both of these things have equivalence in a GPU and so that's not a big difference. But the thing that does not have an equivalent in a GPU is the sort of this branch predictor. And so there is a whole big area in the CPU which is sort of just a whole bunch of predictors that are saying when will my next branch be and where's the branch target for that? And so stripping a lot of that out as well as sort of making these register files tighter in a sense is driving a lot of where the GPU gains. - What is the branch predictor? Do they execute both branches at once or what does it do? - So the issue is that when I've got a series of instructions, like instructions, instructions, instructions, instructions, I, if I have a branch like here, if this instruction is branch, the actual processing step of processing an instruction takes a really long amount of time. It takes like maybe five nanoseconds or something like that. So like the time to actually notice that I've got a branch and then like evaluate the Boolean, whether it's true and then update the program counter to the new target and then read from the instruction memory for that, that could take like actually five nanoseconds to finish. And so in reality, this may finish some way down here. I don't want to, like, but I want to run a clock speed that is much faster than what five nanoseconds allows. Like five nanoseconds is 200 megahertz clock speed. I would like to run at one or two gigahertz or something like that. And so I need to run other instructions while the branch is being evaluated. And so I, like, I really just want to keep running the following instructions that happen after me, but that might have been wrong. Like, if the branch ended up being taken, then I need to know that instead of evaluating these instructions, I actually need to like jump to wherever the target is and run these instructions instead. And so the purpose of the branch predictor is like, like, genuinely to predict based on like before you even get to the instruction to be like, five cycles earlier to predict there was going to be a branch that's going to happen. So if I think about how the brain works, which is what you're describing here, at a high level, the differences might be that, while you can do structured sparsity in these accelerators and then save yourself some area that you would have otherwise had to dedicate to these gates in the brain. There's unstructured sparsity, you know, any neuron can connect any other neuron and not in, like, ways where they'd be column-lined or whatever. - Yeah, yeah. - Then there's a fact that memory and computer co-located? - I guess you could say in a way, the memory and computer co-located on these dyes. - This is exactly the co-location in some sense of the main computer. - That's right, that's right. Yeah, maybe that actually isn't a big difference. And the other, maybe a big difference is that the clock cycle on the brain is much slower than on computers. And partly that's to preserve energy because the faster the clock cycle, the bigger the voltage needs to be in order to identify for the signal to settle and to identify what state of transistor is. - Yeah, that's right. - I don't know if you have other high level takes about like how any commentary on what the brain might be doing versus how these chips work. - Yeah, I mean, so let's take the clock speed or one first, actually. - Yeah. - The clock speed is quite high on a chip because that, I mean, drives higher throughput. When we compare a GPU running some workload, it's running batch size 1000 or something like that. Whereas the brain is not running batch size 1000. It's only one of me. And so you could sort of imagine saying, well, take a GPU and like, instead of running at a gigahertz, run at a megahertz or something like that. And that would start to look maybe a little bit more like sort of equivalent things that you're talking about in the brain. There is in the way that silicon works, there are like, that does not give you a 1000X advantage in energy efficiency. So what it ends up looking like is you can like, you sort of just end up running this circuit once to stabilization and then it'll sit idle for a long period of time. It doesn't consume a lot of energy while it's sitting idle because most of the energy is consumed in sort of toggling bits from zero to one and back. So actually, let's talk about the energy consumption of a circuit like this. The way to think of a bit being stored is you've actually deposited some charge in a capacitor somewhere, sitting somewhere in the chip implicitly. So it becomes charged when it becomes a one and then it becomes discharged when it next goes to a zero. And that cycle of like charging the capacitor and then dumping that charge out to ground, that is where the energy is consumed. This is called the dynamic or switching power. This is most of the energy consumption of a chip. There is some other energy consumption just coming from the fact that insulate is not perfect, insulate is but we'll just discard that. Most of the energy consumption actually comes from just the charging and discharging of toggling from zero to one and back to zero. So if you run a chip much slower and you only clock it once every 1,000 clock cycles or something, you will have 1,000 times fewer transitions. It'll be about 1,000 times less energy consumption but not a substantial advantage in energy efficiency. OK, so you've described how a TPU works at a high level. What is the difference at a high level between how a GPU and a TPU work? Yeah, so I mean, I think there's sort of a high level organization principle that is different. And then there's sort of inside the cores what are different. But we'll look outside the like at the high level so we'll take a GPU and a TPU and what is like sort of the top level block structure look like. If you think of this as the whole chip in each case, the organization of the GPU is mostly a bunch of almost identical units, which are these. These are the ASMs. And then they've got an L2 memory in the middle and then a bunch more of these ASMs on the bottom. And so there's sort of this fairly regular grid of cores. And then like if we look at a TPU in comparison, you end up with much Corsa-grained units of logic. And so you end up with something like some large number of, maybe just a few matrix units. These are the big like systolic arrays. And then in the middle you've got some vector unit and then you've got your matrix units at the bottom. So now sort of like matrix units with a vector unit in the middle, sort of this is the whole TPU chip. You can sort of think of scaling this thing down into a really tiny unit with a smaller matrix unit, smaller vector unit. And that is sort of what an SM is. So sort of at a very high level point of view, the GPU has a lot of tiny, tiny TPUs sort of tiled across the whole chip. Oh, interesting. So like you're suggesting the tensor core within a streaming SM is now goes to an MXU. Yeah, it's very, very similar. I see. And so if you had more like more lack of structure, having a bunch of tiny TPUs makes a lot of sense, whereas if you kind of just have like huge matrix multiplications, you're like, why don't we just, why don't we avoid the cost of having the individual SMs with their own registers and warp schedulers and things like that? Why don't we just like make a huge thing and like amortize those costs across the whole thing? And I mean, I think this shows up in how large you can grow things. We've sort of seen this theme, like especially with the systolic array, where larger systolic array amortizes the register file costs better. Yeah. This sort of design allows you to have larger systolic arrays. This, whereas the sort of GPU design constrains you to having small units of everything. There is a trade-off, however. The, there ends up being because of this sort of coarse grain separation of things, there, you need to move a lot of data from the vector unit to the matrix units. And so like, you need to move a lot of data through a sort of like two lines of parameter here, whereas if you sort of look at the equivalent thing, here you've got vector units everywhere. And you need to move data through this line, through this line, through this line, through this line, through this line, through this line. So the amount of data you can move between a vector unit or matrix unit is actually much higher in a GPU than in a TPU, because like, instead of having to like move all the data through these just two lines, you're moving all these data through like 16 lines or something of wiring instead in a GPU. Right, but also you might have to move across the last area. Which, I mean, is also saving like it's an energy set. So, so data ends up moving like, if you can operate entirely within an SM, the data movement is much smaller, but then the moment you want to operate across SMs, it becomes sort of more complicated and expensive. So you don't have to comment, but one might expect that a thing a matrix might try to do is to get the GPU like smaller structure of systolic arrays surrounded by SRAM, but also at the same time, make it so that like the things you need in an SM to support the CUDA architecture, but take a bunch of space, you might discard. Yeah, we've talked publicly about something which we call a sysplitable systolic array, which is sort of in some sense, you can think of as like big systolic arrays that can be small systolic arrays as well. Okay, I think there's a good note to close on. Right, Er? Thank you so much. [BLANK_AUDIO]

Podcast Summary

Key Points:

  1. AI chips rely on multiply-accumulate (MAC) operations as their core primitive, especially for matrix multiplication in neural networks.
  2. MAC operations involve multiplying two numbers (e.g., 4-bit × 4-bit) and adding the result to an accumulator (e.g., 8-bit), enabling efficient low-precision computation with higher precision in accumulation.
  3. This structure aligns with matrix multiplication loops, where each step involves a MAC operation, and rounding errors accumulate in the sum.
  4. The circuit design uses full adders (3-to-2 compressors) to sum partial products, requiring P×Q full adders for P×Q partial products.
  5. The total gate count scales quadratically with bit width (P×Q), making low-precision arithmetic (e.g., FP4) highly efficient due to area and power savings.
  6. Traditional CPU/GPU architectures suffer from high data movement costs due to register-to-ALU transfers, which use complex multiplexers (MUXes) with N×P AND/OR gates.
  7. Tensor cores and systolic arrays solve this by moving compute-intensive loops into hardware, reducing data movement and increasing compute per communication.
  8. Weight matrices are stored locally in systolic arrays, minimizing data transfer from registers.
  9. Clock cycles synchronize all chip operations, and design must ensure logic paths finish before the next cycle; this drives pipelining and register insertion.
  10. FPGA use in high-frequency trading enables deterministic latency through programmable logic, despite higher cost per gate than ASICs.
  11. FPGAs offer flexibility and fast reconfiguration, making them ideal for dynamic logic changes, while ASICs offer better cost and efficiency for fixed designs.

Summary:

AI chips rely on multiply-accumulate (MAC) operations as their foundational computational unit, especially for matrix multiplication in neural networks. A 4-bit × 4-bit multiplication followed by an 8-bit accumulation efficiently balances precision and performance, aligning with how matrix operations naturally accumulate errors. The circuit implementation uses full adders (3-to-2 compressors) to sum partial products, resulting in a gate count that scales quadratically with bit width—making low-precision arithmetic (like FP4) highly efficient in both area and power.

This efficiency is amplified by architectural innovations such as systolic arrays, which move computation into hardware and reduce data movement from registers, a major cost in traditional designs. In prior architectures, register-to-ALU data transfers consumed most of the circuit area via complex multiplexers. Modern AI chips address this by embedding full compute loops in hardware, storing weight matrices locally and minimizing communication overhead.

This shift increases compute density relative to communication. Clock cycles synchronize all operations across the chip, and logic must be designed to fit within the cycle, leading to pipelining and register insertion. FPGA technology, while more expensive per gate than ASICs, enables deterministic, low-latency execution critical for high-frequency trading by allowing dynamic, reconfigurable logic.

The key insight is that both chip design and AI computation are governed by the fundamental trade-off between compute and communication: minimizing data movement maximizes performance, and this principle applies across precision, architecture, and system-level design.

FAQs

The fundamental operation in AI chips is the multiply-accumulate (MAC) operation. It is crucial because it directly mirrors the matrix multiplication process used in neural networks, which is the core computation in AI models.

MAC is natural for AI because it appears in every step of matrix multiplication. Additionally, multiplication uses low precision, while accumulation requires higher precision to handle rounding errors, making it efficient and accurate for training and inference.

A MAC operation multiplies two numbers (e.g., 4-bit inputs), producing a partial product, and then adds it to an accumulator (e.g., an 8-bit value). This combination allows for both efficient computation and error management in AI workloads.

AND gates are used to compute the multiplication of two bit inputs, while full adders (or 3-to-2 compressors) are used to sum multiple partial products, enabling efficient accumulation of results.

Full adders efficiently sum multiple bits in a column, mimicking how humans perform addition. They are critical for handling carry propagation in large-scale matrix operations, reducing the complexity of the addition circuitry.

The number of full adders scales as P × Q (for P-bit and Q-bit inputs), and the total gate count grows quadratically with bit width. This leads to significant efficiency gains in low-precision arithmetic, such as FP4 or FP8.

Chat with AI

Loading...

Pro features

Go deeper with this episode

Unlock creator-grade tools that turn any transcript into show notes and subtitle files.