 All right. Hi. Back. So today is Friday. We're going to pretend like nothing happened this week. And I was here. How many people watch the videos? OK, that's good. Because I'm just going on like pretending like nothing happened. OK, so today what we're going to do after we get done in Mary and Maria-Claue is talk about schedulers. So last couple lectures went out to the point where we've motivated the scheduling problem. You guys understand why scheduling is important. And we've talked about some of the goals of thread scheduling, as I was on Wednesday. Today what we're going to do is actually start talking about some real schedulers. So today we'll present a couple of fairly simple schedulers. And then we'll talk about slightly more complicated schedulers. And then on Monday, I have a lecture where we look at something that was modern four years ago, but a relatively modern approach at improving schedule down on Linux. And kind of a fun story about Linux development in the Linux community. So we'll do that on Monday. OK, so you have two weeks from today to complete assignment two. How many people have started? OK, thank you. So unlike assignment one, we're not going to accidentally give out three-fifths of the points. If we do, I'm just going to take my ball and go home and just give you guys all A's and be done. And somebody will die. That's the other thing. Not me. So we're not going to do that. So the solution that you have right now, we check the assignment one solution set gets zero out of 150 on assignment two. It's exactly the way it should be. The assignment two solution set gets 150 out of 150. So there's a lot of points on the table for assignment two a lot more than for assignment one. So please start the assignment. A good portion of this assignment is also just kind of figuring out what to do. There's not necessarily a huge amount of code to write, especially for the file system system calls. Most of the work is being done for you. However, if you don't know how to do it, then you can end up writing a lot of extra code. And things are really ugly. So please come in, talk to the core staff in office hours. Remember, as I posted on Discord, starting next week, when you come for help in office hours, the first question that I am commanding the TAs to ask is show me where you design this. Show me your design document. So the design document, the format is up to you. I would suggest that you either write it in some sort of markup and check it into your repository so you and your partner can collaborate on it. Or another good alternative is put it in a Google doc. Please don't use Microsoft Word documents or terrible things like that. If you do, we will certainly help you, but we also make fun of you at the same time. But that's going to be the first question when you come into office hours. Where is your design? We want to see this designed. If you haven't designed it, we're going to tell you, go back and design it. When you're done designing it and then you take a stab at implementing it and you run into trouble, then we'll help you. So this is how we're enforcing that you need. You really need to design for this assignment trust me. So before we were getting ready for the class, Isaac, who's one of our ninjas, said, I tried that last year. He said, I tried not designing. And he said, I ended up making a mess and then starting over and designing it. So you can either go down that route if you want to, or you can just take my advice and design first. I know a lot of you guys aren't going to do it, but that's the point. This is a learning experience, but the TAs will be asking you to see designs. So the screencasters sign it to our up. Thanks to Guru for doing those. If you have any complaints, please forward them to Guru, not me. I think they're probably pretty good. I haven't seen them myself. It's awkward enough for me to watch me. I'm not going to watch Guru. So and if you are working alone on assignment two, please contact us. We have some information for you that you will probably want. And so if you haven't emailed us, or if you've emailed us, we haven't got back to you. We'll work through that queue today, but please email us if you're working alone on assignment two. Finally, any questions about test 161? So unfortunately, I wasn't here to see what happened on Monday. I think things went smoothly. It seems like everybody managed to use the tool. Not a lot of changes for assignment two. Actually, Scott's made a lot of, has fixed a bunch of things. So the tool's going to actually get easier to use. A little bit more intuitive. And we're going to be able to help you sort of handle some of the corner cases that you guys ran into where you didn't follow our instructions or whatever. So that will help. But pretty much, same deal for assignment two. Not much changes. Except this time, we'll be running a lot of things in user space rather than the Chrome. Any questions? Yeah? I have really a question about more of a comment. Yeah. How would I recommend putting in the troubleshooting section of that, making sure that it doesn't happen? Yep. Absolutely. This course did have somebody posted on there, because they probably had the same problem that I did. About 30 minutes, I was getting the not authenticating or whatever. And it's just because test was happening. And I'll try to remind you to do that. It's one command, happily. Just one command you have to run. I don't know. Maybe I'll add this to something so it happens repeatedly. But yeah, it's always safe to do. Whenever you want to, just we're not merging things into master for Dust 161 until they're ready to go. So if you're having a problem, it's not a bad thing to try. So yeah, we'll put it to the other troubleshooting document. Thank you for that comment. Any other questions, comments? We'll have the new targets for assignment two in the point breakdown updated on Monday. We have some changes that you're also going to have to integrate from our staff repo to OS 161 as well. OK. Oh yeah, I said the thing about partners. And I think I was trying to remember to say something. And I think that was it. So I'm good. OK. So let's do a little bit of reviews since I wasn't here. So describe what is thread scheduling? Fundamentally, what is the process of thread scheduling? What does it have to decide? Somebody describe the problem to me. Ron, want to take a stab at it? What is the number of things of prioritize scheduling? What resources to allow a particular thread to have access to to give important thread? How long to allow the threads to run? Well, let's try to make it as simple as possible, right? Those are all parts of the right answer. What is if you could boil it down to as simple as possible? Yeah. Yeah, exactly. I've got a bunch of threads that are runnable. Remember the thread state lecture we talked about the fact that there are times when threads are not runnable. They're waiting for something to happen, whatever. But I have a set of runnable threads. I have a set of cores. If that set of cores is larger than the set of runnable threads and I'm in good shape, I don't really have to make a decision. I do. There are some interesting subtleties to multi-core scheduling in terms of figuring out what gets to run where because there's a cost to moving threads between multiple cores. But let's ignore that for the time being. Essentially, if I have more cores than I have threads and I'm in OK shape, if I have more threads that are runnable, then I have cores that I have to decide what thread gets to run next or what threads get to run next if I'm on a multi-cores. And I have to repeatedly do that decision. So another way of thinking about thread scheduling is given a set of threads to run, what order am I going to run them in? Now the problem with that view is, of course, that set that's runnable is changing all the time because a thread is waiting for something, that way it completes, now it can run again, and so it comes back onto the run queue. So the best way to think about thread scheduling is a periodic decision that's made by the kernel where it looks at all the threads that are runnable and decides which are going to run now. Clearly, one of the things I want to consider is making sure that certain threads get to run eventually. If I'm picking the same threads over and over again, I'm not doing a good job of making all the resources on the machine available to all the threads. Does that make sense? So scheduling. So I'm choosing the next thread or threads to run on the CPUs. Why does the kernel have to do this? Why am I, why is thread scheduling something that the kernel has to do? Yeah. Yeah, well, right, I mean, if I don't schedule a thread it never gets to run, but the cores on the machine are one of the most important resources that the operating system is multiplexing. So this is, we talked about context switching as the mechanism by which I multiplex. Context switching and preemption make it possible for the operating system to regain control of a core so that it can choose another thread to run. This is the choice part. So this is the policy, whereas the context switching is the mechanism. Rhonda, do you have a comment or question? Oh, okay, well, I answered. I got to answer my own question. Makes me feel good. All right. And normally, and also it's good to point out, normally we have this problem, right, where I don't need to multiplex a resource if I have enough of it, but usually I have more threads that are ready to run than I have cores that I'm able to run them on. And so it's my decision as the kernel, this is my job, right? You elected me to be in charge of your system when you installed me and allowed me to run first. So I get to run in privilege mode. This is one of the things that goes along with that. So when does the kernel have an opportunity to schedule CPU cores? When does this decision get me? When can't, let's put it this way. When can the decision get me, for Josh? Well, okay, that's one of the cases, right? So if the current thread exits, clearly there's a core that's now free, okay? What else? What are other cases? Okay, yeah. Well, the context which is the mechanism, right? This is, yeah, so a context which occurs, but another way of asking this question is when does a context which happen? Yeah. Okay, so the timer, right? Remember, this is a key component of preemptive scheduling. Those timer interrupts that I know are guaranteed to arrive as long as interrupts are enabled, guarantee that the kernel has a periodic chance to reexamine the state of the world and decide what to do, right? So even if a thread just wants to compute pi or do something else that's very compute intensive, the timer interrupt makes sure that even if that thread doesn't block for other reasons, the kernel still gets a chance to run periodically. It can thus also happen when a thread exits. Thread can, in certain cases, threads can voluntarily give up the CPU. You saw this all over our test code and that's there because we're trying to get a good mix of, we're trying to make sure there's enough entropy in the threads as they pass through the test so that you can't do silly things in your synchronization problems where you assume something about the order of threads. So all that random yield or that you guys all over the place is just forcing that thread to repeatedly some random number of times give up the CPU and allow something else to run. In that case, the thread, remember, is still runnable so it's state doesn't change but it's allowing the kernel voluntarily to make another scheduling decision. When threads make system call, so this is an important opportunity for me to reschedule things if I want to because the kernel is now running. In some cases, a system call will cause a thread to block in which case I have a situation that's similar to thread exit where I now have a core that's free and so this is a chance for me to make a scheduling decision and choose another thread to run on that call and the timer. So is everybody clear about these conditions? Okay, technically the last one you can expand a little that any time there's an interrupt that gets processed in theory, the kernel is running and the kernel could decide that that thread, whatever thread was running when the interrupt fired, has run long enough. It's usually, I don't think usually most hardware interrupts are used to trigger the scheduler but I could be wrong. On your system, they're triggered by the timer. Okay, so on Wednesday we had talked about some of the goals and this is really something that's uniquely suited to interactive systems. On backend systems, the goals are sometimes a little bit simpler and this is something we'll come back to on Monday. So on a server you might say, you know, my goal is throughput, I just wanna maximize the number of tasks per hour, right? Then, you know, if you're Google you might say, my goal with this machine is just to allow it to handle as many search requests or whatever Google is doing with it per hour. Just maximize throughput. On an interactive machine, the expectation is a little bit different, right? So, you know, I need to make sure that when you interact with the machine, the latency is short enough that the machine feels interactive. So when you press a key, what do you expect to happen? You think that there's a character that should appear on the screen at a particular place. If that doesn't happen for a long period of time, the machine feels slow, right? We talked about the idea that certain things have to happen at regular intervals, such as when you're watching a movie or watching, listening to audio. You know, if I decide that it's a great time to compute Pi right in the middle of your YouTube video and I stall that YouTube video for long enough, you're gonna get annoyed because the frames are gonna start to drop. It's gonna stall, whatever. Usually that's not why YouTube videos stall. Usually it's network problems. And then the fact, the final thing is, there are these long running background jobs that you do expect to be able to complete periodically. So we talked about things like virus scans and building indexes for things like spotlight and search features that your computer has. So these are some of the expectations that you have as a user. You know, I've been having this very interesting experience. So I have this sort of insanely overpowered machine under my desk. I think it has like 20 cores. It's like a Xeon with 20 cores that has 64 gigabytes of memory. It has like two flash drives that are rated together for no reason. And it is slow, right? It is irritatingly slow sometimes, like interactively. So I'll hit a key to switch windows and you can like feel it stall. And I'm thinking, how on earth did I do this, right? I spent an insane amount of money on this stupid machine and I can't even switch windows without feeling like it's lagging a little bit. So scheduling matters, that's what I wanna point out. Like something is wrong with that machine. It is not the hardware. The hardware is golden. The problem is in some somehow in how the hardware is being used. I have no idea what the problem is. I wish I knew and I could fix it. But something is, something is going wrong. Okay. So then we talked about some ways to evaluate schedulers. So one way to evaluate schedulers is to say, is it capable of meeting these deadlines? So when the input event comes in, the timer starts. And if it doesn't finish some long series of actions before a particular period of time, you start to feel like the machine is lagging. And then this goal of interactivity is at some level very much in tension with trying to allocate all of the system's resources. So if I have a bunch of things to do, try to make sure that the CPU is all CPU cores are fully loaded. The storage is super busy if there are things that are trying to use storage. Essentially putting all of your computer's resources to full use all the time, right? So, and then I pointed out that on these human facing systems, deadlines usually take priority or they should. Again, if something seems to be going wrong on my machine, why is that? And the human facing system will usually try to bias things toward interactivity as opposed to towards throughput, towards making best use of the resources on the system. Why is that? Yeah. If everything for the user is running slow, they're just gonna get up and walk away. Yeah, I like that. It's talking about products. So if your product does a good job of that, then yeah, it's gonna feel slow. Even if it's doing a good job of making use of system resources. And the reason is that your time is way more valuable than your computers. So keep this in mind, always. When you're interacting with the computer, again, computers are super good at super things, at certain things like adding numbers, right? You have higher mental capacities that the computer does not have. So if the computer is wasting your time and it's making you hard to type and stuff like that, that's super annoying. And it's really a poor use of all the resources on the system. The computer may think, hey, I'm making great use of my memory, right? I'm really making all these cores run really fast. And you're thinking, I've been here for hours and I would like to go and think some higher thoughts, right? I'd like to write some poetry, paint a picture, all the stuff that computers cannot do. So stop slowing me down. I've got a life to live. Okay. And then the final thing that we were concerned about when we evaluate schedulers is performance. So if the overhead of scheduling itself is too high, nothing else matters. And this is another thing that we'll come back to over and over again when we talk about operating systems because operating systems are this interesting case. So some of you guys may have learned in other classes these super fancy algorithms and machine learning approaches to doing blah, blah, blah, blah, blah, blah, blah, right? And a lot of those don't get used by operating systems and you might think, well, why? And the answer is it doesn't matter. It doesn't matter enough. If you spend 10 times as long to make a decision that's only 10% better, who cares? Nobody cares. The operating system, you don't care. All you feel like is that the system is slow. Scheduling is wasted cycles. Any cycles that the operating system has to devote to scheduling are wasted. The point of scheduling is to make the resources available to the useful programs that are running, not have the kernel spend a lot of time to do some sort of fancy thing that doesn't improve performance very much. So this is something to keep in mind. Okay. And scheduling is sort of a balance between these things, right? Responsiveness requires meeting deadlines, continuity and other things require through. Okay. Any questions about this? I know we did more of you today than usual. Yes, that's a great question. So what happens when CPUs are idle? So it really depends. There's an idle loop on most operating systems that will just run no ops, right? So a lot of CPUs have a no-op instruction and they'll just sit in a loop running no ops, right? On the other hand, it's also advantageous on some systems for you to actually be able to power that core down. So on smartphones, for example, now there's usually a cost to taking the core on and off line to take some time. So I don't wanna do it too soon, but if your smartphone sees that a particular core has been idle for a while, it might decide, yeah, I don't need this core. I'm gonna shut it off. So it turns out going back to this issue of throughput, we've been doing some studies on smartphones and we found that the number of cores that are in use by typical smartphones is not very high. There's some thermal management problems that are partly to blame, but your four core smartphone, hopefully is not using those four cores very often, right? Okay, did that answer your question? Yeah, any other questions? That's a good question. Okay, all right, so let's go on. So today we're gonna talk about a couple of different types of schedulers. Whenever you talk about an algorithm, and scheduling, it's fundamentally, we're talking about an algorithm. It's useful to talk about some simple cases that are as simple as possible, right? So to the degree, so actually, so we're gonna talk about schedulers that use some different pieces of information, but we're also gonna talk about schedulers that don't use any of these pieces of information, right, because they're a good baseline. But what are some of the things that you might want to know, okay? So, again, scheduling is an algorithm. One of the inputs to your algorithm has to be the list of threads that can be run. What are some other things that a scheduler might want to know, or might want to keep track of, yeah? Okay, yeah, so what things are, what programs does the user are working with? That's very interesting, right? So that sort of makes me, I'm gonna lead that answer in the direction of sort of distinguishing between stuff that you're actually interacting with, like an interactive program and something that's running in the background, right? So that could be useful. What else, Ron? Yeah, so I might need to know something about the other resources that that thread is using, because keep in mind, the CPU is kind of the bottleneck for the rest of the system in terms of resources. If I want to use the disk, I have to run on the CPU. If I want to use the network, I have to run on the CPU. If I want to read or write to memory, I have to run on the CPU. Everything goes through the CPU. So even if all I want to do is fire off a network request that's about to take two minutes, I've got to run first. Just to be able to do this few instructions is probably not that small a number, to be able to use those other resources. So yeah, dependencies between resources on the system because the CPU, again, if I can't, even if all I want to do is use some other part of the system, I have to start with the CPU. I have to start by being able to run. Okay, so that's a good point. What else? Just basic stuff. What else would you want to know? Yeah, like I might want to know what the threat is going to do. If I could predict something about what the threat is going to do. I also probably want to know statistics about threads on the system. How often have they been run? How much time have they already had on the CPU? Ron bought off the issue of priorities, which is sort of a way to layer on other information. The scheduler doesn't necessarily know anything about why you've assigned priorities to threads, but the whole point of priorities is to give you a way of sort of layering on other information on top of the scheduler. So when you assign a high priority to a task or to a thread, you're telling the scheduler, hey, this is important. And the scheduler says, you know, I have no idea why you want to run that prime computing program at such a high priority, but it's not my business. I'm just gonna do what you said, right? All right, so here are some of the things. So what will happen next? So we're gonna talk about one scheduler, and this is, again, when you talk about these sorts of algorithms, it's always fun to talk about algorithms that you cannot implement. So this is what I refer to as the oracular scheduler. It is able to see the future. It has a crystal ball and can actually tell things about the future that we cannot in real life. The other, again, the reason to talk about schedulers like this is, again, they're good baselines, because if you can do almost as well as one of those schedulers that can predict the future on a real system, you're doing quite well, right? A lot of times, schedulers use historical information about what just happened. So how long has the thread run? How long has it run since it last blocked? Someone pointed out what resources is the thread using? And then what does the user want? So this is where priorities come into play. This gives you an ability to provide external inputs into the scheduling algorithm that are gonna be used alongside the other. Okay, so let's talk about, but first let's talk about some schedulers that do, that know nothing, right? So all of these schedulers take as inputs is the list of threads to run. So what is probably the simplest, simplest scheduling algorithm? Yeah? No, random, right? Why is random simpler than first and first out? What is first and first out have to maintain? A queue, random does not, right? So yeah, so the FIVO is pretty simple too, right? But random is a little bit simpler, right? So all random does is says, you gave me a list, I take a random number, I pick the one from the list, I'm done, right? I don't have to track anything about the thread. You give me a new list of threads, I take another random number, you get it, right? All right, so I think I have a long, incredibly detailed boring diagram to show you how randomness works in this context. Hopefully you'll enjoy it. But you know, so here's what happens, right? I've got a bunch of threads on the ready queue. They start to run. Now we're gonna use this as a basis when we talk about other schedulers. So something that schedulers also decide, or can decide when they start to run a thread, is what's the longest amount of time I'm gonna let that thread run before I make another scheduling decision? This is frequently known as a scheduling quantum. So here I've got the scheduling quantum right here. Okay, I've used my laser pointer for the day, that felt good. All right, so now what happens? So I've chosen thread T3 to run at random. Either one of two things happens now. There's two possible outcomes here. What's one of them? Well right, I mean it either runs to the quantum, at which point I need to stop it and make another scheduling decision, or it blocks at some point. So it's possible that this thread is going to run to its quantum. This thread is in the midst of doing some piece of computation. It doesn't need to make a system call. It doesn't need to block waiting for something to happen. Okay, now what does the random scheduler do? So the thread is exhausted, it's quantum. Where does this thread go? Go back to our states, right? This goes back into the ready pile. This is a pile right now to queue, this is random. This goes back into the ready pile. Then what do I do? I pick another one at random, okay? Now let's say this guy blocks, so where's he going? He's gotta be waiting for something if he finished before his quantum. Now technically that's not true. He could have yielded, but anyway, he wouldn't have yielded. She might have yielded, right? She's nicer than he is. He did not yield, he blocked. Okay, so what do I do next? Randomly select another one, run it till either it hits the quantum or it blocks. You guys get the idea, right? What else can happen here as I'm going along? What's the reason that I wanna be able to make this decision repeatedly? Yeah, yeah, this is random, right? So I could choose the same thread twice, right? If the run queue only has one thread on it, that I can just keep running that thread over and over. But even if the run queue has one thread on it, why do I wanna probably continue to re-run the scheduling algorithm? So let's say we get to the point where all the threads are waiting except for T3. I'm just picking T3. Why not just let T3 run until it blocks? Say, you know what, T3? Everybody else is asleep, you're busy. I'm just gonna not give you a quantum. I'm never gonna interrupt you. I'm just gonna let you run until you block. Why not? Why can't I do that? Yeah. Yeah, so it's possible another one of those threads that was over there in the waiting, it state changes. So normally what will happen is like some other interrupt will fire, like a network packet was received, and that interrupt will mark this thread as being ready again. It usually will not reschedule anything immediately. All it does is say, okay, this thread is no longer waiting. The next time the scheduler runs, it finds it in the ready pile and schedules it with everything else. This is why even if there's only one thread to run, I need to periodically reexamine my scheduling decision. I might give it a longer quantum because there's nobody else waiting, but I still need to periodically run the scheduler to see if the ready pile has changed. Okay. And on this goes. So random scheduling, here's my algorithm. I choose the scheduling quantum. This is common to a lot of the algorithms and a lot of scheduling, modern scheduling algorithms. We use multiple scheduling quantms under certain conditions, but you can imagine for simplicity's sake, without loss of generality, I choose one. Then I choose a thread at random. I run it till it blocks or the scheduling quantum expires. When the thread leaves the waiting state, I, oh, hello. I guess it's interesting. You guys could probably jam this thing, right? Somebody could be messing with me. Start to collect the signal from this device and start controlling this line. Anyway, if that's interesting to you, try it. I don't know, that would weird me out. So I put it back on the ready pile when it's done, right? Any questions about random? So why discuss random scheduling at all? This seems really obvious. Like why am I telling you about random? Why are we talking about random? Why are we about to talk about Ron Robbins? Why talk about the scheduling algorithm is so simple. Yeah. So it turns out random might not actually be that bad. So let's say you decide, like this guy we're gonna talk about on Monday, who's from Australia did, I can write a new scheduling algorithm. I have a better idea for how to schedule things. And this happens, right? Linux scheduler has changed in the recent history. And you put a lot of time and effort into it, a couple months go by and you're testing it and you think it works awesome, okay? Why might you want to have a random scheduler lying around? Yeah. Yeah, so if random outperforms your scheduler, you're not doing a very good job. And here's the thing, it's unlikely that random is making better decisions than your scheduler is making. But what is random also doing or not doing? Well, that's true, that's true. Ron Boat's out, it doesn't make good decisions or bad decisions. The random is also super fast. So it's possible that the overhead, like I said before, the overhead of your scheduler is actually so bad that random is catching up. Random isn't making good decisions or bad decisions, it's just making more decisions more quickly. And so the overhead of your scheduler is actually reducing some performance. So the simple scheduler is gonna be a great way to, great thing to compare it gets. Cause if you can't beat random, stop with that algorithm and give up. Cause it doesn't work, yeah. What do you mean? You need one. You're allowed to pick random threads in random because you're assuming the threads won't like use parts of memory that other threads are waiting on. They're handling critical search. Okay, so that's a good point. I mean, remember, if I had a critical section, so if I'm inside the kernel and I'm waiting on a lock, where am I going to be? Right? So let's say I've got 10 threads that are competed over a lock. Hopefully, where are they going to be? And I need to give him a point of time. Where? All of them? So hopefully nine of them will be in the waiting pile. One of them will be running. So whatever's inside the critical section will run. And then when it's done, it will use the wait channel to broadcast. Now, you make a good point about the fact that these schedulers can't necessarily see more complex dependencies between threads, right? In terms of using memory, we'll come back to that. I mean, the OS has lots of mechanisms to make sure that threads don't share memory or sound on each other's memory in other ways, right? If a thread dies, it's clearly taken off of the scheduling. Does that make sense? Cool. All right, so round robin scheduling is pretty much the same thing, except I have to establish some kind of order. One way to do it is when a thread goes to the wait queue, just put it back on the end of the queue. When it comes back, I might put it on the front of the queue if I'm trying to be nice because it did some waiting and now I can move it to the front of the line, right? But this is pretty much the same as random, except for the fact that I'm using, imposing order using a queue, okay? Okay, just said that. So, and these, again, these are by far the simplest scheduling algorithms, they don't use any information about the past, which they could collect, or the future, which they can't. And they don't accept any user input, but these are useful in the context of comparing them with other things. Oh, Lord Almighty, what's going on with this thing? The other thing I want to point out because we'll come back to this, is that these algorithms, because they're choosing a fixed size quantum every time, if I'm a thread that blocks a lot, like for example, if I'm a thread that's processing user input, so essentially what happens is the user presses a key, I wake up, I process the input, we go back to sleep, these algorithms have the effect of penalizing me. Why is that? So compare a thread that, when it wakes up only does a small amount of work before blocking again, with a thread that always uses its entire quantum. Who gets more CPU time on random and round robin? Yeah. You have to get blocked for the waiting. Well, okay, let me make the question a little bit different. Let's say that both threads, so let's say both threads have 100 chances to run. Let's say when the one thread blocks, it doesn't block for very long, okay? So it wakes up, does a small amount of work blocks, but then very quickly it's ready to run again. So let's say over a certain period of time, both threads run 100 times. Which thread has been able to use more CPU? That's it? Why? It's lost because it doesn't have an opportunity to reuse it. Yeah, exactly. So the point is if I block, whatever was left in my quantum is gone. Neither one of these schedulers cares. They're like, sorry, you blocked. This is how I work. You're back on the waiting queue. So the thread that runs is computing pi or doing something computationally intensive. It's gonna use all of those 100 quanta. And the other thread by definition is blocking quickly. And so it's gonna use very small amount each time. So it's gonna get a lot less CPU, right? And this is an issue with these types of scheduling approaches. So the last thing I wanna point out is that round robin's scheduling are sometimes random. There's certain cases where other schedulers will get to the point where a group of threads is equivalent from their perspective. I've done some other things. I've made some other calculations. I've used my priorities and other information I'm about to talk about. And what I end up with is to say, eh, these four threads are kind of the same. And so round robin can be a useful primitive at that point, because I might say, well, if I can't distinguish between threads anymore, the most fair thing to do is just to run them in order. Okay. So now let's talk about a different group of somewhat impractical schedulers. And these are schedulers that can actually use information about the future that would not normally be available through a schedule. So I'll call these the know-it-alls, right? And here's some of the things that we might wanna know about a thread before I actually start to run it. The first thing I might like to know is how long is it going to run for? And then, is it going to block or yield? Will it end up in the waiting queue for the waiting pile or back in the ready queue? And if it waits, how long is it going to wait? So if I'm thinking about, well, I'll come back to this when I show the diagram, but if I'm thinking about the case I just mentioned where there's a thread that's maybe rendering key presses, so it does a small amount of work and then blocks for a relatively long amount of time, because it's waiting for you and you're slow. The way I schedule these threads, if I knew this, I can actually make some pretty nice scheduling decisions, right? This is know-it-all cat. I don't know why. I think my cat looks smarter than that cat most of the time. Okay, so let's talk about how I might know this. So let's imagine this is my ready queue, ready pile. And let's imagine that the scheduler actually knows how long each thread is going to run before it blocks. So one way to schedule things at this point, there's an algorithm called shortest job first. So what shortest job first does is it says, and you can apply this either, you can either consider a job as being the time until I block again, or you can imagine that I knew exactly how long this task would take to finish whatever it's doing. But I interact with systems that usually doesn't happen. Yeah. What's that? Yeah, I mean, this is an instance of a greedy algorithm, right? Except that it knows something that the algorithm cannot know, right? So remember, normally I have no idea how long a thread is going to run before it blocks. And to some degree, this is unknowable, yeah. Great question. I was yielding so that could be an estimate for how long it'll run for. Yeah, so in certain cases, I might actually be able to, if I looked, yeah, so essentially if I looked at the code flow, why is this hard? It could. In certain cases, you might be able to pre-compute this. I feel like this is a great midterm question that's emerging out of this conversation. But what frustrates this sort of analysis? In some cases, you're right, right? In some cases, I might get it right. In fact, I might see that there's this huge swath of instructions with no jumps, no branches, no whatever. And I might say, oh, okay, well, this thread is definitely gonna run for a long time. Yeah. Yeah, so there's the problem with this, and this would be a fun thing to try, but the problem with this approach is in general, thread behavior at runtime is highly non-deterministic, right? It depends on state and memory, it depends on user inputs, it depends on all sorts of stuff, right? Now, it turns out, well, we'll come back to this when we talk about memory management in like a month, because it turns out there's, and I'll just leave this as a little bit of a hanging trivia question, there is a brief period of time where the execution of threads on the system is highly predictable. I'll just let you think about when that is, and we'll come back and talk about it when we talk about memory management. But in general, once threads start to run in like interact with state and read things from memory and you know, you're in charge and you're doing stupid things with the app or whatever, making that sort of prediction stuff, right? But it'll be a fun thing to try. Okay, so I know this, and what shortest job first does is it just orders the jobs, it runs things in order, from shortest to longest, okay? And why, so why do we use this algorithm? Like what is shortest job first actually achieving? Yeah? Well, the throughput's the same. Okay, so that's a great question. So I would argue the throughput is sort of, now if you look at instantaneous throughput, but if I say how long is it gonna take me to finish these five tasks? It doesn't matter at all, right? So you finish the first three in the amount of time that it takes to finish the last one, so maybe you finish more faster? Yeah, so it minimizes waiting time, right? So it turns out any other ordering of these, if I average out the waiting time, so the waiting time is, this is the waiting time for T2, this is for T5 from here, right? T4, T1, T3, if I average those out, this will minimize waiting time. Yeah. Yeah, so in this algorithm I run things until I know they're gonna finish, right? Because remember, I know that they're going to finish. Now again, on most systems they're not gonna finish, what they're gonna do is block, right? They're gonna do some sort of blocking operation that's gonna cause them to stop, right? Because we're not talking about a batch scheduling system here. Now on a batch scheduling system, let's say these were running for hours, this is still a good way to run things, right? Because these are some sort of job, like this is Google updating its indexes or whatever, and Google actually kind of cares about the minimum waiting time, right? Because something has to happen every night and the faster some of these things are done, the faster certain parts of their infrastructure are updated or whatever, right? So on batch scheduling systems, this can be really nice. On interactive systems, this requires, now on batch scheduling systems, you might also know this because you've run this job before, right? Every time you run this stupid job, it takes a relatively predictable amount of time, and so you can actually apply this schedule in all of them. On an interactive system where the threads are going to block, not finish, that's what makes it very hard, right? Because A, the time quantum's are much smaller, and B, it depends on a lot of things that are impossible to predict. Okay. So this minimizes waiting time. But let's try to apply a general principle here, right? So what we're gonna do today, and I guess on Monday, is talk a little bit about preferring threads that quickly give up the CPU. And this is a hint about what threads are doing that has made its way into a lot of schedulers, a lot of schedule in algorithms. So a thread that goes from blocked, runs for a little bit, and then blocks again, what does that thread possibly do? That the scheduler might want to be able to identify. What's causing that pattern of activity? Waiting for input, input from who? You, right? So a lot of scheduling algorithms, and again, we talk about the old, old, the next scheduler next time, that one of the improvements that the guy we're gonna talk about was trying to make to it, is that it had developed this interactivity detection that was completely, as he described it, black magic. It had all these weird computations it was doing to try to figure out this thing that sounds very simple, like is the thread interacting with the user? But it turns out it's very hard to figure out. It's very hard for the scheduler to figure this out. And he proposed something that did a better job with this, right? So, and oh, okay, sorry. So there's another reason for this. Sometimes I should let the slides tell me what to say. The other reason is, if a thread runs for a period of time and then goes back to sleep, what I probably allowed it to do is get some other part of the system busy. Now an interactive thread, that other part of the system is you, right? Because maybe what it did is it wrote the character of the console and now you're thinking. But if it runs quickly in blocks, it could be using the network, it could be using the disk. And so now it's done, and now I have one part of the system is busy, whatever it made busy in that short period of time, and I have a core back that I can use for something else. So in the pursuit of trying to keep a lot of different parts of the system busy, if all the thread needs to do is run, for like a very short period of time and then it's gonna fire off a big network request, that's great, right? I should do that right away, because now I've got the network busy and I can schedule some CPU activity on top, right? Okay, and just let me, so let's imagine in this case, I know not only how long the thread is going to run, but I also know how long the operation that it's about to perform is going to take. Now, these aren't really to scale. On a real system, like some of these would be like this and some would be like, you know, way out here or whatever, right? But this is what I had to do to get to fit on the slide. So you'll see, so here's the worst case scenario. Here's if I do the longest job first, okay? And you'll see that what's happened is that, you know, imagine that this is, you know, when this is scheduled, the CPU is busy, but when this is overlapping with the CPU, I've kept something else busy. And it turns out that all, if I look at the waiting time, so this is essentially the time between when the thread ran and the time it was waiting to have the CPU again. This, you can kind of see what size this area is. This is the worst case scenario. This is if I ran longest job first, okay? If I run shortest job first, what's happened is I've paralyzed a lot of the other things that the threads are doing, like this, this overlapped, this overlapped, pretty much every other operation that the threads were performing was done in parallel with the CPU being used. And so the amount of waiting time among all those threads is small. Can you guys see this on these slides? So if you compare this to that, right? See that, that's bigger, right? And it's bred because it's bad, right? So this is bigger, more bad stuff on this slide and less bad stuff on this slide. The way I've achieved this is simply by running the shortest job first, okay? What's that? Oh, sorry, oh gosh. I feel terrible about that. It should be like an angry face, right? Or, I don't know, be brown. No one likes brown, brown is bad. Okay. This makes sense, this might be a good, see where we are in time. Okay. So, so I'll just finish up here and then we'll come back to this on Monday. So we can't predict the future, right? Normally, right? I mean, we can't predictably predict the future. Let's put it that way. You can't rely on that. Control flow is unpredictable, users are unpredictable. And instead we use, and this is something that operating systems do all the time. This is a major design principle we're gonna come back to repeatedly over the course of the semester. Instead of predicting the future, what operating systems typically do is take a small window of the past, what just happened, and make the best assumption they can make, which is that whatever just happened is going to continue to happen. Is that wrong a lot of the times? Of course. But, it's also right some of the time, okay? And it's better than just doing random and pretending that things are always changing, right? But this design principle, using the past to predict the future, is something that underlies the design of the multi-level feedback queue schedule, which we will talk about on Monday. So have a great weekend. Good luck with assignment two. Please contact the course staff if you don't have a partner.