
#include <fstream>
#include <stdlib>
#include <thread>

#include <unistd.h>					// sysconf
#include <sys/times.h>					// times
#include <time.h>

inline unsigned long long int Time() {
    timespec ts;
    clock_gettime(
#if defined( __linux__ )
	 CLOCK_THREAD_CPUTIME_ID,
#elif defined( __freebsd__ )
	 CLOCK_PROF,
#elif defined( __solaris__ )
	 CLOCK_HIGHRES,
#else
    #error uC++ : internal error, unsupported architecture
#endif
	 &ts );
    return 1000000000LL * ts.tv_sec + ts.tv_nsec;
} // Time

//=======================================
// time struct
//=======================================

// prevent dead-code removal
struct StructDummy {
	volatile int i;
};

void ?{}(StructDummy * this) __attribute__(( noinline )) {
	this->i = 2;
}

int get_i(StructDummy * this) {
	return this->i;
}

forall( dtype T | { int get_i(T*); } )
int bidirectional( StructDummy * this ) __attribute__(( noinline )) {
	return get_i( this );
}

void BlockStructCreateDelete( int N ) {
    long long int StartTime, EndTime;

    StartTime = Time();
    for ( int i = 0; i < N; i += 1 ) {
	StructDummy dummy;
    }
    EndTime = Time();
    sout | "\t " | ( EndTime - StartTime ) / N;
}

void DynamicStructCreateDelete( int N ) {
    long long int StartTime, EndTime;

    StartTime = Time();
    for ( int i = 0; i < N; i += 1 ) {
	StructDummy *dummy = new();
	delete(dummy);
    }
    EndTime = Time();
    sout | "\t " | ( EndTime - StartTime ) / N;
}

void StructBidirectional( int N ) {
    long long int StartTime, EndTime;
    StructDummy dummy;
    volatile int rv __attribute__(( unused ));

    StartTime = Time();
    for ( int i = 0; i < N; i += 1 ) {
	rv = bidirectional( &dummy ); 
    }
    EndTime = Time();
    sout | "\t " | ( EndTime - StartTime ) / N;
}

//=======================================
// time coroutine
//=======================================

struct CoroutineDummy { coroutine_desc __cor; };
DECL_COROUTINE(CoroutineDummy);
void main(CoroutineDummy * this) {}

void ?{}(CoroutineDummy * this) {
	prime(this);
}

void BlockCoroutineCreateDelete( int N ) {
    long long int StartTime, EndTime;

    StartTime = Time();
    for ( int i = 0; i < N; i += 1 ) {
	CoroutineDummy dummy;
    }
    EndTime = Time();
    sout | "\t " | ( EndTime - StartTime ) / N;
}

void DynamicCoroutineCreateDelete( int N ) {
    long long int StartTime, EndTime;

    StartTime = Time();
    for ( int i = 0; i < N; i += 1 ) {
	CoroutineDummy * dummy = new();
	delete(dummy);
    }
    EndTime = Time();
    sout | "\t " | ( EndTime - StartTime ) / N;
}

struct CoroutineResume {
    int N;
    coroutine_desc __cor;
};

DECL_COROUTINE(CoroutineResume);

void ?{}(CoroutineResume* this, int N) {
      this->N = N;
	prime(this);
}

void main(CoroutineResume* this) {
	for ( int i = 1; i <= this->N; i += 1 ) {
		suspend();
	}
}

void resumer(CoroutineResume* this) {
	long long int StartTime, EndTime;

	StartTime = Time();
	for ( int i = 1; i <= this->N; i += 1 ) {
		resume(this);
	}
	EndTime = Time();
	sout | "\t " | ( EndTime - StartTime ) / this->N;
}

//=======================================
// time task
//=======================================

struct ThreadDummy { thread_desc __thrd; };
DECL_THREAD(ThreadDummy);
void main(ThreadDummy * this) {}

void BlockTaskCreateDelete( int N ) {
    long long int StartTime, EndTime;

    StartTime = Time();
    for ( int i = 0; i < N; i += 1 ) {
	scoped(ThreadDummy) dummy;
    }
    EndTime = Time();
    sout | "\t " | ( EndTime - StartTime ) / N;
}

void DynamicTaskCreateDelete( int N ) {
    long long int StartTime, EndTime;

    StartTime = Time();
    for ( int i = 0; i < N; i += 1 ) {
	scoped(ThreadDummy) * dummy = new();
	delete(dummy);
    }
    EndTime = Time();
    sout | "\t " | ( EndTime - StartTime ) / N;
}

struct ContextSwitch {
    int N;
    long long result;
    thread_desc __thrd;
};

DECL_THREAD(ContextSwitch);

void main(ContextSwitch * this) {    
	long long int StartTime, EndTime;

	StartTime = Time();
	for ( int i = 1; i <= this->N; i += 1 ) {
	    yield();
	} // for
	EndTime = Time();
	this->result = ( EndTime - StartTime ) / this->N;
}

void ?{}(ContextSwitch * this, int N) {
	this->N = N;
}

void ^?{}(ContextSwitch * this) {
	sout | "\t " | this->result;
}

//=======================================
// benchmark driver
//=======================================


int main() {
	const int NoOfTimes =
#if defined( __U_DEBUG__ )				// takes longer so run fewer iterations
	100000;
#else
	1000000;
#endif // __U_DEBUG__

	sout | "\t\tcreate\tcreate\tcall/" | endl;
	sout | "(nsecs)";
	sout | "\t\tdelete/\tdelete/\tctx" | endl;
	sout | "\t\tblock\tdynamic\tcycle" | endl;

	sout | "object\t";
	BlockStructCreateDelete( NoOfTimes );
	DynamicStructCreateDelete( NoOfTimes );
	StructBidirectional( NoOfTimes );
	sout | endl;

	sout | "coroutine";
	BlockCoroutineCreateDelete( NoOfTimes );
	DynamicCoroutineCreateDelete( NoOfTimes );
	{
		CoroutineResume resumer = { NoOfTimes };
		resumer(&resumer);
	}
	sout | endl;

	sout | "task\t";
	BlockTaskCreateDelete( NoOfTimes );
	DynamicTaskCreateDelete( NoOfTimes );
	{
		scoped(ContextSwitch) dummy = { (int)NoOfTimes };		// context switch
	}
	sout | "\t" | endl;
}