self() → self trap_exit(...) → self->trap_exit(...) last_received() → self->last_dequeued() link(other) → self->link_to(other) quit(reason) → self->quit(reason)
Sunday, January 22, 2012
Minor API Changes
I've removed some function from the cppa namespace in the latest version. If your code fails to compile after git pull, follow this instructions:
Wednesday, January 18, 2012
Opt for option!
In a perfect world, each function could safely assume all arguments are valid. But this is the real world and things go wrong all the time. Especially whenever a function needs to parse a user-defined input. A good example for such a function is X to_int(string const& str). What is the correct type of X?
Well, some might argue "int of course and the function should throw an exception on error!". I would not recommend it. Throwing an exception is really the very last thing you should do. An exception is a gigantic hammer that smashes your stack to smithereens. If your user didn't read the documentation and uses your function without a try block, the exception will kill the whole program. Furthermore, exceptions are slow. All major compilers are optimized to have few overhead for entering a try block. Throwing an exception, stack unwinding and catching are expensive. You should not throw an exception unless there is nothing else you can do.
Use a bool pointer. Some libraries, e.g., the Qt library, use a bool pointer as function argument. Honestly, I don't like this approach. It forces you do declare additional variables and always returns an object, even if the function shouldn't. It's ok for integers, but what if your function returns a vector or string? Creating empty objects is a waste of time.
Return a pair. Some STL functions return a pair with a boolean and the result. The boolean indicates whether the function was successful. Again, creating empty objects is a waste of time. Thus, this approach isn't very efficient. But that's not the real issue here:
Return a pointer. Safety issues aside, you should use the stack for doing work and the heap for dynamically growing containers. Your stack is your friend. It is fast, automatically destroys variables as they go out of scope, and did I mention fast? Allocating small objects on the heap has a significant performance impact.
Return an option. If you're familiar with Haskell or Scala, you'll know Maybe or Option. In short, a function doesn't return a value. It returns maybe a value. If your string actually is an integer, the function returns an integer. Otherwise, it returns nothing. libcppa does have an option class. I guess you'll know what this code is supposed to do:
This are some performance results for cppa::option compared to returning an int, returning a pair, and returning a boost::optional.
The boost implementation clearly falls short. This is because the boost implementation doesn't use the stack. That's not because the boost developers don't know how to write efficient code. It's because unrestricted unions are a C++11 feature. cppa::option has a slight overhead compared to a pair for returning values but is even faster than a pair for returning empties, because the memory is uninitialized in this case. If you're already a user of libcppa: use option. If you're not (yet) a user: copy & paste the source code and use it. :)
Of course, it's C++11 only. Honestly, I really don't know why this isn't part of the STL. It should! It's general, fast, safe and really improves the readability of your source code.
Well, some might argue "int of course and the function should throw an exception on error!". I would not recommend it. Throwing an exception is really the very last thing you should do. An exception is a gigantic hammer that smashes your stack to smithereens. If your user didn't read the documentation and uses your function without a try block, the exception will kill the whole program. Furthermore, exceptions are slow. All major compilers are optimized to have few overhead for entering a try block. Throwing an exception, stack unwinding and catching are expensive. You should not throw an exception unless there is nothing else you can do.
Use a bool pointer. Some libraries, e.g., the Qt library, use a bool pointer as function argument. Honestly, I don't like this approach. It forces you do declare additional variables and always returns an object, even if the function shouldn't. It's ok for integers, but what if your function returns a vector or string? Creating empty objects is a waste of time.
Return a pair. Some STL functions return a pair with a boolean and the result. The boolean indicates whether the function was successful. Again, creating empty objects is a waste of time. Thus, this approach isn't very efficient. But that's not the real issue here:
auto x = to_int("try again");
if (x.first) do_something(x.second);
Is this code correct? The answer is "I don't know". Remember, you can assign a bool to an int and you can use integers in if-statements. You have to read the documentation of to_int to see if x.first is the bool or the int.Return a pointer. Safety issues aside, you should use the stack for doing work and the heap for dynamically growing containers. Your stack is your friend. It is fast, automatically destroys variables as they go out of scope, and did I mention fast? Allocating small objects on the heap has a significant performance impact.
Return an option. If you're familiar with Haskell or Scala, you'll know Maybe or Option. In short, a function doesn't return a value. It returns maybe a value. If your string actually is an integer, the function returns an integer. Otherwise, it returns nothing. libcppa does have an option class. I guess you'll know what this code is supposed to do:
auto x = to_int("try again");
if (x) do_something(*x);
So, what is the type of x here? It is "option<int>". You can write if (x.valid()) do_something(x.get()); instead if you prefer a more verbose style. Option supports default values: do_something(x.get_or_else(0));. If you're a user of the boost library, maybe you'll know boost::optional. In general, I would recommend boost to everyone, but boost::optional is slow. Just have a look at the implementation. cppa::option uses a union to store the value. If the option is empty, the object in the union won't get constructed. You'll have a slight overhead of returning "empty memory" but you don't pay for creating empty objects.This are some performance results for cppa::option compared to returning an int, returning a pair, and returning a boost::optional.
| return type | 100,000,000 values | 100,000,000 empties |
|---|---|---|
| int | 0.488s | - |
| std::pair<bool, int> | 2.418s | 2.304s |
| cppa::option<int> | 2.776s | 1.598s |
| boost::optional<int> | 7.419s | 2.987s |
Of course, it's C++11 only. Honestly, I really don't know why this isn't part of the STL. It should! It's general, fast, safe and really improves the readability of your source code.
Wednesday, December 7, 2011
Documentation
Finally! The section about message handling is done and the doxygen documentation is now online at github pages: http://neverlord.github.com/libcppa/.
Send me a mail or leave a comment if you miss something important.
Have fun!
Send me a mail or leave a comment if you miss something important.
Have fun!
Friday, November 11, 2011
Pattern Matching Changes
The new pattern matching implementation is now merged back into master. There are a lot changes under the hood to significantly speedup pattern matching. However, there is one change to the user interface as well: any_type no longer exists. There is a replacement though, but it's usage differs a little bit: anything.
This is a snipped using the old any_type syntax:
This is a snipped using the old any_type syntax:
on<int, any_type>() >> [](int v1) { }, // 1
on<int, any_type*>() >> [](int v1) { } // 2
The semantic of the first line - matching exactly one element of any type - is no longer supported. The second line just needs to replace any_type* with anything to work:
// equal to 2
on<int, anything>() >> [](int v1) { }
This will match any message with an integer as first element.
Wednesday, September 21, 2011
delayed_send
delayed_send and its brother delayed_reply are great if you need to poll a resource every x (milli)seconds while staying responsive to other messages or if you want to implement a "timed loop". Both accept std::chrono durations in seconds, milliseconds, microseconds and minutes. However, the accuracy depends on your scheduler, operating system, workload, etc., so you shouldn't rely on a microseconds-timing.
A good example for such a loop is an animation. In message oriented programming (and this is was libcppa is about), the most idiomatic way to implement such a loop is to send a message to yourself that is delayed by a predefined amount of time. If you receive that message, you do the next animation step and send another delayed message to yourself, and so on, until the animation is done.
Today's example is a terminal-animation with a dancing Kirby. Have fun!
A good example for such a loop is an animation. In message oriented programming (and this is was libcppa is about), the most idiomatic way to implement such a loop is to send a message to yourself that is delayed by a predefined amount of time. If you receive that message, you do the next animation step and send another delayed message to yourself, and so on, until the animation is done.
Today's example is a terminal-animation with a dancing Kirby. Have fun!
#include <chrono>
#include <iostream>
#include <algorithm>
#include "cppa/cppa.hpp"
using std::cout;
using std::endl;
using namespace cppa;
// ASCII art figures
constexpr const char* figures[] = {
"<(^.^<)",
"<(^.^)>",
"(>^.^)>"
};
// array of {figure, offset} pairs
constexpr size_t animation_steps[][2] = {
{1, 7}, {0, 7}, {0, 6}, {0, 5}, {1, 5}, {2, 5}, {2, 6},
{2, 7}, {2, 8}, {2, 9}, {2, 10}, {1, 10}, {0, 10}, {0, 9},
{1, 9}, {2, 10}, {2, 11}, {2, 12}, {2, 13}, {1, 13}, {0, 13},
{0, 12}, {0, 11}, {0, 10}, {0, 9}, {0, 8}, {0, 7}, {1, 7}
};
constexpr size_t animation_width = 20;
// "draws" an animation step: {offset_whitespaces}{figure}{padding}
void draw_kirby(size_t const (&animation)[2]) {
cout.width(animation_width);
cout << '\r';
std::fill_n(std::ostream_iterator<char>{cout}, animation[1], ' ');
cout << figures[animation[0]];
cout.fill(' ');
cout.flush();
}
void dancing_kirby() {
// let's get it started
send(self, atom("Step"));
// iterate over animation_steps
auto i = std::begin(animation_steps);
receive_for(i, std::end(animation_steps)) (
on<atom("Step")>() >> [&]() {
draw_kirby(*i);
// animate next step in 150ms
delayed_send(self, std::chrono::milliseconds(150), atom("Step"));
}
);
}
int main() {
cout << endl;
dancing_kirby();
cout << endl;
}
Tuesday, September 6, 2011
Timed Receive
It's not unusual to send a message and then wait for response. But if your communication partner doesn't reply, you'll wait forever. That's why we need an option to specify timeouts.
Here's a short example actor that does nothing but sends you back your own messages and exits if he idles for 10sec.
The after()-statement has to be the last one and you can't specify more than one. An empty receive with nothing but a timeout should be used to sleep for a certain amount of time, e.g.:
Btw: you should not use native sleep functions, because they are blocking. That's only ok if you spawned your Actor with the detached flag, because the Actor has its own thread in this case. But remember that your Actors usually share one thread pool and blocking function calls should be best avoided.
Here's a short example actor that does nothing but sends you back your own messages and exits if he idles for 10sec.
#include <chrono>
#include "cppa/cppa.hpp"
using namespace cppa;
void echo_server()
{
receive_loop (
others() >> []() {
self->last_sender() << self->last_dequeued();
},
after(std::chrono::seconds(10)) >> []() {
quit(exit_reason::user_defined);
}
);
}
The after()-statement has to be the last one and you can't specify more than one. An empty receive with nothing but a timeout should be used to sleep for a certain amount of time, e.g.:
receive(after(std::chrono::milliseconds(5)) >> []() {});
Btw: you should not use native sleep functions, because they are blocking. That's only ok if you spawned your Actor with the detached flag, because the Actor has its own thread in this case. But remember that your Actors usually share one thread pool and blocking function calls should be best avoided.
Wednesday, August 24, 2011
libcppa vs. Erlang vs. Scala Performance
I recently found a Scala Actor vs. Erlang Performance test on github.
The test benchmarks the speed of message handling and I decided to port it to libcppa. This is the full source code for a sequential-send-version (sending 3,000,000 messages):
System: 2.66 GHz Intel Core i7 (dual core); Mac OS 10.7.1
The source code can be found in the folder gen_server on github.
Note: This benchmark results are outdated. The current version of libcppa runs the gen_server benchmark in ~5.5 seconds.
Please see the mixed scenario post for a more detailed benchmark.
The test benchmarks the speed of message handling and I decided to port it to libcppa. This is the full source code for a sequential-send-version (sending 3,000,000 messages):
#include <iostream>
#include "cppa/cppa.hpp"
#include <boost/progress.hpp>
using std::cout;
using std::endl;
using boost::timer;
using namespace cppa;
void counter_actor()
{
long count = 0;
receive_loop
(
on<atom("Get")>() >> [&]()
{
reply(count);
count = 0;
},
on<atom("AddCount"), long>() >> [&](long val)
{
count += val;
}
);
}
long the_test(int msg_count)
{
constexpr long val = 100;
auto counter = spawn(counter_actor);
for (int i = 0; i < msg_count; ++i)
{
send(counter, atom("AddCount"), val);
}
send(counter, atom("Get"));
long result = 0;
receive
(
on<long>() >> [&](long value)
{
result = value;
}
);
send(counter, atom(":Exit"), exit_reason::user_defined);
return result;
}
void run_test(int msg_count)
{
timer t0;
long count = the_test(msg_count);
auto elapsed = t0.elapsed();
cout << "Count is " << count << endl
<< "Test took " << elapsed << " seconds" << endl
<< "Throughput = " << (msg_count / elapsed)
<< " per sec" << endl;
}
int main()
{
run_test(3000000);
await_all_others_done();
return 0;
}Results (average value of 5 runs)
| Language | Time(s) | Throughput (msg/s) |
|---|---|---|
| Erlang OTP | ~9 | ~333,333.333 |
| Erlang "bare receive" | ~7 | ~428,571.429 |
| Scala 2.9 | ~9 | ~333,333.333 |
| libcppa | ~12 | ~250,000 |
System: 2.66 GHz Intel Core i7 (dual core); Mac OS 10.7.1
The source code can be found in the folder gen_server on github.
Note: This benchmark results are outdated. The current version of libcppa runs the gen_server benchmark in ~5.5 seconds.
Please see the mixed scenario post for a more detailed benchmark.
Subscribe to:
Posts (Atom)