// testthreadWin32.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

// testthread.cpp : main project file.

#include <windows.h>

#include <stdlib.h>

#include <omp.h>

// 10 / 10000 10
// 1  20 2000 
// 1  20 200000

const int C_OUTERLOOP =100;
const int C_MIDDLELOOP =100;
const int C_INNERLOOP =200;  // 200000


int _NN;

struct arguments {
	int i1,i2;
	int innerLoopCount;
};

LARGE_INTEGER GetTickCount2() {
	HANDLE hCurrentProcess = GetCurrentProcess();
	DWORD dwProcessAffinity;
	DWORD dwSystemAffinity;
	LARGE_INTEGER result;

	GetProcessAffinityMask(hCurrentProcess,&dwProcessAffinity, &dwSystemAffinity);
	SetProcessAffinityMask(hCurrentProcess,1);

	QueryPerformanceCounter(&result);

	SetProcessAffinityMask(hCurrentProcess,dwProcessAffinity);

	return result;
}


unsigned long work (int loopCount) {
	unsigned long result=0;
	for (int i=0; i<loopCount; ++i) 
		result+=i;
	return result;
}

unsigned long testserialCode() {
	unsigned long s=0;
	for (int i=0; i < C_OUTERLOOP; ++i) {
		s=0;
		for (int j=0; j < C_MIDDLELOOP; ++j) {
			s+=work(_NN);
		}
	}
	return s;
}

unsigned long workAllLoop () {
	unsigned long s=0;
	for (int i=0; i < C_OUTERLOOP; ++i) {
		for (int j=0; j < C_MIDDLELOOP; ++j) {
			s=0;
			for (int k=0; k<_NN; ++k) 
				s+=k;
		}
	}
	return s;
}


void testSectionCode(int chunksize) {
	omp_set_num_threads(2);
	for (int i=0; i < C_OUTERLOOP; ++i) {
		#pragma omp parallel sections 
		{
			#pragma omp section
			{
				for (int j=0; j < chunksize; ++j) {
					work(_NN);
				}
			}
		
			#pragma omp section
			{
				for (int j=chunksize; j < C_MIDDLELOOP; ++j) {
					work(_NN);
				}
			}
		}
	}
}



void testParallelFor1Code(){
	#pragma omp parallel for
	for (int i=0; i < C_OUTERLOOP; ++i) {
			for (int j=0; j < C_MIDDLELOOP; ++j) {
				work(_NN);
			}
	}
} // testParallelFor1Code


void testParallelFor2Code(){
	for (int i=0; i < C_OUTERLOOP; ++i) {
		#pragma omp parallel for
			for (int j=0; j < C_MIDDLELOOP; ++j) {
				work(_NN);
			}

	}
} // testParallelFor2Code


void testParallelFor3Code(){
	#pragma omp parallel for
	for (int i=0; i < C_OUTERLOOP; ++i) {
		#pragma omp parallel for
			for (int j=0; j < C_MIDDLELOOP; ++j) {
				work(_NN);
			}

	}
} // testParallelFor3Code


DWORD WINAPI LoopThreadFunction(LPVOID arguments) {
	struct arguments *args;
	args = (struct arguments *) arguments;
	int innerLoopCount=args->innerLoopCount;
	for (int i=args->i1; i < args->i2; ++i) {
			work(C_INNERLOOP);
	}
	return 0;
}



void testWin32Code_Version1(int maxworkers, int chunksize){
	struct arguments *args;
	HANDLE *hThreadVector;
	
	DWORD CreationFlags =  CREATE_SUSPENDED;

	int stacksize = 10000;

	hThreadVector = new HANDLE[maxworkers];
	args = new arguments[maxworkers];

	
	//int chunksize=C_MIDDLELOOP/maxworkers;
	int start = 0;
	int ende = chunksize;
	for (int j=0; j < maxworkers; ++j) {
		args[j].i1 = start;
		args[j].i2 = ende;
		args[j].innerLoopCount = C_INNERLOOP;
		start +=chunksize;
		ende +=chunksize;
		if (ende>C_MIDDLELOOP) ende = C_MIDDLELOOP;
	}

	CreationFlags =  0;
	for (int i=0; i < C_OUTERLOOP; ++i) {
		for (int j=0; j < maxworkers; ++j) {
			hThreadVector[j] = CreateThread (NULL, stacksize, 
				   (LPTHREAD_START_ROUTINE) LoopThreadFunction,
						(LPDWORD ) &args[j], CreationFlags, 
						(LPDWORD) NULL );
		}
		WaitForMultipleObjects(maxworkers,hThreadVector, TRUE, INFINITE);
		
	} // for outerloop
	//for (int j=0; j < maxworkers; ++j) {
	//	CloseHandle(hThreadVector[j]);
	//}
	delete [] hThreadVector;
	delete [] args;
} // win32 Threads Version1




void testWin32Code_Version2(int maxworkers, int chunksize){
	struct arguments *args;
	HANDLE *hThreadVector;
	
	DWORD CreationFlags =  CREATE_SUSPENDED;

	int stacksize = 10000;

	hThreadVector = new HANDLE[maxworkers];
	args = new arguments[maxworkers];

	
	// int chunksize=C_MIDDLELOOP/maxworkers;
	int start = 0;
	int ende = chunksize;
	for (int j=0; j < maxworkers; ++j) {
		args[j].i1 = start;
		args[j].i2 = ende;
		args[j].innerLoopCount = C_INNERLOOP;
		start +=chunksize;
		ende +=chunksize;
		if (ende>C_MIDDLELOOP) ende = C_MIDDLELOOP;
	}

	CreationFlags =  CREATE_SUSPENDED;
	for (int j=0; j < maxworkers; ++j) {
		hThreadVector[j] = CreateThread (NULL, stacksize, 
			   (LPTHREAD_START_ROUTINE) LoopThreadFunction,
					(LPDWORD ) &args[j], CreationFlags, 
						(LPDWORD) NULL );
	}
	for (int i=0; i < C_OUTERLOOP; ++i) {
		for (int j=0; j < maxworkers; ++j) {
			ResumeThread( hThreadVector[j] );
		}		
		WaitForMultipleObjects(maxworkers,hThreadVector, TRUE, INFINITE);
	} // for outerloop
	delete [] hThreadVector;
	delete [] args;
} // win32 Threads Version2





int _tmain(int argc, char* argv[])  {
	const int MAX=8;
	LARGE_INTEGER t1, t2;
	unsigned long time[MAX];
	unsigned long s1, s2;
	int chunksize=0;
	int anz=20;
	int numThreads = omp_get_num_threads();
	int maxworkers_array[] = {1,2,4,6,8,10,12,14,16,18,20, 30, 40};
	int maxworkers;


	// 13
	for (int i=1; i<13; i++) {
		maxworkers = maxworkers_array[i];
		omp_set_num_threads(maxworkers);  // ???
		printf("\n\nAnzThreads: %d\n ", maxworkers );
	
		chunksize=C_MIDDLELOOP/maxworkers;
		for (int j=0; j<MAX;j++) time[j]=0;

		for (int j=0; j<anz; j++) {
			t1 = GetTickCount2();
				s1=testserialCode();
				t2 = GetTickCount2();
				time[0]+=t2.LowPart-t1.LowPart;
			t1 = GetTickCount2();
				testSectionCode(chunksize);	
				t2 = GetTickCount2();
				time[1]+=t2.LowPart-t1.LowPart;

			omp_set_num_threads(maxworkers);  // wird in Section neu gesetzt
			t1 = GetTickCount2();
				testParallelFor1Code();
				t2 = GetTickCount2();
				time[2]+=t2.LowPart-t1.LowPart;
			t1 = GetTickCount2();
			testParallelFor2Code();
				t2 = GetTickCount2();
				time[3]+=t2.LowPart-t1.LowPart;
			t1 = GetTickCount2();
			testParallelFor3Code();
				t2 = GetTickCount2();
				time[4]+=t2.LowPart-t1.LowPart;

			t1 = GetTickCount2();
				testWin32Code_Version1(maxworkers, chunksize);
				t2 = GetTickCount2();
				time[5]+=t2.LowPart-t1.LowPart;

			t1 = GetTickCount2();
				testWin32Code_Version2(maxworkers, chunksize);
				t2 = GetTickCount2();
				time[6]+=t2.LowPart-t1.LowPart;

			t1 = GetTickCount2();
				s2=workAllLoop();
				t2 = GetTickCount2();
				time[7]+=t2.LowPart-t1.LowPart;
		}
		for (int j=0; j<MAX;j++) time[j]/=anz;

		printf("\nSchleife Seriell mit sub time: %9u  summe %u\n", time[0], s1 );
		printf("Schleife workAllLoop     time: %9u  summe %u\n", time[7], s2 );
		printf("Schleife 2+Sectionen     time: %9u\n", time[1] );
		printf("Schleife ParallelFor1    time: %9u\n", time[2] );
		printf("Schleife ParallelFor2    time: %9u\n", time[3] );
		printf("Schleife ParallelFor3    time: %9u\n", time[4] );
		printf("Schleife Win32 V1        time: %9u\n", time[5] );
		printf("Schleife Win32 V2        time: %9u\n", time[6] );
		
		printf("\n\n%u\n", time[0] );
		printf("%u\n", time[1] );
		printf("%u\n", time[2] );
		printf("%u\n", time[3] );
		printf("%u\n", time[4] );
		printf("%u\n", time[5] );
		printf("%u\n", time[6] );
		printf("%u\n", time[7] );
	}
	

		//testParallelForCode(1);
		//testParallelForCode(2);
	

	char  ch[200];
	gets_s(ch);	

    return 0;
}
/*
innerloop 200000  middlelopp 20
1:
Schleife Seriell        time: 57272
Schleife 2+Sectionen    time: 102996
Schleife Parallel-For1: time: 58654
Schleife Parallel-For2: time: 90067
Schleife Win32-Threads  time: 51337

2:
Schleife Seriell        time: 114563
Schleife 2+Sectionen    time: 149818
Schleife Parallel-For1: time: 117851
Schleife Parallel-For2: time: 67720

4:
Schleife Seriell        time: 230249
Schleife 2+Sectionen    time: 248544
Schleife Parallel-For1: time: 232300
Schleife Parallel-For2: time: 119762

6:
Schleife Seriell        time: 344976
Schleife 2+Sectionen    time: 336107
Schleife Parallel-For1: time: 349326
Schleife Parallel-For2: time: 259663

8:
Schleife Seriell        time: 459666
Schleife 2+Sectionen    time: 397189
Schleife Parallel-For1: time: 468285
Schleife Parallel-For2: time: 260394

10:
Schleife Seriell        time: 571738
Schleife 2+Sectionen    time: 534234
Schleife Parallel-For1: time: 586873
Schleife Parallel-For2: time: 314387

Schleife Seriell        time: 688168
Schleife 2+Sectionen    time: 588028
Schleife Parallel-For1: time: 695239
Schleife Parallel-For2: time: 398474

*/


/*  innerloop 2000 middleloop 20
1:
Schleife Seriell        time: 940
Schleife 2+Sectionen    time: 75174
Schleife Parallel-For1: time: 1769
Schleife Parallel-For2: time: 29888
2:
Schleife Seriell        time: 1498
Schleife 2+Sectionen    time: 5516
Schleife Parallel-For1: time: 2440
Schleife Parallel-For2: time: 4330
4:
Schleife Seriell        time: 2340
Schleife 2+Sectionen    time: 6922
Schleife Parallel-For1: time: 4156
Schleife Parallel-For2: time: 6062
6:
Schleife Seriell        time: 3597
Schleife 2+Sectionen    time: 56465
Schleife Parallel-For1: time: 6132
Schleife Parallel-For2: time: 7567
8:
Schleife Seriell        time: 4886
Schleife 2+Sectionen    time: 60276
Schleife Parallel-For1: time: 7877
Schleife Parallel-For2: time: 45536
10:
Schleife Seriell        time: 6802
Schleife 2+Sectionen    time: 13785
Schleife Parallel-For1: time: 9754
Schleife Parallel-For2: time: 35600

12:
Schleife Seriell        time: 7562
Schleife 2+Sectionen    time: 16202
Schleife Parallel-For1: time: 12395
Schleife Parallel-For2: time: 13196


*/

/*  innerloop 200000 middleloop 20
1:
Schleife Seriell        time: 56978
Schleife 2+Sectionen    time: 55264
Schleife Parallel-For1: time: 57371
Schleife Parallel-For2: time: 57653

2:
Schleife Seriell        time: 115910
Schleife 2+Sectionen    time: 105985
Schleife Parallel-For1: time: 114573
Schleife Parallel-For2: time: 115729

10:
Schleife Seriell        time: 574701
Schleife 2+Sectionen    time: 502565
Schleife Parallel-For1: time: 597717
Schleife Parallel-For2: time: 581715

100:
Schleife Seriell        time: 5747823
Schleife 2+Sectionen    time: 5284290
Schleife Parallel-For1: time: 5731248
Schleife Parallel-For2: time: 5726558







*/


