CSE 30 -- Lecture 19 -- Dec 3


Answers to assignment 6 questions

This assignment is graded based on 50 points for efficiency, 20 points for comments (just a block describing what speedup techniques you used -- no comments intermixed with the code is needed, since we will not read your code), and 30 points on correctness.

If your program does not generate the correct output, you will not get full credit for efficiency; if the output ``looks like'' life, then you will get partial credit for correctness, and your efficiency score will be scaled: if it is extremely close to life, your efficiency score will be scaled by 0.6; if it is nowhere near, it will be 0. So, if you just take the given code and comment it, you will get 30 points for correctness and at most 20 points for commenting, or 50 points maximum. If you have a mostly correct program which is fast, you could get 50 * 0.6 = 30 points for efficiency and 20 points for commenting, plus some partial credit for correctness, or 50 + partial credit for correctness. So if your code is close to correct and is likely to get partial credit points, and it's reasonably fast, you should definitely turn that in. It is, of course, a judgement call as to how closely your code's output resembles that of life. If, for example, it looks exactly right in the center of the grid but does not handle the border properly, then you should expect to get the 0.6 scaling. If, on the other hand, it diverges very quickly from the correct output (e.g., will not even do the

. . .
x x x
. . .
to
. x .
. x .
. x .
transformation properly), then you should expect to get a zero for efficiency.

There were still some confusion about modifying driver.asm. You may not modify it. You can make your own versions, and you can certainly inline the routines into your life_xform when it would otherwise call the routines. Your code, however, will be run with different drivers, and any modifications you make to driver.asm will be ignored.

User level threads

The design is that the main thread calls thread_spawn, perhaps several times, and then calls thread_wait to wait for all the spawned threads to finish running. None of the other threads should call thread_wait.

The C-intermixed-with-assembly implementation is:

struct thread_context {
	unsigned int	reg[32]; /* really fewer */
	int		alive;
} thread[256];

int	cur_thread = 0;

/* initialize the user-level threads package */
void	thread_init(void)
{
	int	i;
	for (i = 1; i < 256; i++) thread[i].alive = 0;
	thread[0].alive = 1;	/* main thread */
}

void	thread_yield(void)
{
	int	i;

	thread[cur_thread].reg[A0] = $a0;
	...
	thread[cur_thread].reg[S0] = $s0;
	...
	thread[cur_thread].reg[SP] = $sp;
	thread[cur_thread].reg[RA] = $ra;
	for (i = (cur_thread + 1) % 256; 1; i = (i+1) % 256) {
		if (thread[i].alive) {
			cur_thread = i;
			$a0 = thread[i].reg[A0];
			...
			$s0 = thread[i].reg[S0];
			...
			$sp = thread[i].reg[SP];
			$ra = thread[i].reg[RA];
			jr $ra
		}
	}
}

void	thread_exit(void)
{
	thread[cur_thread].alive = 0;
	thread_yield();
}
	
void	bootstrapper(void	*arg,
		     void	(*fn)(void *arg))
{
	(*fn)(arg);	/* jalr $a1; $a0 is already okay */
	thread_exit();
}

void	thread_spawn(void	(*fn)(void *arg),
		     void	*arg,
		     void	*stack)
{
	int	i;
	for (i = 1; i < 256; i++) {
		if (thread[i].alive == 0) {
			thread[i].reg[RA] = bootstrapper;
			thread[i].reg[A0] = arg;
			thread[i].reg[A1] = fn;
			thread[i].reg[SP] = stack;
			return;
		}
	}
	/* error to reach here */
}

void	thread_wait(void)
{
	int	i, nliving;
	for (;;) {
		thread_yield();
		for (i = nliving = 0; i < 256; i++)
			if (thread[i].alive) nliving++
		if (nliving == 1)
			return;
	}
}

[ CSE home | CSE talks | bsy's home page | webster i/f | yahoo | lycos | altavista | pgp key svr | spam | commerce ]
picture of bsy

bsy@cse.ucsd.edu, last updated Wed Dec 3 20:05:27 PST 1997.

email bsy


Don't make me hand over my privacy keys!