Lec 17 MIT 6.035 Computer Language Engineering, Fall 2005

PROFESSOR: OK, so we've come to the last lecture. So what I want to do is let's do some overview of what we went through to make sure that everybody's-- remembers, basically, from the beginning, talk a little bit about the project, and a little bit about what you can use this class material for. I mean, this is at least my view. After talking to a few people who have been taking this class over the years, where they have been using it. And I think-- so you don't just completely the minute you finish the next quiz completely forget about everything and just move on. And if I have time, I will go and talk about current research. So if you're interested in compiler stuff, to give you a flavor of this is great, what you learn, you actually got a good sliver of what compilers are all about. But what happens today in the research community? Just touch on some of that. And hopefully, we'll have some time for discussion and feedback. So this is where we started. We started saying we have this program. So we're thinking programming languages. And then microprocessors, understand assembly language, or object code. And we want to build a compiler there to do that. And so here's a simple program that we started with. So the first thing you can do is you can generate this unoptimized piece of code. This actually came out of I had a CPC code that I came out of GCC minus or 0. And then the interesting thing is you can actually optimize and get rid a lot of these redundant instructions and run it much faster. And I think we went from doing left and now last project. And this one, what you're working on will end up more to the right. So in this thing, you basically build one from scratch. This is very interesting, because many places where you go take compiler course, you build a compiler from scratch. But most of the time what you get, since this is such a large project, most places, when you do teach a compiler course, you get a template and say, here's a template. You go fill out the things, fill out the optimizations, fill out parsing, and stuff like that. And you basically go end-to-end. But what's missing there is most of the decisions are done for you, OK? You don't get to design the instruction set. You don't get to design the [? intermediate ?] representation, stuff like that. So what that means is at the end of the day, you build a compiler. But you don't have idea what the hard decisions are. In this course, we basically, what I like to say is give you the rope to hang yourself with. Basically we said, it's all open. Go do it. And what that means is many times what happens is do something, you make a decision early on. And then later you learn something. And then you say, oops, that was a bad decision. You had to go undo things. I mean, I guess a lot of people had that feeling. I think key thing is it's a much harder course because of that. I mean, if you talk to your peers in other institutions, they might have done a compiler. But you probably had to deal with a lot of those decisions, and all the bad decisions, and recover from it. And the nice thing about [INAUDIBLE] they manage to recover from those things and still do and keep going. And what that means is you understood some very basic stuff about building compiler, what those hard decisions are. This is a lot more important, because next time when you decide to compile, you actually have that knowledge. Because you actually had made the decision yourself. So I think that is key. So if we look at a compiler, what's a compiler? So you have a program. You do lexical analysis, get a token stream. You do syntax analysis, parser. Get a parse tree. Do some semantic analysis and have intermediate representation at the end. You code optimize, and we do it in a different order. But normally, the code optimization come-- you optimize intermediate representation. And finally a code generator and design assembly. So here's a question. Why do you need a lexical analyzer, a syntax analyzer? Why can't both be combined to one? Can you combine it to one? Can't you just have a grammar that read just characters, and then write you expressions and your tokens-- your tokens also with the grammar. Why did-- AUDIENCE: And actually compares to have both of those in [INAUDIBLE]. PROFESSOR: OK, that's-- well, [INAUDIBLE].. That's good answer. What's else? Why can't-- AUDIENCE: Because you need to actually see the time thing for, actually, [INAUDIBLE]. PROFESSOR: Not really. I mean, in LR(1) one path, you had to only see a little bit forward. You can still do that. What else? And well, it stopped. AUDIENCE: I guess, there's syntax and things that maybe do with just tokens that might not fit in [INAUDIBLE]. PROFESSOR: Different-- AUDIENCE: [? Text representations. ?] PROFESSOR: Yeah. So here's my take on it. I think there are a few things. First of all, it's doable. But then, I probably have to-- I can do just-- if I do LR(1) passing, I look ahead one token. But now if I actually build, that gives me a lot of expression-- expressiveness in my language. But on the other hand, if I actually had to build the token myself, I probably had to look forward a lot further so in there. So I might have issue about looking forward. Because the minute I go there, I probably cannot make that decision. So there might be look-ahead issues in there. Second, I think it's a little bit of historical reasons because for a long time, passing was a very computation-heavy thing. I mean, to build this past reason, and large programs took a lot of time, a lot of effort, very large tables, and stuff like that. It was taxing programs. So by doing lexical analysis, you really brought in having 100,000 characters, you only have 5,000 tokens. So it really reduces what you had to do. So in some sense, when you are really doing that, that breakdown was a good logical breakdown. But on the other hand, I think you can do both together. And there might be issues in there. But it's not-- I don't think it's fundamental. So in lexical analysis, what do you do? You have tokens out of character string. And we did it with regular expressions. And I'm just picking sides. I mean, you should know these things. I don't have to go and explain what regular expression works. So this is just to bring those things back to your mind. And then, we dealt with lexical analysis. So what we did was, we defined regular expressions. And we built a nondeterministic finite automaton out of that NFA. And then, we came up with the algorithms, actually go from NFA to DFA. And then, DFA you can distribute it, go and build these tokens very nicely. So that was really a nice set of theory that we really use in compilers. Then we went to syntax analysis. So that means we start defining language using context-free grammars. So now we have much more expressive thing. We can actually create this grammars from modern languages in there. And so, here's a simple example where what you have is a bunch of this expression recognizer with minus plus and multiply simple thing. And then, we basically build a pass tree at the end. So you were token stream comes a pass tree at the end. So here's something a little bit new I am adding in here that we didn't talk this time in the class. So if you look at different grammars, the simplest grammar is regular expression. It's a grammar. It's a very simple grammar. And the biggest grammar is a context-free grammar. That basically says you can define from a context-free language how biggest thing you can define. Something a little bit less is unambiguous grammar. So you can write a context-free grammar that's basically ambiguous. You don't know-- you have multiple ways of going that. And after that, you can create more and more restrictive sets by defining the technique you can use to identify that. So if, for example, if you have the LR(0) passer, there's a certain type of grammars it can recognize. Beyond that, it cannot recognize. And as we go, we can keep building higher and higher things. So for example, if you are doing a modern programming language, you can come and say, OK, this language can be represented in the grammar that LR(1) passer can pass. So you can have these multiple classes in there. And that's LR(k), k could be any number. So that's why I said, if I actually start doing the entire tokens also using grammar, so probably I have to have a very high k in the large k passer. So then, basically, we came up with this pass engine, which you give this table. And you have a state stack and a symbol stack. And then we just basically go streaming through the input data and look at the current symbol and look at what's in the stack. And we get a pass action given in the table. So you can have a generic engine given a passer table, and it lets you do the right thing. And of course, depending on the k, you have to actually look ahead too a little bit. And then, we came up with a method to divide these passer tables. A lot of these things, probably you can just completely forget about it after this class because you are not going to come up with and write a [INAUDIBLE] yourself to do a passage and that you probably use a tool that actually do [INAUDIBLE] or something like that to do the generator. So but the interesting thing to use that is when it comes and say there's a shift reducer. You at least know what it means. Instead of say, darn, what this mean? What do I have to do? Now you understand why it's saying that's a shift reduce error-- shift reduce conflict. And then, you actually can think through this and figure that out. And that's why this-- learning this was interesting. And then, we move into semantic analysis, did a bunch of checks to understand the beyond context freeness. That means that we are actually putting context into these checks in there, understand the types, uniqueness of variables. For control checks, you can do a bunch of things statically and also dynamic things like array bounds checks and null pointer checks that can be done. And by doing-- after doing that, we translate it into intermediate format that basically got rid of both a lot of language-specific thing and also machine-- dependent part. So you came up with a very simple representation that just basically understand the programming there. And we basically multiple layers in the high level that has more and more language specifics in the high level IR. And as we go to lower level, you eliminate a lot of this language-dependent stuff. And finally, one before the last we did code optimization. So a key thing is, we want to get as close as possible to what somebody would write by hand. And if we have good hand assembler. And also, we want to start taking advantage of, in this project, exploiting architectural strengths and weaknesses-- strengths to take advantage, and weaknesses you're trying to avoid in there. And we did a bunch of things. I guess we already do register location. And we are open to do other things in this thing. So good. That's the entire semester and in 10 minutes. So you guys have actually amazingly built a good compiler. And from all indications, every group managed to get a compiler working. This is amazing feat given how complex this project is and knowing this not only project you guys are also working on. And this is really cool. I think this is a great achievement to get all those things done. And so, now the fun part. This is optional, but I hope everybody participates. So what you want to do is figure out who has the fastest compiler, or at least who produces the fastest scores in the compiler. And some years, people have really surprised me. And from how well they do. And in fact, one year they managed to beat GCC03. And I didn't believe them. I said it can't be done. But after looking at it, they actually did it. And so, I was like, wow. So people-- I mean, I don't think they got any sleep for a few weeks. But besides that, I mean, so there's a lot of history behind it. So what I'm going to do is, for the first time we are going to give the program 12 hours in advance. The main reason is, I want to make sure you get all your optimizations working on this program. A lot of times, it is happen is, I give it an hour before, within the beginning of the hour. And for most people, some optimizations didn't work. And actually, there are some years that the most aggressive compilers didn't really win because they couldn't get all the optimizations to work. They did a lot of things. Whereas the few compilers that did some things that managed to be more robust wins. And this year, we give-- because of that, I'm going to give 12 hours. But this has a huge temptation. You look at the code and say, hmm, that statement there, it's a random number. I know what it does. I can actually replace it with this crazy thing. And I can get this to run so much faster. I don't want to see that. I mean, the thing is, if you win, then we'll actually examine what you compile it, and you explain the code and tell us how you achieved that. So don't try to do crazy things to make-- everything you do should be correct-- is provably correct. I mean, of course you are to look at a transformation. But you should be able to prove that it's doable. Because there are a lot of times you look at the program, you say, aha, this transformation is correct. But there's no way for you eyeball and you can do that. But you should be able to-- the compiler should be able to prove everything it does in fact is correct. So that's critical. So you can't look at the function and say, OK, I can do this. And it will work. That doesn't work. So you had actually say that, I go from A to B, and I-- in the compile in the code, not in your head, prove that going from A to B is correct. So I don't want that people do that. So this is on the 14th at 11:00 AM. And we'll announce a place where, and we'll provide some refreshments. So we can have fun. And I think this is a fun time to celebrate everybody's what they have done. So a little bit on so what's this all about. You had, you spend some bunch of sleepless nights building a compiler. What did you gain? I think, after you left this course, at many different levels I think you have gained something. Most of you will become a-- will be programming for a long time in your careers, and where you end up doing. And I think this whole course you actually learn something in that. And there are few things you might want to-- some direct things that we learn in this language you can use. Some of this-- and people you will be working with, things like new architectures, either from designing architectures, building compilers, this large group of easiest type end up doing things like that. And then, I think that will be useful. And very few of you, probably one or two, probably one in this class if I'm lucky will become a true compiler hacker and do a lot of it. But even if you don't do that, I think you learn a lot. So from an informed programmer's point of view, every program has to use a compiler. And with things like JIT compiler, things are much even more complex, more random, a lot of things happen. And for most of you until now, probably you treated the compiler as a black box. It's there, you give a program, something comes out, and it runs. And you had to trust it to do the right thing. And of course, compilers are programs. They have bugs too. And they have many implications for performance, debugging, correctness, that from now you should be able to just not trust this black box to be working all the time and trusting it all the time. So what did you learn from 6.035? You learned how optimizations work. And you also know why it didn't work. So assume you did something, and you expecting some results. And if it doesn't happen of what you expected, and you can look at that and say, aha, this is not to my fault, because I fooled the compiler. Compiler got fooled because of that. It might be optimization problem. It might be a correctness issue. There might be issues in there that [? new-- ?] now because you understand what the compiler does, can actually decipher that. And another interesting thing is, you should be able to read optimized code because you ran through the compiler and you're trying to debug. And there's this garbled grou-- a bunch of assembly instructions, and you don't know what's going on. And if you know how to optimize it, you say, aha, this is because it did comments about expression elimination. I see that. And that is why this code looks like this. And I think for debugging purposes, especially if you're doing low-level debugging, this is very useful. And for example, some of the stuff I have been in more recent is basically trying to reverse engineer production code. So you don't have source. You have a binary. And you need to figure out what's happening in the binary. And they are optimized code. And so, you look at a piece of instructions and you say, what kind of code would have produced that? And knowing optimizations, you say, aha, this is probably what it produced. And then after optimization, this is how it looks like. So knowing all these optimizations, what they do, will be really useful when you're debugging. Second thing, a lot of people use this kind of knowledge is doing simple language extensions. Most of the time when you have a large programming project, one of the easiest abstraction you can do is do a very simple language abstraction. You are assigned some-- read some complex input out there, XML file, or some very complex-- you try to define some kind of interface. You are doing-- you have a bunch of very high-level problem that, for example, you are trying to build interface for people to use. And you can say, OK, well, I can build a huge library where I can define a very simple language. And the nice thing about doing a language is you can easily add nice functionality in here and check for semantics. And you can, instead of having a language-type thing where if people make mistakes it's hard to understand, building a simple language you can actually give people warnings and guide people within the domain you want to be. And you can also help with optimization. So for example, if-- compilers show up in weird places postscript. It's a printer, but it's a language. It has optimization. OpenGL for graphics, XML, all those things, I mean, they don't sound like a programming language. But a lot of this actually is a language. There's a compiler and optimization, all those tools it comes into play. And a lot of times, my feeling is, as a programmer, at least when I was doing a lot of programming, every six months I came up with a problem, I said, the best solution is trying to come up with a simple language. And I'll do the language. And I'll compile into that language. And because of that, I can have this interface that is lot tighter than anything you could do with providing a Java interface or something like that. Because when you need to do that library, people abuse it, misuse it, do things. And in language, you can actually have much more better control, give good feedback, even do nice optimizations and stuff like that. And 6.035 actually provided a lot of it. I mean, if you are doing language, you need a, let's say, and a passer, and we actually gave you all those things-- tools, and you learn to do that. And using [INAUDIBLE] representations, how to build that-- you learned all those things. So next thing you might do is, if you are involved in more aspect of E side of computer architecture side, every processor-- there are many different processors. It's not just Intel x86 out there. There are hundreds of different process. Your cell phone has one. Your watch has one. If [INAUDIBLE] to build a process, you need to program it. And we need to program it, you need some kind of a toolchain, probably C Java, some kind of a compiler, or a [INAUDIBLE] that actually maps into that. And it has implications in many different directions. So for example. If you are more from the architecture side. If designing architects, you really need to have understanding about the compiler aspects. Also, every architecture has to have coupled with the back-end compiler that people targets that architecture. So I think that's-- from architect's point of view, there are a lot of cool things happening these days. I mean, I don't know how many of you are interested in that area. And people are looking at many different new architectures to build. How, if you are building architecture, ever become architect? You have to have a really good notion of what compilers can do and cannot do. So you can actually balance of what happens in hardware and what happens in software, with compilers is one come in the middle. So a very good architect is somebody-- is done by people who really understand the capabilities and limitations of the compiler. They can say, aha, I can get the compiler to do this. I don't have to do it in hardware. This is something that compilers can't do where I will do it in hardware. So that limitation that people understand actually builds very good hardware. So knowing that is very important, especially architects. And this is what you learn. [COUGHS] Excuse me. You need to-- you learn what are the capabilities and limitations of compilers. And you need to you learn how to think like a compiler right there. And also, every time you come up with a new architecture, you actually have to have a compiler toolchain. So there'll be even the same amount of people actually building the compiler toolchain. And what that means is, every architecture is a little bit different. And how to take advantage of that difference is generate the right code, generate optimal code, stuff like that. And in here, we learn a lot about that. We learned about intermediate representations, doing a lot of different optimizations, things like issues like assembly interfaces, register location issues, code scheduling issues, calling conventions, stuff like that. All those things come into play. So this is, I think, interesting what do we have to do. So all those interesting outcomes. I think there's a lot that you guys learned here. And then, finally, if you are not sick of compilers by now-- some of you might decide to actually become a true compiler hacker. Compilers are very interesting field. It combines things like theory, a lot of things you do, things like if you look at lexical analysis in passing, data [? analysis, ?] a lot of very serious theory behind it. And there's a lot of algorithms. Because everything in compiler is basically from algorithmic transformation. And also, there are the aspect, compilers is probably one of the most complex piece of code that you have to implement. So there's a huge amount of implementation system aspect of it. So it really spans the entire spectrum in there. In theory, what we do is basically we have to develop these general abstract concepts, and things like prove correctness, prove optimality. I mean, for example, interesting thing about something like Java these days. So when you get a Java piece of code, and you are running it, the people working on saying, can you actually prove things about it? So instead of blindly run it, i.e. You give me a proof that I can actually verify that, in fact, it's not going to do a bad thing. How do you do that? Then think of what's going to prove getting code. So code comes to the proof of its goodness. It says, I will give you these guarantees. And here's a proof. And you can go and verify. So there are a lot of those kind of things. It's very theoretical aspect for that. And also, programming languages also people are exploring lots of different ways. I mean, we are used to this imperative paradigm. But there are object-oriented programming, logic programming, very different paradigms to think, OK, it's like-- I think 001 kind of touches some of it. So what does it mean, the very abstract concept of programming? And a lot of these come in this theory. And here are some example past theory. And we talked about lattices in data flow. There are a lot of theory behind it. In programming language, there are people doing language just to evaluate this space in there. Algorithms-- and most of the things we have learned has huge amount of algorithms. And most transformation you have, you want to do something, and you need to have algorithmic solutions to do that. And think of-- we use things from all over the place-- graph theory, number theory, many different aspects come in-- optimization theory, all those things come in in doing a lot of compiler transformation. And also, there's another aspect of it. A lot of times, a lot of transformations are heuristics. Because the theoretical solution is too complicated. So people come up with heuristics. So there's a lot of algorithmic aspect and also a lot of heuristic development aspect. So there's a huge range in here for people who are-- there are a lot of good compiler people who are really good algorithms people. Things like one reason optimization people have been working is called partial redundancy elimination. It's like a combination of things like dead code elimination, and cost and propagation, and [INAUDIBLE] elimination. Can you combine all those into one? Register allocation by graph coloring. So here's the [INAUDIBLE] problem map into really concrete algorithm. Things like most of these architecture have these multimedia instructions-- how do we take advantage, how to compile to that? There's a lot of algorithms behind doing things like that. Come up with a compiler to this automatically. And finally, implementation. Compilers are probably one of the most complex pieces of software out there. I was fond of saying for a long time, one only saving grace for compilers is it's deterministic. For example, operating system is deterministic. But JIT compilers just completely threw that out, basically. Now you-- compilers-- they are not even deterministic. So it's complex. And it is very deterministic. You can replay things and get the same results. But JIT compilers, that entire concept is gone. And so, we are basically-- every problem of developing a large piece of software is in here. And any reasonable compiler has probably millions of lines of code. And the worst is, most of other-- for example, operating system comes in nice modules. Compilers, a lot of times, one gigantic big beast. It's just all has to work with very much more closer approximation then. Because in operating system, there are very well-defined interfaces. Compiler is very hard. It's the same piece of code that we keep working on. So compilers in some sense has a huge systems problem in here. JIT compilers-- compilers are entire in, I say-- one thing I say is the most lucrative compiler business in industry is actually all the databases. What's the database? It's basically BigQuery optimization engine. And also, there's some operating system aspect of it to get a consistency, persistency. And there are a lot of things you have to get. But one large aspect is a query optimization. So it's a compiler. A lot of-- if you go to Oracle, it's a large group of compiler-type people working on that. PostScript [INAUDIBLE],, it's a compiler. I mean, every HP's printer division has a bunch of compiler guys trying to figure out what's the best way to get from postscript [INAUDIBLE] drive. It's a compiler. So there are a lot of places-- XML parsing, XML processing, a lot of things have very compiler-oriented. So if thinking like a compiler hacker is very interesting in these things. So to be a compiler hacker, what did you learn in 6.035? Basically everything. I mean, everything we did is also put in this mainframe. So yeah, I have about 10 minutes before we do the [INAUDIBLE] review. And in that 10 minutes, I want to just switch gears a little bit and talk about current compiler research. So this is not going to be in the test. So don't worry about it. And don't [INAUDIBLE]. So I will go through something very far. So one thing I did was, if you're interested, I added a list of places where you find the most up-to-date compiler work. Most of them are in conference proceedings because we-- most compiler people don't publish in journals. With some areas, it's all journals. And I listed different proceedings. So if you are really interested, you can-- some of these journals available on the web. You can search for it. I can look it up. This is not exhaustive list. And this is some-- and depending on where your emphasis is, there are-- it's a very broad field. There are many, many other conferences, and journals, and places. But here is a subset. So what I want to do is switch from doing a class to give you a feel for what's happening at research. So I'm going to talk about a project that I'm doing in my group. There are a bunch of people out there setting up, they're actually working on this project. So I give you a very quick glimpse of what it's like. So one thing I'm building in my group that it's this really exciting thing is this new language and compiler for streaming domain. Streaming means everything that from audio, video, everything in cell phone, lot of applications today, they have data streaming through that, and how do we actually program it. So we have a language called StreamIt. And it provides a very high level abstraction for streams. So a lot of interesting things that drove us was mostly streaming applications are written in nice block, saying the block does something, feed their next block. I mean, if you look at the DSP class, it's all in block diagrams. And the minute you write C, the entire blocks vanishes all over the place. And then, it's a very ugly mess of C. And so we figured out, OK, how can you actually have a language that you actually have that representation? So another thing is, minute you go into language like C, those languages very hard to analyze. Because everything in single memory, everything in single control flow. All that nice representation is gone. So in this language, what we have done is create a-- still maintain that representation that gives us a nice ability to do a bunch of analysis. And then we build a compiler that to understand all those things and actually can do really cool amount of transformation. So let me give a little bit of a view of the language. It's, like, kind of like Java. So here's a filter. Here's a computer in it. And it has what we call init function. We have in the filter starts, it just calculate some local ones. And then, there's a thing called a work function that keep executing forever. So what happens is there's input data coming. This work function says, I look at two, peak two. I take-- I remove one from the input and produce one into the output. So every invocation of that, I look at two elements, I produce one and consume one. And the next invocation I will go up. So I keep doing this forever. And so, that's very representative of a lot of stream calculation. Same thing keep happening forever. So this is only one filter. And then, you can combine these filters from pipelines and parallelism for what we call split joints. And then there's a feedback loop when they have feedback. So by doing this, you can build this very complex higher larger graphs. So here's a very simple StreamIt program. The only thing to observe here, the important thing is, you have this graph representation and a text representation, we are almost one-to-one. You can look at a text and almost imagine what the graph is like. And what it is, is you build this graph. And you're instantiating these nodes into the graph. And then after, this program basically will build the graph. And then, when you say run, it keep running forever. So it's not-- what this program does is, the running of this program basically builds this entire graph. And you can build very large graph. So there are some examples of a graph of FM radio, Vocoder that basically process-- or separate amplitude and frequency of voice and do different processing in there. There's a GSM decoder in a cell phone. So these complicated graphs. Each node is doing a bunch of computation. And this is 3G, this next phone in there. And so, we can build this graph. So the next thing I want to do is talk about some kind of optimization we can do that. So a lot of these are for DSP application. So one thing we-- compilers do is we basically look at what people do by hand and automate it. So what a good compiler does is look at what assembly program is going to do by hand, trying to abstract it out, and automate it. So in here what we did was we looked at what DSP engineers do by hand and try to automate it. So one thing is DSP engineer look at is what we call linear state space filters. These are the ones that are input are basically linear functions, or outputs are linear function of inputs and some kind of internal state. And the state also keeps changing from a linear function. So in the DSP community, there's a lot of math around it. And what we try to do is come up with a way to automate that. So many DSP filters, programs fit into this category. So what that means is that each block is represented by-- if you have input coming in and some local state, and local state is updated using the inputs and the previous state, and output is also calculated using the inputs and the state. And you have some matrices A, B, C, D defining what's going on. So interesting thing here is, assume you have program in there. We came up with here's a representation of that program in this state. So A, B, C, D, you have some concrete values in here. And we came up with the linear data for analysis to look at the program and get to the right. So you write-- some would write this, something looking like that left. And we automatically do the data for analysis. And you can easily build the data from equations and do that. And we end up in this right. So the nice thing is, now when you get to this representation, so you learn different representation. We learn one interior representation in this program-- in this class. And here, we have using a different [INAUDIBLE] representation. It's basically four matrices representing each of these filters. And the nice thing about that is, when you have that you can do a lot of interesting optimizations using that representation. I will show on and show results for two others. So one thing you can do is combine adjacent filters. So assume the filter one is-- to simplify, I only use D. So output of the first filter, output is matrix multiplied by the input. And the second filter has the same thing. One thing we can do is, so the output of both is D2 time D1 time mu. And you can calculate this a priory at compile time and come up with a combined filter basically just do that. So instead of doing these two matrix as separate things, I can just do the matrix multiplication. Because D1 and D2 are basically constants. It's in the program. And I will do that matrix multiplication once and do that. So the nice thing about here is, so if we have these two filters like that, and I can come up with this combined filter. And in the first one, for each output we can have to do eight floating point operations to get the output. The second one you only need do six operations, because some of them are done once at the compile time. So I reduce the amount of operations you need to do. So by doing that, I can really eliminate a lot of computation. So this is an interesting optimization. And actually, if you do a lot-- the first-- the simulator basically eliminates one value. So it's only looking at one result. If the decimator eliminate a lot of values and only look at-- because a lot of times, in DSP algorithm, that's what they do, you down-sample. You only look at every 100th value. If you go, you can almost eliminate 75% of all the computation. So neat. So you can do optimization, you get rid of huge amount of computation. So in fact, for a bunch of programs we looked at, so the first line is show-- the yellow, the light blue line shows what the results of the optimizer I talk about. So most times, you can eliminate 80% of the computation. So 80% is huge. Basically, at the end of the day, most of the computation is gone. So there are two other optimizations we did. The second one here is basically-- a lot of in DSP, people go from free-- time down to frequency domain manually. We said, why don't we do it-- why don't we automate that? So we look at the program and said, aha, instead of doing all this thing in time domain, we'll automatically go to frequency domain, do a simplified calculation, and come back to time domain. And it's a very, very tedious process to do it manually. Because a lot of these matrices have to be transformed. A lot of weird [INAUDIBLE] conditions has to be met. It's all done automatically nicely in the compiler. And we get a huge amount of benefit. And then, we actually have a programming-- we have an algorithm to select what's the best transformations to do and get a very good performance [? move. ?] So here's the kind of things we do today. So for this very high-level new domain, we are building a compiler that basically trying to automate the DSP engineer. Because right now, this is all done by some guy by hand, basically, for each algorithm. So we are trying to automate that. So this kind of gives you a feel for what goes a few floors up here. There are some people here who are basically working on these projects as [INAUDIBLE].. And if anybody else is interested in working in these kind of projects as [INAUDIBLE] at some point, send me mail.

Comments

Popular posts from this blog

12. Ion Implantation and Annealing - Analytic Models and Monte Carlo

L17.4 Molecules and energy scales

Lecture 20 Maxwell Theory and its Canonical Quantization