Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Sunday, April 28, 2013

Iterating visitables


A common design pattern in programming is the 'visitor design pattern' (see here). The idea is that, the caller passes a visitor object to the callee, and the callee uses this object to make calls as it visits some internal structure that it maintains, passing the info about the visited objects as a parameter to the calls made on the visitor object.

Here is a real-world use case I run across recently: I was using a persistent R-tree library written in C++. R-tree is a tree used for indexing spatial data, such as 2-d and 3-d objects. This particular implementation provided a visitor interface, where querying the R-tree index was done by passing a visitor object to its query method. Every time a node of the tree is visited during the answering of the query, the query method makes a call to the visitor object, passing in the node being visited as a parameter.

An alternative approach, typically used with indexes like this is iteration (cursors in DB lingo). An iterator can be used to iterate through the results. In my use case, I needed to provide an iterator interface over the R-tree index. Doing this kind of adaptation requires use of non-traditional abstractions, which we will get to soon. Take a few minutes to think about a solution, and you will see that it is not at all straightforward.

Btw, I should note that the problem of providing an iterator interface on top of a visitor one is quite real, and something you can run across in real life programming, as I did. See here for a StackOverflow question about the same issue (which I tried to answer). It is also worth noting that the reverse problem of providing a visitor interface over an iterator one is trivial.

Here I will give a simple example that involves a counter to illustrate the problem. Let us first introduce abstract classes that define the concepts of visitor and visitable.

template<typename Data>
class Visitor
{
public:
  virtual ~Visitor() { }
  virtual bool visit(Data const & data) = 0;
};

template <typename Data>
class Visitable
{
public:
    virtual ~Visitable() {}
    virtual void performVisits(Visitor<Data> & visitor) = 0;
};

Let us illustrate their use with a simple counter that is visitable:

class Counter : public Visitable<int>
{
public:
    Counter(int start, int end)
        : start_(start), end_(end) {}
    void performVisits(Visitor<int> & visitor)
    {
        bool terminated = false;
        for (int current=start_; !terminated && current<=end_; ++current)
            terminated = visitor.visit(current);
    }
private:
    int start_;
    int end_;
};

Here, we have a Counter class that provides a Visitable interface for visiting numbers between 1 and n. Using it is rather straightforward:

class CounterVisitor : public Visitor<int>
{
public:
    bool visit(int const & data)
    {
        std::cerr << data << std::endl;
        return false; // not terminated
    }
};

int main(void)
{
    Counter counter(1, 100);
    CounterVisitor visitor;
    counter.performVisits(visitor);
    return EXIT_SUCCESS;
}

How can we convert this code to use iteration, rather than visitation? Let's first define the concept of an iterator:

template<typename Data>
class Iterator
{
public:
    virtual ~Iterator() {}
    virtual bool isValid()=0;
    virtual void moveToNext()=0;
    virtual Data const & getData()=0;
};

It is worth noting that I am not using STL style code here, to make the exposition more general. I am following a mainly OO approach. Here is what we want to do ideally:

int main(void)
{
    Counter counter(1, 100);
    Iterator<int> & it = magicFunction(counter);
    for (; it.isValid(); it.moveToNext()) {
        int const & data = it.getData();
        std::cerr << data << std::endl;
    }
    return EXIT_SUCCESS;
}

Below are two ideas on how to achieve this, both with serious shortcomings.

Idea 1) We write a wrapper that collects all data items into a collection and then we can provide the usual iteration interfaces.

Unfortunately, this idea has a critical shortcoming. It requires space in the order of number of items visited. In some applications this is unacceptable. A good example is the R-tree example I gave earlier, where the number of query results might be too large. If the only thing you want to do is to compute some aggregate over the results, or perhaps just look at the first k of the results, this approach will be very costly in some cases. So we rule it out as a general solution.

Idea 2) Another idea is to use threads.

We can spawn a new thread that will execute the performVisits method.  We can then start waiting on a conditional variable until the spawned thread signals us about a result. When a visit call is made on the spawned thread, the current data item will be saved into a shared variable, the spawned thread will signal us about the readiness of the result, and it will start waiting on another conditional variable until we signal it for the next request. This way our iterator can return the item stored in the shared variable when the getData method is called. When the moveToNext is called for the next step of the iteration, we could signal the spawned thread's conditional variable, so that it can do the next visit. Meanwhile we can wait on our conditional variable for the results. This could continue until the the visit is completed.

Unfortunately, this switching between threads is really costly if we end up doing it on a per-item basis. Alternatively, we can buffer some data items to improve performance. In general, threads are somewhat heavyweight, and also this kind of implementation is somewhat dirty. It can be made to work, however.

The second idea is actually moving us in the right direction: Threads have their own stack! So what we need is a different stack. We do not need concurrent execution, however. Instead we need alternating execution between two contexts with their own stacks. An abstraction that can provide this is 'coroutines'. See here, if you are not familiar with coroutines. Coroutines enable two functions to suspend and resume each other. That's where the name 'co' comes from. I won't describe them in detail here.

C++, being the flexible language it is, can support coroutines via a library. boost::coroutines is one such library and surprisingly it is very easy to use (assuming 'easy' can be applied to anything in boost).

Below, I will provide the code for a class that can convert a visitable into an iterator. Fasten your seatbelt:


template<typename Data>
class VisitableIterator : public Iterator<Data>
{
private:
    typedef boost::coroutines::coroutine<void()> coro_t;
    typedef coro_t::caller_type caller_t;
public:
    VisitableIterator(Visitable<Data> & visitable)
        : valid_(true), visitable_(visitable)
    {
        coro_ = coro_t(boost::bind(&VisitableIterator::visitCoro, this, _1));
    }
    inline bool isValid()
    {
        return valid_;
    }
    inline Data const & getData()
    {
        return visitor_.getData();
    }
    inline void moveToNext()
    {
        if(valid_)
            coro_();
    }
private:
    class InternalVisitor : public Visitor<Data>
    {
    public:
        InternalVisitor() {}
        inline bool visit(Data const & data)
        {
            data_ = &data;
            (*caller_)(); // return back to caller
            return false;
        }
        inline void setCaller(caller_t & caller)
        {
            caller_ = &caller;
        }
        inline Data const & getData()
        {
            return *data_;
        }
    private:
        Data const * data_;
        caller_t * caller_;
    };
    void visitCoro(coro_t::caller_type & caller)
    {
        visitor_.setCaller(caller);
        visitable_.performVisits(static_cast<Visitor<Data> &>(visitor_));
        valid_ = false;
    }
private:
    bool valid_;
    coro_t coro_;
    InternalVisitor visitor_;
    Visitable<Data> & visitable_;
};

In the code above, we see a class called VisitableIterator. The class provides a constructor that takes a Visitable object as parameter. Since it extends an Iterator, it provides the appropriate methods to perform iteration over the visitable object.

The code makes use of a coroutine. The coroutine is stored in the variable coro_. The code for the coroutine is specified in the visitCoro function. The visitCoro function takes as a parameter a caller object. The caller object represents the caller of the coroutine and is used to transfer the control back to the caller when needed. The visitCoro function makes use of a visitor object of type InternalVisitor, named visitor_. This is the object that implements the visit function. After performing each visit, it saves the current data item and transfers the control back to the caller. Since it needs to perform this transfer, it needs access to the caller object, which is kept in the caller_ variable. There are a few other details that should be obvious from the code.

Here is how we use this:

int main(void)
{
    Counter counter(1, 100);
    VisitableIterator<int> iter(static_cast<Visitable<int>&>(counter));
    for (; iter.isValid(); iter.moveToNext()) {
        int data = iter.getData();
        std::cerr << data << std::endl;
    }
    return EXIT_SUCCESS;
}

That was not too hard!

I have not yet investigated performance implications. Would update once I do.

Saturday, February 2, 2013

Varints

Variable length encoding of integers results in using less space for small integers and more for big ones. This comes handy in a few places. For instance, if you are encoding a string, you have the choice of using a null-terminated string or a length-encoded string. In the former case, you only use 1 extra byte, but end up with the restriction of not being able to include the 0 byte in your string. If you go the length-encoded way, then you can use 4-bytes to encode the string length. In this alternative, the 0 byte can appear in the string without any issue. However, for short strings, using 4 bytes for the length is very wasteful. To solve this, one can use variable length encoding for the string length, which makes it possible to use up only 1 byte for the encoding for a small string.

Another useful scenario is when you have a single integer type in your system, say a 64 bit signed integer, but most of the time, the integers you represent have a much smaller range. Again, the variable length encoding will help minimize the size of the encoding, in case you want to write things to disk or send data over the network. In fact, Google Protocol buffers uses a variable length encoding for a similar purpose. Wikipedia has an article on variable length encoding of integers here. Apparently it was popularized by the MIDI file format.  IBM's SPL language uses a simpler version of variable length encoding for serializing its string types (see here).

I want to open a parenthesis here and talk about type names in programming languages briefly. Until C99, there was a lot of confusion in C in terms of the sizes of the types. How big is a long? Well, it depends on the system. Historically, not all systems supported all sizes, so the size for different types were not fixed. With C99, inttypes.h now defines types such as int64_t and uint64_t, which remove the ambiguity (there is also the craziness of int_leastX_t and int_fastX_t stuff, and in fact uintX_t is optional, but let's not get into that). What surprises me is that, Java has decided to call its types byte, short, int, long, etc. I much prefer int8, int16, int32, int64. If short is defined as a signed integer with a size of 16 bits (which means the size is not an implementation detail), why not call it int16, which makes it easier to remember as well. 

Going back to variable length encoding, the idea is to divide the integer into 7-bit blocks, and use 1-byte (8-bits) to encode each block. The trailing 0-only bytes need not be encoded, saving space for small valued integers. When encoding, we go from the least significant block to most significant block, and set the most significant bit of each block to 1, except for the last one, which will have 0 as its most significant bit. As a result, a most significant bit value of 1 for a block is an indicator that the block is a continuation block, whereas a 0 value means that the block is the termination block. Here is an example:

89657 = 10101111000111001

Divide into 7-bit blocks

 101 - 0111100 - 0111001

Set the most significant bit to 1 for all but the leftmost block, which is padded with 0's instead:

00000101 - 10111100 - 10111001

Recall that we encode starting from the least significant block, so we get:

10111001 - 10111100 - 00000101

To decode, we apply the following procedure. If the first block starts with a 0, we have a single block. Otherwise, we collect all the consecutive blocks that start with 1, until we reach the block that starts with a 0 - the termination block. We then strip the most significant bits from each block and concatenate. In the running example, we have:

Strip the most significant bit:

0111001 - 0111100 - 0000101

Reverse block order:

0000101 - 0111100 - 0111001

And concatenate:

10101111000111001= 89657

This works for unsigned types. For singed types, the negative numbers will have their most significant bit set. So -1 will take a lot of space. If you have an application where you want the encoding to take up less space if the absolute value of the number is small, then you can apply a mapping like the following:

0 -> 0
-1 -> 1
1 -> 2
-2 -> 3
2 -> 4
...
-2^(n-1)+1 -> 2^n-3
2^(n-1)-1 -> 2^n-2 
-2^(n-1) -> 2^n-1 

Code not reusable is not worth writing. So, below is the C++ code for this, which I hope you will find reusable:

#include <inttypes.h>
#include <cstdlib>


class VarintUtils {
private:
    template <typename T>
    static char * encodeUnsigned(char * buf, T value) {
        do {
            uint8_t byte = static_cast<uint8_t>(value) & 0x7F;
            value >>= 7;
            if (value)
                byte |= 0x80;
            *(buf++) = byte;
        } while (value!=0);
        return buf;
    }

    template <typename T>
    static char * encodeSigned(char * buf, T value) {
        size_t const size = sizeof(T)<<3;
        T newValue = (value << 1) ^ (value >> (size-1));
        return encodeUnsigned(buf, toUnsigned(newValue));
    }

    template <typename T>
    static char * decodeUnsigned(char * buf, T & value) {
        value = 0;
        size_t shift = 0;
        uint8_t byte;
        do {
            byte = *(buf++);
            value |= (static_cast<T>(byte & 0x7F) << shift);
            shift += 7;
        } while ((byte&0x80)!=0);
        return buf;
    }

    template <typename T>
    static char * decodeSigned(char * buf, T & value) {
        buf = decodeUnsigned(buf, toUnsignedRef(value));
        T half = toUnsigned(value) >> 1;
        value = (value & 1) ? ~half : half;
        return buf;
    }

    // conversion from signed to unsigned
    inline static uint32_t toUnsigned(int32_t value) { return value; }
    inline static uint64_t toUnsigned(int64_t value) { return value; }
    inline static uint32_t & toUnsignedRef(int32_t & value)
      { return reinterpret_cast<uint32_t &>(value); }
    inline static uint64_t & toUnsignedRef(int64_t & value)
      { return reinterpret_cast<uint64_t &>(value); }

public:
    inline static char * encode(char * buf, uint32_t value) {
        return encodeUnsigned(buf, value);
    }
    inline static char * encode(char * buf, uint64_t value) {
        return encodeUnsigned(buf, value);
    }
    inline static char * encode(char * buf, int32_t value) {
        return encodeSigned(buf, value);
    }
    inline static char * encode(char * buf, int64_t value) {
        return encodeSigned(buf, value);
    }
    inline static char * decode(char * buf, uint32_t & value) {
        return decodeUnsigned(buf, value);
    }
    inline static char * decode(char * buf, uint64_t & value) {
        return decodeUnsigned(buf, value);
    }
    inline static char * decode(char * buf, int32_t & value) {
        return decodeSigned(buf, value);
    }
    inline static char * decode(char * buf, int64_t & value) {
        return decodeSigned(buf, value);
    }
};

Interestingly enough, you can perform binary search on a variable length encoded buffer. You might wonder why this is useful. Well, imagine you have a large graph stored using the adjacency list layout. Further assume that you have remapped the vertex ids so that the ones with the highest degree have the lowest ids. Now you can get significant compression if you encode adjacency lists using the variable length encoding of vertex ids. However, once you do that, how are you going to find if a given vertex is a neighbor of another vertex? You would have to resort to a linear search. Well, it is relatively easy to implement binary search on a variable length encoded list (of course, assuming it is sorted).

The idea is to readjust the location of the mid byte so that it aligns with the start of a variable length encoded value. Here is the code:


class VarintUtils {
    ...
private:
    template <typename T>
    static char * binarySearchGeneric(char * start, char * end, T value) {
        char * origin = start;
        T refval;
        while (start < end) {
           char * mid = adjust(start + (end-start) / 2, origin);
           char * midNext = decode(mid, refval);
           if (refval==value)
               return mid;
           if (refval>value)
               end = mid;
           else
               start = midNext;
        }
        return NULL;
    }

    static char * adjust(char * bufCurr, char * bufStart) {
        if (isFinalByte(*bufCurr)) {
            if (bufCurr==bufStart || isFinalByte(*(bufCurr-1)))
                return bufCurr;
            bufCurr--;
        }
        while (bufCurr!=bufStart && !isFinalByte(*(bufCurr-1)))
            bufCurr--;
        return bufCurr;
    }

    inline static bool isFinalByte(char c) {
        return (0x80 & c)==0;
    }
public:
    inline static char * binarySearch(char * bufferStart, char * bufferEnd, uint32_t value) {
        return binarySearchGeneric(bufferStart, bufferEnd, value);
    }
    inline static char * binarySearch(char * bufferStart, char * bufferEnd, uint64_t value) {
        return binarySearchGeneric(bufferStart, bufferEnd, value);
    }
    inline static char * binarySearch(char * bufferStart, char * bufferEnd, int32_t value) {
        return binarySearchGeneric(bufferStart, bufferEnd, value);
    }
    inline static char * binarySearch(char * bufferStart, char * bufferEnd, int64_t value) {
        return binarySearchGeneric(bufferStart, bufferEnd, value);
    }
};




Well, one weakness of the above implementation is that, mid point is determined by considering the number of bytes between the start and the end. However, towards the end of the list, the numbers are larger and thus may take more bytes. Still, for long lists, this provides good speedup compared to a linear scan.

That's all for today...









Tuesday, December 13, 2011

C++: I hate you, I hate you, I hate you. Leave me alone! Yet, I find you strangely attractive.

Had a really bad day with C++. Two strange bugs ruined it for me.

The first one had to do with the use of the ternary operator without proper parenthesis around it, while working with the shift operator on streams:

ostringstream ostr;
ostr << "bla bla" << x?y:z;
// This becomes
(ostr << "bla bla" << x) ? y : z;
// Which was simply becoming

ostr << "bla bla" << x;
A stupid bug like this inside a distributed app caused me to spend several hours. The bug was manifesting itself in a barrier operation, where different sets of distributed processes that were supposed to barrier independently, were barriering all at once, and the resulting incorrect counter was causing a deadlock!

Once I fixed the bug, another one followed. This time operator overloading and function overloading together were causing trouble:

void foo(X x) {}

template<class Y>
void foo(Y y) {}

And there is a class Z that implements an evil cast operator to class X.

In this setup foo(z) resolves to foo<Z>(z) but the coder has intended to call foo((X)z). The code used to work before the templated overload was added.

This was causing a rather strange segfault in a distributed app, and for some reason not showing up in standalone mode. After some more hours, this bug got cleared as well.

Given that I also spent a good deal of debugging and refactoring time yesterday with the evil problem of static object construction order (which is best eliminated via lazy initialization), as well as destruction order, I started to have some questions about C++. Not that I don't know all these problems. I do know them very well. But when there is so much depth and complexity in the application logic (multiple threads, database connections, forked processes, incremental checkpointing, cross-process synchronization, etc), having to deal with the warts of C++ becomes really annoying.

However, despite all its shortcomings, there is something about C++ that still attracts me. It is somewhat like enjoying the smell of your own armpit.