Here\'s an example of a program, where coroutines really help to simplify the algorithm - imho its hardly possible to implement otherwise. I also tried to choose a useful ta
Ok, here's what I actually asked about (see [1]) - the trick to statically call a function from child class. tl;dr is apparently a mighty power, so here's your readable standard coroutine fibonacci generator this time. There's a small difference though - we don't really need coroutines to generate these numbers, but its really hard (if possible) to make a faster implementation of my first program without coroutines.
#include
#include
#include
// without noinline some compilers tend to allocate the array before setjmp()
#ifdef __GNUC__
#define NOINLINE __attribute__((noinline))
#else
#define NOINLINE __declspec(noinline)
#endif
enum{ STKPAD=1<<16 };
struct coroutine {
volatile unsigned state;
jmp_buf PointA, PointB;
void yield( int value ) { if( setjmp(PointB)==0 ) { state=value; longjmp(PointA,value); } }
template NOINLINE void call_do_process() {
char stktmp[STKPAD]; state=ptrdiff_t(stktmp); ((T*)this)->do_process();
}
template unsigned coro_process( T* ) {
if( setjmp(PointA)==0 ) if( state ) longjmp(PointB,3); else call_do_process();
return state;
}
};
struct fibonacci : coroutine {
void do_process( void ) {
unsigned a=0,b=1;
while(1) {
yield( b );
b = b + a;
a = b - a;
}
}
unsigned get( void ) {
return coro_process(this);
}
} F;
int main( int argc, char** argv ) {
for( int i=0; i<20; i++ ) {
printf( "%i ", F.get() );
} printf( "\n" );
return 0;
}
And since Jerry Coffin's alternative version still fails to produce sensible results, here're some simpler stream benchmarks. Its a pity, as I'd expect it to be even slower with iterators.
In fact I've tested all kinds of approaches with arithmetic coders - plain getc/putc, virtual methods, plain functions pointers, iterator-like classes, and its clear that there's a large difference. For now, coroutines proved to be the best way for this - there's no complex logic encapsulated into byte i/o calls (unlike iterators), and the processing doesn't have to care about i/o details. Sure, there're even further optimizations, but I really only tried to demonstrate the benefits of coroutine approach here...
#define _CRT_SECURE_NO_DEPRECATE
#define _CRT_DISABLE_PERFCRIT_LOCKS
#include
#include
#include
int main( int argc, char** argv ) {
if( argc<3 ) return 1;
{
clock_t start = clock();
FILE* f = fopen( argv[1], "rb" ); if( f==0 ) return 2;
FILE* g = fopen( argv[2], "wb" ); if( g==0 ) return 3;
while(1) {
int c = getc(f);
if( c<0 ) break;
putc(c,g);
}
fclose(f);
fclose(g);
clock_t stop = clock();
printf( " File copy via stdio getc/putc - %7.3fs\n", float(stop-start)/CLOCKS_PER_SEC );
}
{
clock_t start = clock();
FILE* f = fopen( argv[1], "rb" ); if( f==0 ) return 2;
FILE* g = fopen( argv[2], "wb" ); if( g==0 ) return 3;
while(1) {
static char buf[1<<16];
int l = fread( buf, 1,sizeof(buf), f ); if( l<=0 ) break;
fwrite( buf, 1,l, g ); if( l
----- 100,000,000 byte file ----- [ GCC 4.5 ] File copy via stdio getc/putc - 0.546s File copy via stdio 64k fread/fwrite - 0.188s File copy via ifstream::get/ofstream::put - 10.578s [ IntelC 11.1 / VS 2005 ] File copy via stdio getc/putc - 0.500s File copy via stdio 64k fread/fwrite - 0.156s File copy via ifstream::get/ofstream::put - 14.656s [ MSC 14.0 / VS 2005 ] File copy via stdio getc/putc - 0.609s File copy via stdio 64k fread/fwrite - 0.156s File copy via ifstream::get/ofstream::put - 19.063s ----- 1,000,000,000 byte file ----- [ GCC 4.5 ] File copy via stdio getc/putc - 7.468s File copy via stdio 64k fread/fwrite - 1.828s File copy via ifstream::get/ofstream::put - 109.891s [ IntelC 11.1 / VS 2005 ] File copy via stdio getc/putc - 6.718s File copy via stdio 64k fread/fwrite - 1.672s File copy via ifstream::get/ofstream::put - 145.500s [ MSC 14.0 / VS 2005 ] File copy via stdio getc/putc - 6.453s File copy via stdio 64k fread/fwrite - 1.609s File copy via ifstream::get/ofstream::put - 191.031s