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

#include "bench.h"

//=======================================
// 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
//=======================================

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;
}

coroutine CoroutineResume {
    int N;
};

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
//=======================================

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;
}

thread ContextSwitch {
    int N;
    long long result;
};

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 );
	{
		ContextSwitch dummy = { (int)NoOfTimes };		// context switch
	}
	sout | "\t" | endl;
}