CS 509, Advanced Programming II

Fall 2002 - Test 3


  1. Write a test case that determines if an implementation of the third assignment uses register renaming to optimize code.

    Describe the input script and expected output, and explain how the expected output satisfies the required test.


    One simple test is

    move r0  a
    move  b r0
    move r0  c
    move  d r0
    

    The effect of this code is

    b = a
    d = c
    

    The first and second (and third and fourth) statements can't be combined because of data dependencies. The first and third statements can't be combined because they both write register 0 and combining them in a single compound statement would cause a conflict over the value written to register 0. Without register renaming the optimized code would be the same size as the input code.

    However, renaming the register used in the first and second (or third and fourth) statements removes the conflict and allows the pairs to be combined:

    move r0  a ; move r1  b
    move  b r0 ; move  d r1
    


  2. Write a small code example showing why the std::remove(s, e, v) generic algorithm is misnamed.


    Because mutating generic algorithms can't change the size of a container, std::remove() is misnamed because it can't remove anything; that is, it can't decrease the size of a container by removing elements. For example:

    std::vector<int> ivec(10, 3);
    std::remove(ivec.begin(), ivec.end(), 3);
    assert(ivec.size() == 10);
    

    executes without error.

    A lot of people answered this without providing a code sample. It's important that you, in addition to understanding the problem, actually do what the problem asks you to do.


  3. One of these two loops is correct, the other is incorrect:

    a) for (C::iterator i = c.begin(); i != c.end(); i++)
         c.erase(i)
    
    b) C::iterator e = c.end()
       while (c.begin() != e)
         c.erase(c.begin());
    

    What is the type of container C?


    Loop a can never be correct, because the call to erase() invalidates i, which makes the effect of i++ undefined. Loop b is correct as long as erasing an element of c doesn't invalidate the iterator e. This means C can't be a vector, but it could be a list.


  4. Use asymptotic analysis to determine which of these two algorithms is the asymptotically faster.

    a) string str2;
       for (string::iterator cp = str1.begin(); cp != str1.end(); cp++)
         str2 += *cp;
    
    b) string str2; 
       for (string::iterator cp = str1.begin(); cp != str1.end(); cp++)
         str2 = *cp + str2;
    

    Make sure you clearly state your realistic assumptions.


    In loop a the calls to the iterator member functions take constant time. The loop body, which appends a character to the end of a string, also takes constant time if we ignore the need to resize the string, which we can do by assuming that str2 is properly sized before starting the loop, most likely by executing something like

    str2.capacity(str1.size())
    

    If you don't want to assume str2 is of proper size, then you have to make a guess as to what the resizing (and subsequent copying) behavior. One reasonable guess is a doubling from some initial size, say 1.

    To finish out loop a, it performs O(n) iterations, where n is the number of characters in str1. Under the simpler assumption of str2 being properly sized, loop a takes O(n)*O(1) = O(n) time to execute.

    In loop b the loop overhead is the same as it was for a. However, the loop body now takes O(n) times per iteration because it has to (in effect) push all the characters currently in str2 to the right one to make room for the new character (here we can reasonably assume that the temporary created to hold the new string resulting from the catenation is properly sized, and so there's only one copy done).

    Loop b also iterates the same number of times as did loop a, for an overall estimate of O(n)*O(n) = O(n2).

    Given the asymptotic estimates, loop a is faster than is loop b.

    As a sanity check, this graph

    plots data generated by this program. The graph shows that 1) it more expensive to add a character to the beginning of a string than to the end, and 2) as the string size grows, the cost of adding a character to the beginning of a string grows more rapidly than does the cost of adding a character to the end of a string.



This page last modified on 25 October 2002.