CS360 Midterm Exam #2. March 11, 2015. James S. Plank

Answers and Grading


Question 1: 12 Points

The only subtle part of this question was dealing with p and k. The for loop sets k so that it is:

0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07

The memcpy() statement overwrites the middle four bytes of k. However, is k[2] the least significant byte of 0x90abcdef, or the most signifcant? We don't know yet. However, we know that when we're done, k is either going to be:

0x00 0x01 0x90 0xab 0xcd 0xef 0x06 0x07

or

0x00 0x01 0xef 0xcd 0xab 0x90 0x06 0x07

Line zero shows us that k[2] is 0xef, so it is the second representation of k that we need to go with. That also means that when we represent integers, their least significant bytes go first. Now, we're ready to answer the question:

The program is in q1.c if you want to test it:
UNIX> gcc q1.c
UNIX> a.out
Line 0: 0xef
Line 1: 0x1234567
Line 2: 0x1efb38
Line 3: 0x7
Line 4: 0x90
Line 5: 0xcdef0100
Line 6: 0x70690ab
UNIX> 

Grading

Two points per line.

Here's the partial credit rubric:


Question 2: 12 Points

I was hoping the two problems would jump out at you: Fixing the first bug is easy -- don't call strdup(). To fix the second bug, you need to keep track of the end of rv, and simply call strcpy() or strcat() there. I call strcpy(), because that way I don't have to keep the string null-terminated when I add the space.

In my code, I add a second int which I name eorv. Then, I replace the second for() loop with:

  eorv = 0;
  for (i = 0; i < numwords; i++) {
    if (i != 0) {
      rv[eorv] = ' ';
      eorv++;
    }
    strcpy(rv+eorv, words[i]);
    eorv += strlen(words[i]);
  }
My loop is now O(mn). I have the old code in q2.cpp and the new code in q2-good.cpp. They both read words from standard input, build a words array, and then call build_string(). As you can see from q2-input.txt, the second is much faster:
UNIX> wc q2-input.txt
   11000   23298  174306 q2-input.txt
UNIX> g++ q2.cpp
UNIX> time a.out < q2-input.txt
1.571u 0.003s 0:01.57 100.0%	0+0k 0+0io 0pf+0w
UNIX> time sh -c "cat q2-input.txt q2-input.txt | a.out"
6.243u 0.016s 0:06.59 94.8%	0+0k 5+2io 0pf+0w
UNIX> g++ q2-good.cpp
UNIX> time a.out < q2-input.txt
0.032u 0.002s 0:00.03 100.0%	0+0k 0+0io 0pf+0w
UNIX> time sh -c "cat q2-input.txt q2-input.txt | a.out"
0.066u 0.011s 0:00.07 100.0%	0+0k 0+0io 0pf+0w
UNIX> 

Grading

Three points for spotting each problem. Three points for fixing each problem. I gave a point for spotting problems that weren't really problems. When you see "ML" in the grading, that means "Memory Leak".


Question 3: 8 Points

Here are the two lines of code that I was anticipating: Four points for a line that requires spilling, and four points for your explanation.


Question 4: 10 Points

Nuts and bolts assembler from the third Assembler lab.

a:
  push #8  / Allocate i and b.  i is [fp-4] and b is [fp]

  ld [fp+16] -> %r0    / i = *y
  ld [r0] -> %r0
  st %r0 -> [fp-4]

  ld [fp+12] -> %r0    / b = *x
  ld [r0] -> %r0
  st %r0 -> [fp]

  ld [fp-4] -> %r0    / return b[i]
  mov #4 -> %r1
  mul %r0, %r1 -> %r0
  ld [fp] -> %r1
  add %r0, %r1 -> %r0
  ld [r0] -> %r0
  ret

In q4.jas, I have a main() and b() that set up the stack in jassem exactly as in the question.

Grading


Question 5: 19 Points

Each part was worth a point.


Question 6: 18 Points

As you can see, these two procedures are identical, except one reads from ssd_buf, while the other writes to it. There are three egregious problems with this code, which all involve making too many system calls:

A more subtle problem involving system calls is that if ssd_read() needs to read from the page that's already loaded, there is no need to call load_ssd_page() at all.

There are more minor problems, and perhaps your compiler can figure some of them out, but why rely on that?

The code below solves all of these problems. You can bet that memcpy() has been written to use the widest word size possible to do its copying, so using it is much better than trying to write it yourself.

This can be improved, and were I writing these procedures for real, I would make the improvement. I'll tell you how after you see the code (which is in q6.c):

void ssd_read(char *buf, int size, int a)
{
  int page, bytes;

  page = a / 4096;
  a %= 4096;
  bytes = (a + size > 4096) ? (4096 - a) : size;
  if (ssd_bufid != page) load_ssd_page(page);
  memcpy(buf, ssd_buf+a, bytes);
  size -= bytes;
  buf += bytes;

  while (size > 0) {
    page++;
    load_ssd_page(page);
    bytes = (size > 4096) ? 4096 : size;
    memcpy(buf, ssd_buf, bytes);
    size -= bytes;
    buf += bytes;
  }
}

What's the improvement? Well, I only check to see if the first page is loaded already. It could be that one of the pages in the middle of the read is already in memory. I could read that page first, and then read the remainders.

Grading

Spotting the problems was worth 10 points -- these sum to more than 10, but you were capped at 10 points for this part: Fixing the problem was worth 8 points. If your code structure was off, you started off with four points, and if it was really off, you started with two points. I took off for things like calling flush_ssd_page(), not calling memcpy(), or not having a loop in your code.