tl;dr

July 23, 2009

Some thoughts on flat hierarchies in C++

Filed under: Software — dublindan @ 4:27 pm
Tags: , , , ,

The top search terms for this blog seem to be “flat hierarchy C++”, so this is something I should address.

I had originally intended on writing a crude implementation in Python, to demonstrate the consept, leaving an actual efficient implementation as an exercise for the reader (possibly implementing it myself for a future post), but perhaps it would be more useful to think about it sooner. In this post, I will write a few short sentences to outline my thoughts on implementing a flat hierarchy in C++, which will hopefully provide readers with some ideas to implement this themselves.

There are two ways of implementing flat hierarchies which I am currently interested in exploring, though I’m sure theres more. I will briefly explain these here:

  1. Independent components which communicate over an anonymous shared-nothing messaging system. This works because the messaging is transparent – a component never knows which other components will receive its messages, if any. A receiving component likewise never knows which components sent the messages which it is receviing. This anonymous message sending means that components may be added or removed at will and other components will never know. Obviously it may not logically make sense to do this – if a component relies on certain messages to work and you remove the component which generates the messages, then obviously the first component cannot fnction – but it will not fail either, it will wait around until you add a component which does generate those messages back into the system. Each component is a node in a flat hierarchy.
  2. An entity system. In this context, an entity system is an alternative to object oriented systems where an object is simply a collection of traits and is a technique borrowed from the game development community. How this object behaves is determined by the traits it has associated with it. Traits may be added or removed at runtime. Each object is independent and can only communicate through its traits. Since traits can be added and removed dynamically, the system maintains a flat hierarchy. Objects can be processed independently and concurrently. The building of entity systems is the subject of my “Building a Flat Hierarchy” series of posts, so be sure to check those out, if interested.

Since entity systems are reasonably complex and are being covered in their own series of blog posts, I will not talk about them here. Instead I will briefly talk about the independent component method of building a flat software hierarchy.

Depicts how components communicate with each other

Depicts how components communicate with each other

Ok, so hopefully at this stage you have a reasonably good idea of why this system works. If theres still confusion, please leave a comment and I’ll try and clear things up. Time to move on to implementation details.

I’m not going to implement the entire thing and just give out the code for people to use because that would take more time than I have to write this post. Also, different projects have different requirements and a system I build now may not be ideal for whatever you have in mind. Best to leave it up to you to fine tune the details to your needs. The version presented here whould give you ideas, but it won’t be terribly good or efficient.

Lets start with a component:

class Component {
private:
     static std::map<unsigned int, std::vector<Component*> > eventMap;
protected:
     // Register for all events of type "type".
     void registerForEvent (const unsigned type);
     // Send an event.
     void send (const Event* const event);

public:
     // Event handler, called when an event, which is being listened for, is received.
     virtual void eventReceived(const Event* const event)=0;
};

And the implementation:

std::map<unsigned int, std::vector<Component*> > Component::eventMap;

void Component::registerForEvent (const unsigned int type) {
    eventMap[type].push_back(this);
}
void Component::send (const Event* const event) {
    std::vector<Component*>& components = eventMap[event->type];
    for (std::vector<Component*>::iterator i = components.begin(); i != components.end(); i++) {
        (*i)->eventReceived(event);
    }
}

All events are derived from the Event base type:

class Event {
public:
    Event (unsigned int t) : type(t) {}
    virtual ~Event () {}

    const unsigned int type;
};

Lets test this with some stupid events:

enum EventIdentifiers {
    QUIT_EVENT=0,
    PRINT_HELLO,
    PRINT_MESSAGE
};

class MessageEvent : public Event {
public:
    MessageEvent () : Event(PRINT_MESSAGE) {}
    virtual ~MessageEvent () {}

    std::string message;
};

Now I’ll implement some very simple and crude components, just to have something that can be run:

class HelloPrinter : public Component {
public:
    HelloPrinter () {
        registerForEvent(QUIT_EVENT);
        registerForEvent(PRINT_HELLO);
        registerForEvent(PRINT_MESSAGE);
    }
    virtual ~HelloPrinter () {}

    void eventReceived(const Event* const event) {
        if (event->type == QUIT_EVENT) {
            std::exit(0);
        }
        else if (event->type == PRINT_HELLO) {
            std::cout << "Hello!\n";
        }
        else if (event->type == PRINT_MESSAGE) {
            std::cout << "Message: " << static_cast<const MessageEvent*>(event)->message << "\n";
        }
    }

    void sendQuit () {
        Event event(QUIT_EVENT);
        send(&event);
    }
    void sendHello () {
        Event event(PRINT_HELLO);
        send(&event);
    }
     void sendHelloMessage (const std::string&amp; message) {
        MessageEvent event;
        event.message = message;
        send(&event);
    }
};

Ok, almost done. Lets tie it all together:

int main (int argc, char** argv) {
    HelloPrinter a, b, c;
    a.sendHello();
    b.sendHelloMessage("Hi from b");
    c.sendQuit();
}

This should print “Hello” three times followed by “Hi from b” three times and then it should quit.

Ok, I’ll be the first to admit that this implementation is extremely limited and suffers from a bunch of flaws – BUT hopefully you can see the ideas and use that to implement a much more powerful version. Before I leave you to experiment, let me briefly mention some improvements which could be made to the above code. I’m sure you can think of many more too:

  • Should the sending component receive its own events? Perhaps the sender should be able to choose.
  • Should events be processed right away? This is useful for simple programs, but for large systems, you may end up in situations where some components never get to process events because other events are always processed first. A good solution would be to queue up all events, instead of sending them right away and then dispatch them when all event handlers have returned.
  • Should events be processed sequentially? It may be better to process them in threads. One good solution would be to have a pool of threads which will execute the event handlers. Since communication is done through immutable events, the handlers can all run at once.
  • The events themselves can be constructed from a memory pool to make creating and destroying events (event large events) a fast and cheap operation.

At some point in the future, I will implement (because I will need an implementation soon) and explain an implementation which has all of those features and probably some others too. I cannot commit to a date though, could be tomorrow or it could be next year.. who knows.

I hope that seeing the idea in code has helped you understand what it is I’m talking about and hopefully this will spark your imagination and creativity and perhaps motivate you to implement your own, more advanced, efficient and powerful versions. I’d love to hear what people come up with, so if you do implement this yourself, I’d appreciate a quick comment!

July 20, 2009

What is dataflow?

Filed under: Concurrent Programming,Dataflow,Software — dublindan @ 3:39 pm
Tags: , ,

I was reading through old blog posts and came upon this gem in a 3 year old post on Cleo Saulniers blog, which I think is a near perfect description of what dataflow actually is. I know some of you were itching at a real discussion about what dataflow is and how it helps us. Well, here you go:

Many still can’t distinguish the difference between control statements and the traces on a circuit board. These are two and completely unrelated concepts.

  1. One is used to control what next component or instruction gets executed.
  2. The other determines what next component (or instruction) the data goes to.

Read the last two sentences again. Do you see the difference?

This is, in my opinion, the clearest, simplest contrast between dataflow and conventional programming paradigms that I have found. However, just in case this didn’t sink in, I’ll attempt to clarify:

  • Dataflow: material flows from person to person on an assembly line, where a person always performs the same action (but each individual person may perform a different action)
  • Not dataflow: a person, who performs a number of different operations, takes different materials as needed and passes them back to a central pool of materials when done

It is interesting to look at other systems in nature, because software is not, after all, unique. Software builds on hardware, which builds on physical properties of the world. The theories are built upon abstract mathematical models, on chemestry, on biology, on architecture, on art.. Computer scientists draw inspiration for new concepts, theories, ideas and designs from a vast array of other fields. Software is not unique at all. So, lets look at nature, at biological organisms.

Biology is interesting to us, because it works in exactly the same way as computer hardware, but instead of electrical processes, they rely on biological and chemical processes. They have the interesting property that they are, essentially, dataflow systems (just like computer hardware, under the hood). They are spatially sequenced (instead of temporally), just like dataflow systems (and spreadsheets, pixel shaders etc) and they are structured in flat hierarchies. Do you see the buzzwords? Flat hierarchies, spatial software, dataflow. Exactly what I’ve been ranting about here, on this blog, all along.

Lets look at a concrete example:

Cell based organisms. A cell is, essentially, a biological compute node. They are fed data, through the exchange of proteins, hormones and electrical and chemical signals, do something with this data and feed it on. Input, Process, Output. They are spatially configured, in multiple dimensions, in a flat hierarchy. That is, if you take out a cell, the entire system does not collapse. Sure, it is now missing a compute node and it won’t be able to work quite the same as before, but the system is still intact. The cells are not temporally sequential structures; they are spatial – their sequence depends on their spatial position in relation to each other.

Neural networks (or, to avoid confusion, networks of neurons) are also spatially configured compute nodes, leveraging a flat hierarchical architecture, composed of a dataflow network. The links are the synapses, which feed data through a dataflow network of neurons. The neurons are positioned spatially and the synapses depend upon their spatial position in relation to one another. The overall hierarchy is a flat one – if you remove a subsection of the network, the rest of the network does not collapse. Most biological systems appear to have these properties.

So back to dataflow programming. Our dataflow system will also feed data from node to node, in a spatially configured flat hierarchy. They are no different from biological systems. Can we infer anything from this?

Of course we can. Biological systems have other interesting properties, which we would love to achieve in software. Since our dataflow system exhibits the other properties of biological systems, logically we could infer that it may also exhibit the other desirable properties. At the risk of falling into the correlation = causation trap (that is, just because our system has similar properties to biological systems, does not mean ours will have all properties present in biological systems), we can see the potential for other desirable properties: fault tolerance, tolerance of incomplete data, redundancy, replaceable parts (that is, hot-swappable components – thanks to the flat hierarchy).

I hope you can see the huge potential a dataflow system provides.

I guess you can describe the programming system, which I am trying to describe here in this blog, as a multi-dimensional spatially configured feed forward nodal compute network. Now that’s a buzzword loaded sentence!

July 14, 2009

Building a Flat Hierarchy (Part 1)

Filed under: Software — dublindan @ 11:47 am
Tags: , , ,

To understand this post, it may be necessary to read about the differences between stacked and flat software hierarchies. Yes, its a long piece of required reading, but its worth it. I’ll wait for you here.

Still here? In this post, I am not going to talk about what stacked or flat hierarchies are. I’m not going to talk about their advantages or disadvantages. Instead, I will talk about a little software architecture I learned about from the game development community known as Entity Systems. I will also show a simple sample implementation of such an entity system – a flat hierarchy. My implementation will be crude and simple and not really a good representative of flat hierarchies, component systems or entity systems, but I’m hoping that it will be enough to give you a practical taster of how one can make use of these concepts. My hope is that others will expand on these ideas to build some useful software tools for the programmer community to make use of. So, put simply, please don’t complain that this implementation is inefficient and useless, instead use the concepts to build an efficient and useful variant. In a future post, I may develop such a system, but for now, a simple proof-of-concept implementation is more suitable, as it allows us to talk and reason about the concepts. Anyway, here goes…

My definitions and terminology may be slightly different from those used in the game development communities and if anyone spots such discrepancies, please point them out so that I can fix it. The concepts are simple enough – instead of of having our software composed as objects in the object oriented manner, we have entities. This is more than just a simple name change, however, as an Entity is really just a name or a tag. It does not contain data, it does not contain actions. Its just a named placeholder so that our system knows that it exists and can find it. An entity may be tagged with zero or more traits. A trait provides two things to an entity: a system and a state. The system is a function of code which continuously processes all entities with its particular trait. For example, a Renderable trait may exist, which tells the software that an entity can be rendered on the screen, that traits system will continuosly process all Renderable entities and render them to the screen. By tagging an entity as Renderable, it instantly becomes seen and processed by the system and if the tag is removed, it will no longer be processed by that system. A trait will also give an entity a state, which is the collection of all data required for that traits system to process it. In the case of a Renderable trait, the state could contain important data such as the representation (image data, polygons, etc) and position on screen.

Time for a quick recap. Our entity based software will consist of the following components:

  • Entity – an identifier which may have zero or more traits attached to it.
  • Trait – a behaviour or characteristic applied to an entity.
  • System – a processing system which updates or otherwise processes the states for a given trait.
  • State – the data asscociated with a trait.

In the next post (or perhaps I’ll edit this one instead), I’ll introduce a simple prototype implementation of these ideas, so that people can play around with it and see what they can do. This should help spark new ideas. Hopefully in the future, I will then implement a proper and efficient version which can be used to build real softweare, though for now a prototype should be good enough.

Theme: Rubric. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.