 And music almost makes me too excited to teach class. OK, so today our goal is to finish talking about scheduling algorithms. So I know I promised on Wednesday we would do a fun story time-type lecture today. But we're going to do that. Well, I haven't decided it. I think we'll do that on Monday because it's kind of a fun lecture and it gives you an introduction to a real scheduling algorithm. The other option is I can just have you guys watch a lecture from last year online. And so I'll think about it. So today we're going to finish our schedule on algorithms that we'll be able to review and that's what we're going to do. And we might, I don't want to excite anybody because it's Friday, we might finish just a little bit early. But maybe not. But yeah, I'm trying to kind of re-synchronize a little bit with last year. So maybe we will, maybe we won't. OK, so next week, Aditya and I talked yesterday about what to cover in recitation next week. And I know a lot of people are still working on assignment one, but you kind of got to like move on. If you're still, some of you guys are still submitting stuff for assignment zero, which is making me a little bit nervous. So next year, yeah, I wish. Next week's recitations, we'll start covering assignment two. And that's kind of, we're going to keep the recitations presenting material that's indicative of where you guys should be with the programming assignments. And if you're not there, then it's maybe time to put in a little work over the weekend so that you can catch up. So yeah, and then it was Gino actually was telling me. It's like, oh, I heard last year, the end of this class was really intense. And that was true last year because we were behind all semester, and there were a number of assignments that came out at the very end. This year, those assignments are up already. So last year, that was true. And this year, it really depends on what you do now. So if you want to have a miserable final two weeks of the semester and still do badly, you can do that. If you want to have a miserable two or three weeks now and then be out enjoying the nice spring weather in April, you can do that too. So again, this is really up to you guys. And there's just, you guys know this. There's one deadline in this class. All right, so yeah, there you go. Get cracking. So on Wednesday, we started talking about scheduling. We talked about the general principles that underlie scheduling. We talked about some of the tensions that make scheduling an interesting problem. And we talked about some of the goals of scheduling. And then we looked at two, well, one or two very, very simple scheduling algorithms that don't do anything smart, that are zero information schedulers. So any questions about that before we do a little bit of review? Or questions about anything at this point? Of course, administration, assignments, et cetera. So what is scheduling? I'm going to get a victim today. Satish, OK? Oh, OK, that's not bad. What in particular are we talking about or were we talking about Wednesday, Paul? OK, so we're talking about the CPU. I'm just picking the parts of your answers So we're talking about scheduling the CPU. But when you're actually like, you're the schedule, or what is your job? Sure. You're deciding which thread is going to run. At various points, and we'll talk in a second about when you get to make this decision, you are in charge of selecting a thread to run. What is the population of threads that you are selecting from, Kevin? Threads, in what state does the thread have to be in order to be considered by the scheduler? Waiting? Somebody want to amend that answer? Sam? That's be ready. In fact, the waiting threads are ones who are not considered by the scheduler because they can't run. They're waiting for something to happen. So if a thread is not on the ready queue, it's not considered for scheduling. Why does the operating system have this responsibility? There's a number of different directions you can take this question in, Spencer. Yeah, so on one hand, the first thing is we have more threads than we have cores. So we have to make this choice. If we had fewer threads than we have cores, how could we do the scheduling then? Yeah? No, no, no. Let's say I have more cores than I have threads. How do I schedule threads at that point? Thornton. Dump them on a core? I like the haphazard nature of that response. But why does that make things easy? This is not intended to be a trick question. Peng. OK, kernel privilege. So that's the way that we schedule threads. Tom? Yeah, I can just give each thread its own core. Maybe that's what Thornton meant by dump them on cores. I can just say, hey, thread one, you're on core one. There you go. You're on core. Have it. Do what you want. It's not a problem. So until I have more threads than I have cores, I don't really have a scheduling problem. But in general, I always have more threads than I have cores. So I always have a scheduling problem. And then somebody else mentioned the other reason. This is our job. Like you guys put the kernel in charge of doing this, and so it has to do it. When do I have a chance to, OK, so now we know when you're the scheduler, somebody comes to you, the type of question they're asking, and why they're coming to you. So the question is, which thread should run? The reason they're coming to you is because you're in charge here. Now when does this happen? What is your, is it tau? Yeah, what's that? There's a couple of different times. So there's like, I think, I wish I could remember, maybe four pairs of students in this class with the same name. But I like the fact that tau and tau sit on opposite sides of the room. The Spencer's are more difficult because they're usually like right in line with each other. OK, so should we go to the left side of the room tau? When do I get a chance to schedule threads? What's that? Yeah, so when a thread exits, stops running, then, yeah, now I've got a core that's free. So I should find another thread to run. When's another time, Wembley? Yeah, when a thread makes a system call or, right, so when a thread goes from ready to waiting, right, then, or from running to waiting, sorry, that wouldn't happen when I make a box system call, then I also have a core free. What other cases do I have cores that open up? Yeah, so remember, in order to be able to yank threads off the CPU, if one of these things doesn't happen, right, if they don't voluntarily give up the CPU for some reason because they blocked or exited, I've set up this mechanism where there's a timer that fires periodically that gives me a chance to stop a thread, right? So I always know that the amount of time a thread will run on a CPU is bounded before the kernel gets a chance to make another schedule indecision. I think there's one more, Andrew, when it yields, right? So a thread can voluntarily give up the CPU by calling yield, right? If a thread makes a blocking system call, it has to sleep until the call is done, right, so I can set up something else. When a thread exits, clearly it's gone and something else can use that resource. And when the kernel decides that a thread has run for long enough, right? And the way that the kernel is guaranteed to be able to make this decision repeatedly is through timer, right? So remember, we talked about these human-computer interaction to kind of our expectations from the machine, right? So who can give me an example of a respond-type expectation? Brian, I realize I don't pick on the people in the front of the room enough, so. Yeah, when you click on something on the screen, you expect some feedback that the machine heard what you said, right? It's kind of like when you have a dog and you tell it to do something. It doesn't look at you. Sometimes it looks to you like, yeah, I heard you. Still not going to do it. So what's a continue-type expectation, Tim? Yeah, the Tims are also problematic. Yeah, so playing a video, right? So I expect when I start something that that process is going to continue in some smooth way. And I notice if it doesn't, right? I notice if there's some break in the continuity expectation, right? Frames get dropped, things get played at weird speeds, music skips, or whatever. Those are things that humans tend to notice. And then what's this last one? I'd expect things to finish. Yeshas? Yeah, so I would argue some of the keystrokes are more open to respond because there's usually some feedback that's coming back to me, right? But what's the example of a, yeah, sure. What's that? Yeah, like some background tasks that I want to kind of run. I don't really want to notice it, right? I don't want to notice it happening, like defragmentation. That's our favorite example, right? I don't want to notice it happening. I don't want my machine to be slow, like noticeably slow because these things are going on. But I also want my disk to be clean, right? So I had a friend who used to work in an IT department. And there was a guy he worked with who, this was maybe 15 years ago. Whenever somebody came in with the machine that was slow, the guy would always run the defragmentation, right? That was his way of fixing everything, right? Just like run the defrag. And he would usually set up the machine to run the defragmentation at periodic intervals, right? And of course, inevitably, the company CTO came in with his machine and the guy fixed it, right? Set up the defrager to run and then the defrager popped up at some conference and started turning away while he was in the middle of a talk or something. It required apparently about a dozen attendees to figure out how to disable this, right? And the other funny defrager thing is I had a friend who wanted to write a, how many people remember the, does Windows still do this? Does Windows even have a defragmentation feature anymore? Wow. Does it solve the GUI where it will show you like the things being moved around and stuff? Oh man. So I had a friend who wanted to like write something that would just do that. So it would just spin the disk really hard. It would create a lot of IO. It wouldn't actually move your files around, right? But it would just like give you that visual feedback and then he wanted to ask people afterwards like, is your computer faster, right? Clearly it's cleaned up. All of the orange pieces are over on that side and the red pieces are over there. So it's gonna be fast. Anyway, I mean, so this guy will really actually matters a lot. I'm making light of it, but I don't know if defrager defragmentation has a huge impact anymore. But clearly Windows is still doing it so it must be important. It doesn't do it what? Uh-huh. Oh good, okay, well. Yeah, yeah, yeah, that's a good point. Yeah, we'll get back to disks later, but this is, so okay. So how do, okay, so going back to scheduling, sorry about the diversion. So how do we, what are some of the goals of scheduling, right? When we start to talk about, you know, we talked about a couple of scholars on Wednesday and we'll talk about a couple more today. What are the types of things that were, what are the metrics that we're using to evaluate them? Yeah, John. Okay, so as, I would argue thread priority is probably an input to the scheduler, right? As opposed to something that we look out of it. But you're right, you could evaluate the degree to which schedulers respect the priorities I give them, right? So if I give a task a lot of priority, then it should have access to more resources and that's a lower priority. If it doesn't, then I'm doing something wrong. Yeah, Brian. Okay, yeah, so that's a good point. So one way to compare schedulers is by comparing them to an oracular scheduler. And we'll talk about a few of those today that have access to scheduling information that normal schedulers wouldn't have on it. Yeah, how long does the scheduler take to run? Right, that's a great question and we'll talk a little bit more about that on Monday if we do the Linux scheduling lecture because that was a problem with old Linux schedulers. Yeah, yeah, so how efficiently do I, this is more what I was getting at. These are all great answers. How efficiently do I allocate resources, right? If I have a scheduler where I discovered that the CPU is sitting idle for long periods of time but there are still lots of tasks waiting to run, then I'm probably not doing a very good job of scheduling. But what's a, so that's kind of like the throughput argument, right? I want, assuming I have tasks that can run, that can use resources, I want those resources in use, right? I want all the memory bandwidth in use, I want the CPU constantly busy, I want the disk churning away, right? I want to keep everything busy all at once, right? Assuming that there's work to do, right? Yeah, actually, what else? Okay, so I mean that's more of a kernel thing, right? Like how optimized is the context which path? And that ends up having an impact on scheduling overhead. But what's the other, like when we go back here, right? What's the other thing that I'm interested in with schedulers? What else can schedulers do or not do? Yeah, Paul. Yeah, so how well do I meet deadlines, right? Deadlines are at the root of responsiveness and continuity, right? You know, responsiveness is about these unpredictable deadlines, so I click on something and I need to draw, I need to redraw the screen or I need to perform some action on your behalf, right? And then continuity is this idea of being able to meet these recurring tasks deadlines over time, right? And dribbling stuff out to the audio card or whatever. And yeah, so these are the two things that are kind of intention here, right? Bandwidth, complete allocation of resources, and the ability to meet deadlines, right? And on human facing instances, we talked about how interactivity is usually the winner, right? Because you don't notice the other thing, right? You don't know if your system is really, it's really making full use of the resources, but you do know when it makes you wait, right? When it makes you slow down when it feels unresponsive, right? And then, yeah, so someone else pointed this out, if, you know, if I, and this is an important thing, this is a tension that we'll come back to in systems design in general, right? You know, so somebody might, you know, say, hey, I've got this great new schedule and algorithm. And it improves, you know, interactivity by 10% or it improves throughput by 10% or something, right? Maybe improves both, right? It takes four times longer to run. That's going to be a tough sell anywhere, right? I mean, this algorithm runs a lot, right? And we'll, again, we'll talk in the lecture on Linux scheduling, you'll talk about, you'll see some decisions that were made in order to just simply try to get the schedule to run more efficiently, because again, this runs a lot, right? Scheduling and algorithm gets called quite a bit. All right, and you know, and essentially we talked about how scheduling is trying to produce an optimal balance between meeting deadlines and optimizing resource allocation, right? Yeah, we just did this. Okay, any questions about scheduling principles and the stuff from Wednesday before we go on? Okay, good. All right, I just wanted to stop and I don't know if they're all here yet, but Guru is here, is Zeehe here? They picked a bad day to miss class. Well, let's all give Guru a little round of applause. I think he's been doing a very nice job of DTA. And I'll show this video to Zeehe and I did you too. And apparently they were doing a great job of DTAs until they decided not to come to class. They were probably busy with something. Okay, so, nope. So we talked on Wednesday about the types of input that we want our scheduling algorithms to have, right? So, you know, what would we like to know, right? We'd like to know what happened next, right? That would be great, right? And we'll talk today about a regular schedule that has access to that information and can do sort of optimal scheduling, just make optimal scheduling decisions, right? Usually as a substitute we take some data about what just happened, right? So, you know, we look at the past and we use the past to predict the future, right? You guys can say it along with me later. So, and then finally, you know, scheduling algorithms usually have ways to incorporate user input, give you some ability to modify the scheduling priorities. But let me ask you a question. I mean, how many people have ever done that on your system? I have one hand up. And by done that, I mean, what are you saying? I mean, given the system some explicit input as to scheduling, like used nice on Linux or I don't know what the equivalent is on Windows or how many people have ever had to do that obviously, right? So, I mean, to the degree that systems have priorities, they're not always about this either, right? Like sometimes the priorities are things that the system is guessing about the importance of different tasks, right? Because you guys aren't providing a lot of input, right? Which is normal. I mean, that's not, we don't want our computers to be constantly asking us like, hey, is this Firefox thing really important or should I like, you know, make it run really slowly, right? Sorry, Chrome or whatever. Okay, so, and then we talked about these scheduling algorithms that incorporate no information, right? So, you know, the no nothings, right? Random and round robin don't, you know, don't need any state. They collect no input and to some degree, you know, for example, you might argue that you could have a round robin schedule and a random schedule that incorporated priorities, right? And I'll leave that to you guys to think about how you would do that, right? So, but you could, so you could add priorities to this, right? And the other thing we mentioned is that these algorithms don't really reward in any useful way or, well, I don't know if they penalize, right? So we talked about this idea of threads surrendering the CPU before their time quantum expires, right? And we mentioned that that might be something that characterizes interactive threads, right? Because they spend a lot of their time waiting for a little bit of input and then doing a little bit of work and then waiting again, right? And if the system can notice that, then it might be something that we want to try to, try to, to notice and then use as an input into our algorithms. So we'll talk more about that later. And then, you know, to some degree, these, you know, these algorithms, to the degree that they're algorithms are sometimes used inside other scheduling algorithms, right? So for example, if I had a priority-based scheduler and I had a set of threads with the same priority and I had to pick one, I might pick one at random or I might rotate it some arbitrary way, right? So you see these sometimes as building blocks and other schedulers, right? Okay, so let's talk today about a different group, right? So these, this is what we would call, you know, the know-it-alls, right? These are, oracular schedulers with full access to the future state of the thread. So what is some information about the future of the thread that I might like to know? Okay, so I'm gonna argue that the know-it-alls, no. So remember, when I start running, I'm looking at threads, I know what state they're in, right? I know if they're ready, I know if they're waiting, you know. So I know that, right? But what else might I wanna know? You were starting to go in a good direction. Okay, yeah, so what resources on the system is it gonna use? That's a good answer, yeah. Okay, so I want the priorities, right? That's an input, but again, information about the future, right? Priorities is not about the future, I know that, right? That's already an input, same. Until what? Okay, until, you guys are getting closer to the answer that I want, Frank. How long it's gonna run until what? Or how long it's gonna wait until what? Okay, okay, I'll accept that answer. Two thirds isn't bad. So if a thread starts to run, I might wanna know how long it's gonna run before it blocks, right? It's gonna start running, and at some point, maybe it won't block, maybe it's never gonna block, maybe it's computing pi, right? It's not gonna block, but maybe it is gonna block. Maybe all it's gonna do is wake up, read one character from the character device, paint, and go back to sleep, right? So that would be nice to know. Let's say it's waiting on the waiting queue. What else might I wanna know about that? That's kind of maybe a little bit more obvious, Dan. So it's waiting, it's on the waiting queue, yeah. What it's waiting for, and what else? Yeah, how long is that thing gonna take, right? It's waiting for the disk to finish some read, right? How long is it gonna be before that happens, right? So again, one of the most important things is how long is it gonna run, right? Until it exits, not that common, until it yields sometimes, but particularly until it blocks, right, until it blocks or gives the kernel a chance to do something, right? Will it block or yield, right? Is it gonna just continue to crunch in a while, one loop, right, or something? And then if it's waiting, how long is it going to wait, right? How long is that thing gonna take, right? All right, so let's look at an example of a kind of know-it-all type scheduling algorithm, right? So I have these threads on the ready queue, right? Our old friends, and let's say what I know is I know how long each one of them is going to run. And this period of time is determined potentially by how long it, maybe in this particular, let's talk about an example where this is actually how long the thread's going to run before it exits, right? So each one of these threads for whatever reason is just doing a small amount of work, and then they're gonna exit, right? So one way to schedule them, assuming I knew this, would be to order them in order of how long they're going to run before they finish. This ordering is interesting because it produces, this is called shortest job for scheduling, right? This is another sort of classic simple schedule and algorithm that people discuss, right? And this can be particularly important for batch workloads, right? Let's say I was actually going to let the thread run until it finished, right? I'm not going to preempt it, right? And more of a batch schedule in the world, I'm giving it the whole machine and just going to let it go. So can anybody identify what does this produce, right? Let's say I have a bunch of tasks to run and they're gonna run until they finish and then they're gonna produce some result. If I order them by the one that's going to, you know, consume the least time first, what nice feature does this ordering produce? Churag, I'm ignoring you for a few minutes. Jen. Front, front, Jen. Yeah. Oh, I don't remember. What is it? The towers are the only people that are making things easy. Some of the duplicates are all over them. Sorry, people with the same name. Clearly not duplicates. You weren't answering the question I interrupted you. You don't really know. Okay, Jen. Well, you've had a few minutes to think about it. Anybody guess this? Robert. So it turns out that this minimizes the waiting time, right? This, so if I, you know, if I look at when do these, you know, if I averaged all of the waiting times here, this produces, you know, the smallest result, right? If I did it the other way, right, and I let the longest task run first, then that longest task is blocking everybody else, right? So in this, I get the first task done first and then I keep going to that order and this turns out it minimizes the total waiting time for all the tasks to complete, right? So this is a nice feature of this algorithm. This is the reason that we would use it, right? We talked about this a little bit on Wednesday, right? But here's another perspective on this, right? Another perspective on why we would prefer or why we would want to prioritize threads that aren't going to run for very long, right? Because when a thread blocks or yields, right? So remember, I had these multiple resources on the system, right? I've got the CPU, which is what we've been talking about for several weeks, but we're going to talk about memory, we're going to talk about disks, right? And we talked about how scheduling the CPU, the CPU is kind of the gatekeeper to all these other devices, right? I have to use the CPU in particular to do things like IO, right? Got to give the thread isn't going to be able to initiate IO if I don't let it run, right? So I have to let it run, it's going to issue IO. But the nice thing is, if all it's going to do is wake up and start some other resource going, then if I start that job first, now what's happened is, now the disk is working, right? And now while the disk is working, I can take a CPU bound job and let it use the CPU, right? So if I have two tasks, right? One is going to compute pi, and the other is going to initiate a big bunch of disk reads, right? Then I run the task that initiates the disk reads, now the disk is busy, right? That's good. That guy's blocked. I let the guy who's computing pi run, now the CPU is busy and the disk is busy, right? So let's walk through an example of this, right? So let's say that these are my threads. Here's how long they're going to run actively on the CPU. And then what this indicates is, I hate when I have to use this laser thing, how long it's going to wait, right? So this task T1 is going to run and then it's going to initiate some kind of request that's going to take this long to finish. Does this make sense? Okay, so I can annotate each one of these. T3 is just going to run. T3 is not going to initiate any task, right? T3 is just going to run and either exit or maybe just keep, maybe he just goes all the way over here and you know, the door or whatever. Slides aren't that wide, so it's difficult to show. All right, so there's T2 and T5. So again, so here's kind of the worst way to do this, right? The worst way is to say, let's take T3, right? T3 is not ever going to block or maybe he is in or he's just going to run for a while, but he's not going to initiate the use of any other part of the system, right? So if I let him run first and then I sort them in, essentially what I'm doing is I'm sorting them in decreasing order of how long they're going to run, okay? So what happens here? This is, so now all the tasks have run and here's, now what I'm showing here is the waiting time, right? So this guy ran and he had to wait all this time until the CPU could potential, let's say I repeat this process over and over again, right? So he, T3 ran, blocked and then had to wait all this time until he could run again, right? Same thing with these other guys, right? And actually, T2 would have to even wait longer, right? He'd have to wait all this too, right? So this is one half of the waiting and I guess the other half is under here, right? Bug in the slides, right? But anyway, you can see that at least until another scheduling quantum starts, right? You know, this is the waiting time, okay? So now let's do it the other way, right? Now let's say that we sort them by who's going to run the shortest amount of time first, right, which is what we did before, right? So now I start with T2, now I get T5 in there, T4, T1, T3, and the waiting time is reduced, right? Can people see that on the slides? Is that visually apparent? Let me go back and show you the other one, right? Okay, big red, you know? Little or red, okay? And why is this happening, right? Well, part of it is because I'm parallelizing more operation, right? The idea is that now this guy's waiting, but let's say this is the resources, the disk, I'm keeping this busy, right? Whereas here, with this other guy, there was this whole period of big block of time where the CPU is the only resource on the system that's being utilized, okay? This is something that we'll probably need to cover in recitation and go over a little bit more carefully, but this is shortest job first and this is why we do it, right? Questions about this, right? Yeah, longest waiting time. I don't know, I'd have to think about it. I think this produces actually the optimal allocation, but I'd have to think about it more. Sorry, any other questions about this? I don't want to rush through this because this is, oh no, I'm way behind my slides. Yeah, any other questions about this before we go on? Yeah, sure, right, so yeah, and this graph doesn't actually show that, right? But so for example, let's say this is, like let's say they're all trying to use the disk, right? So what would happen here is that this guy would get bumped over a little bit, right? This guy, they would all have to move a little bit, right? But there's a little overlap here and so, but the nice thing is they'd still be done by the time that the next round completed, right? You know, again, one of the big problems with this approach is this, right? This whole block of time here where the disk isn't in use, right? All right, so let's, so again, look, keep in mind these algorithms all have this knowledge that real scheduling algorithms do not have. Real scheduling algorithms do not know how, they don't know this and they don't know that either, right? They typically don't know how long a thread will run and when it blocks, like sometimes when a thread blocks you might be able to predict, hey, it's doing a certain IO to a certain disk and I have some idea of how long that takes, right? But in general, you might not be able to predict exactly how long a certain block and request are going to take, right? So the question is, how can we do better than random and round robin, which are the only algorithms we've talked about today that you could actually implement. But we're not gonna be able to implement these guys, but how can we do better? How can we do better and how can we do this a little bit, right? And these are the obvious reasons, right? Like control flow is unpredictable and users are unpredictable, right? I mean, some of the unpredictability of the system is because it's not inside your brain, right? So it doesn't know what you're about to do, right? Some of it is because it's genuinely difficult to predict control flow. So Guru's been working on, you know, doing some Linux hacking with me and he was mentioning the other day and I actually didn't know this that apparently in Linux, when they write if statements they've actually annotated different branches with compiler annotations that indicate how likely those branches are, right? So if they have an if else, they'll say, you know, if and then unlikely, right? Like, indicating to the compiler that this branch is unlikely to be taken, right? So that's just a signal that the compiler, the compiler's gonna try, right? The compiler will look at your code and try to figure out what's gonna happen, but it doesn't know, right? And these are just ways for humans to help it, right? Because the human knows that probably doesn't happen very often. So it annotates that branch and the compiler can do a better job of compiling the code and doing layout. So it's nice, right? So again, here's our mantra, right? Use the past to predict the future. Why don't we all say it together? Use the past to predict the future, okay? So if I look at what the thread did recently, my assumption is, and again, it's just, it's not true, but it's the best I can do, right? It's gonna keep doing something similar, right? So this is, oh, wow, there's all this text on this slide, so let's go through this slowly, right? So this is another sort of classic scheduling algorithm, right? Scheduling algorithm is called multi-level feedback cues, okay? So in order to implement multi-level feedback cues, here's what we do, right? The first thing we do is we choose the scheduling quant. This is preemptive scheduling and we're going to stop every thread after some period of time, right? And typically this is fixed, right? So I say the maximum amount of time a thread can run before I stop it and choose another thread to run is this much time, right? The next thing I do is I establish some number of cues. These correspond to levels and essentially also correspond to priorities, right? So when I start scheduling, I prefer threads in the top cues, right? And I'm gonna run my scheduling algorithm so that I start with those threads and potentially give them more opportunity to run. And then in general, the way I move threads between cues is based on this following algorithm. If a thread blocks, so I start a thread running and I say you can run for 10 seconds or that would be too long, right? 10 milliseconds. If a thread runs for four milliseconds and blocks, then I say good little thread, right? Like you are being promoted, right? I'm going to potentially move you up to a higher cue, okay? If a thread runs to the end of its quantum and I have to yank it off the CPU, I say, you know, bad little thread, right? Like I am going to move you down to a lower cue, okay? So right, if a thread blocks or yields, I raise it potentially. I don't necessarily raise it the first time. I might let it do that a few times or whatever. But in general, the way you go up is by blocking or yielding before your time quantum is finished. The way you go down is by repeatedly or using up your time quantum, right? Running to the end and still being busy when the timer fires and I have to come get you, okay? So let's look at an example of how this works with some real threats, right? And then we'll talk about why we would do this, right? So let's say at the beginning of time, everybody's in the top. Everyone's in the top level of my multi-level feedback. I could start people anywhere. I could put them in the bottom, right? So T3 starts running and there's his quantum and T3 blocks, okay? So when T3 comes back from the waiting cue, where is T3 going to go? It's going to stay in the top, right? Like he was a good little thread. He blocked, so I'm good, okay? Now I picked T5 and T5 hits the end of his quantum. So where does T5 go? T5 goes into the middle, right? Did someone say the bottom? No, just got one cue, man. I don't want to be in your multi-level feedback cue. All the way to the bottom with you. Do the dungeon. Give him another shot, man. It's like a three cues here, you know? Yeah, we should try that. The binary grading for the semester, right? One or zero. Okay, so now I chose another thread and let's say the T4 also blocks, okay? So, and notice too, the T4 and T3 went to the waiting cue, right? They are not ready to run anymore, right? Because they blocked and performed a system call or something, right? And then who's left? So I run T1, T1. Now, okay, what happened to T1? That's interesting. So T1 ran, it did not reach the end of its quantum but it's back on the ready cue. Called deal, right? T1 actually voluntarily said, I'm done, right? I'm gonna give up the CPU, right? And now T2 runs, T2 hits the end of its quantum and where do I put T2? Nick? Middle, okay, thank you. Didn't ask Wumbly. To the dungeon. No, to the middle, right? Okay, so let's run another round of this. So now, again, my schedule and algorithm looks at what's available and what does it do? Who's the next thread to run? Yeah, are T4 and T3 ready to run? No, they're waiting for something to happen that hasn't happened yet, right? So what's the next thread I'm going to run? T1, right? Because T1's still ready, it's at the top of the running cue, right? And now let's say that T1 blocks, right? And somehow it blocked, anyway. It actually went there and there, but somehow we stroked it and we saw it in both places at once, okay? And then now, so now T1 ran, it blocked, it's on the waiting cue, now who would I run? Jen? Yeah, T5 or T2, and here's a case where, you know, I could do some sort of round robin or random scheduling within each feedback cue, right? So now I'm gonna say I run T5, let's say T5 is computing pi, T5 hits the end of its quantum, and now, I can't believe where does T5 go? It's at the bottom, okay, there it is. And then T2 runs, well, okay, sorry. So now what happens is now let's say while T5 was running, T3, whatever T3 was waiting for happened, okay? So where does T3 go back to? Does anyone remember? Goes back to the top, right? And so now who runs next? T3, right? And T3 may, and T3 again, maybe blinking the cursor, right? So T3 is gonna run, it's not gonna exhaust its quantum, it goes back to the waiting cue, now who do I run? Spencer? T2, you had a different question though, right? Yeah. If you had two threads alternately calling you. Ah, good question. Yeah, I was thinking about this as I did this example. Let me think about that, ask that on piazza. I think there's, it's possible that yield doesn't actually get you the benefit that I claimed it does, right? Yield may not move you up, right? Yield may leave you where you are, but it may not move you up or something else. I'll have to think about it, but that's a good question. Wish I knew the answer to it, okay. Any other questions about this example? This example is, so I just have to say it, it's kind of a sad thing, but scheduling algorithms show up on exams because they're easy to ask questions about, yeah. Yeah. So right, so this is a good point, right? And yes, there is overhead to running the schedule, right? However, on most systems, the scheduler doesn't run, can you remind me what's the scheduling quantum on Linux? 100 milliseconds, right? So that's kind of long actually, right? So 100 milliseconds is long enough for a thread to run on a gigahertz CPU, 100 million instructions, right? The scheduling algorithm is not gonna take 100 million instructions, right? So when I really let a thread out and let it run on the CPU, for real systems, the scheduling quantum is chosen to be large enough so that the scheduling overhead is small, right? So yeah, I mean, I might spend a few thousand instructions or even 10,000 instructions figuring out which thread to run, right? But then I let it run for up to 100 million instructions. But it's a good question, yeah, whatever. How much smaller does it take? You're right, it's not usually the same. The timer usually fires faster than the time quantum, right? So there are cases where the timer fires, I enter the kernel and I say, I don't need to run the scheduling algorithm, right? And so I just put the thread that was running right back and get out, right? Yeah, that's a good question, yeah. Yeah, which is running the whole. What's that? You know, credit, okay, if this is going to take up the whole time quantum, then I know the next thread that's going to run. But if it is gonna yield, then I would have to recompute what is the next thread. Well, but it doesn't know, right? Like I don't know if a thread, like to some degree this algorithm starts to collect information about what threads have done recently, right? But again, I mean maybe Wembley would do this, right? But I'm just not gonna like, I'm not gonna confine thread, like T5 doesn't have to stay at the bottom, right? T5 may be doing some computation, but then maybe it's gonna display the result, right? So at some point, T5 may start to climb, right? So again, I don't know, right? All I'm doing is I'm saying, what did it do last, right? And I take some information about that, but if T5 starts to block frequently, then it'll, its priority will be boost, right? So, you know, because programs go through periods of time where they're doing a bunch of stuff and computing something and you know, and then they start accepting IO again or whatever. So your program execution is unpredictable in general. Sharad, do you have a question? Ah, yes, okay. So, okay, but let's come back to that, right? Because it's a very, very good point. So in general, though, when we look at this algorithm, what happens to CPU bound threads? A CPU bound thread or a thread that's in a period of its execution where it's CPU bound, where do they go? Bethany. Yeah, I mean, if a thread is CPU bound for a while, in general, like, which direction is it gonna go? It's gonna, boom, plummet, right? It's gonna descend into the depths, right? What about IO bound threads? Swetha, gotta go up. Yeah, I'll come back to you with a question. Yeah, so right, yeah, somebody already did, right? So in certain cases, right, and yes, multi-level feedback queues do have the starvation issue, right, in certain cases, threads that are in the bottom queues may never have a chance to run, depending on the behavior of threads above them, right? And this can be an issue, okay? One solution that, you know, I think people generally recognize as kind of a hack is just periodically to, you know, declare like an amdesty for all the threads and just put them all back in the middle queue or something, right, and they call it, rather than calling this a hack, they call it rebalancing, right, but it's kind of still a hack, right? I mean, it's just, so, and when we talk about some more mature scheduling algorithms that, doing this kind of stuff is, like we need to have to do something like this, you think something's wrong, this isn't a great idea, right? All right, so, so let's talk, let's talk just, I think we have time to get through the rest of this, so, yeah, okay. So we talked a little bit about priorities, I think people understand this, I think that, you know, and feedback queues are one way of establishing priorities, right? So if I establish a priority with each queue, right, what's happening is that the system as I go through this is using the recent behavior of threads to prioritize IO bound threads, right? Under the assumption that they're doing some kind of interactive task, potentially, and sort of penalize or deprioritize CPU bound threads, right, and this is kind of what happens with this. I don't want to talk about priorities too much on this slide, but one thing to point out with priorities is that priorities are always relative, right? So priorities can be used to help the system decide between two different tasks, but not necessarily figure out which one to do, right? And again, starvation can usually be a problem whenever I have strict adherence to priorities, right? So for example, if you think about the feedback queues, starvation occurs if I always look at the threads on the top, right? If I have an algorithm that just says if there was a thread in the top, run it else, if there was a thread in the medium, run it else, if there was a thread in the low, right? A way to get around this problem is to use more probabilistic approaches, right? So one approach is to say to hold, you know, to do what's called lottery scheduling, right? So instead of strict priorities, right, I give threads a certain chance of running that's based on their queue, right? So I give each thread in the top queue 10 tickets, I give each thread in the middle queue five and each thread in the bottom queue one, right? And then I pick a number at random and the thread holding that ticket runs, right? So in that case, the nice thing about it is that note, you know, there's really, you can show that there's not necessarily a chance that a thread will be starved, right? Even threads in the bottom queue have a non-zero chance of being able to run. So that can be a nice approach to this. And yeah, lottery, yeah, we didn't do this last year either, but we can, this is something that maybe we can, we can put up on piazza or something, so. Pire, lottery scheduling, one of the things lottery scheduling was introduced to do was to solve a certain problem with synchronization derivatives called priory. Priority inversion, okay? So, okay, yeah, we're done, I think, basically. So the last thing I just want to point out is, you know, these are examples of scheduling abstractions, right? I mean, priorities don't exist in any real way on your system, right? Priorities are an abstraction that's used by the system to help it make better scheduling decisions, right? And tickets are also that way, right? So on Monday, we're going to talk about Con Calivis. We're gonna talk a little bit about Linux development, and then we will introduce one of my favorite schedulers whose name may not be said while the camera is rolling, but, which is a neat schedule, actually, and several of the schedules we're talking about on Monday are quite cool, and they're understandable. And to some degree, they were developed to try to get away for some of the black magic that was being done in the kernel schedule. So this will be fun. So I think we will do this lecture. I've convinced myself. So we'll see you guys on Monday.