// bsp10.cpp : Threads mit critical Section
// Berechnung greift auf ein oder mehrere Variable zu
// Synchronisation mittels pragma omp critical
// zwei Varianten

#include "stdafx.h"
#include <omp.h>

#include <windows.h>  // LARGE_INTEGER

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


int _tmain(int argc, _TCHAR* argv[])
{
	const int MAX = 201;  // 10

	double M[MAX][MAX], x[MAX], y[MAX], total;
	int i, j;
    LARGE_INTEGER t1,t2;
	long z;

	for (i=0; i < MAX; i++) {
		for (j=0; j < MAX; j++)
			M[i][j]=(double) ((i+j)*0.1);
	}
	for (i=0; i < MAX; i++) {
		x[i] = (i % 3);
	}

	LARGE_INTEGER Frequency;
	if ( ! QueryPerformanceFrequency( &Frequency ) )
	{
		Frequency.HighPart=0;
		Frequency.LowPart=0;
	}
	printf("Frequency: %u %u\n\n", Frequency.HighPart, Frequency.LowPart );

	// -------------------------------------------------------------------------
	// jeweils 10 Threads, zwei Schleifen

	t1 = GetTickCount2();
	total = 0;
	for (i=0; i < MAX; i++) {
		y[i] = 0;
	}
	// 1. Variante, ohne Synchronisation
	#pragma omp parallel for private(i,j) shared(M,x,y,total) num_threads(10)
	
			for (i=0; i < MAX; i++) {				
				for (j=0; j < MAX; j++)
				{
					// y[i] += (M[i][j] * x[i]);					
					total += (M[i][j] * x[i]);
				}
				// total = total + y[i];
			}
	
	t2 = GetTickCount2();
	z=  (t2.HighPart-t1.HighPart)  +  (t2.LowPart-t1.LowPart);
	printf("1. Gesamtsumme y = %f time: %u \n\n",total, z);
	

	// 2. Variante, mit Synchronisation
	t1 = GetTickCount2();
	total = 0;
	for (i=0; i < MAX; i++) {
		y[i] = 0;
	}
	#pragma omp parallel for private(i,j) shared(M,x,y,total) num_threads(10)
		for (i=0; i < MAX; i++) {
			for (j=0; j < MAX; j++)
					y[i] += (M[i][j] * x[i]);
				#pragma omp critical
				{
					total = total + y[i];					
				}
			}
	
	t2 = GetTickCount2();
	z=  (t2.HighPart-t1.HighPart)  +  (t2.LowPart-t1.LowPart);
	printf("2. Gesamtsumme (crit): y = %f  time: %u \n\n",total,z);


	t1 = GetTickCount2();
	total = 0;
	for (i=0; i < MAX; i++) {
		y[i] = 0;
	}
	#pragma omp parallel for private(i,j) shared(M,x,y,total)  num_threads(10)
			for (i=0; i < MAX; i++) {
				for (j=0; j < MAX; j++)
					y[i] += (M[i][j] * x[i]);
				#pragma omp atomic
					total += y[i];  // muss bei atomic += sein !!
			}

	t2 = GetTickCount2();
	z=  (t2.HighPart-t1.HighPart)  +  (t2.LowPart-t1.LowPart);
	printf("3. Gesamtsumme (atomic): y = %f  time: %u \n\n",total,z);
	
	t1 = GetTickCount2();
	total = 0;
	for (i=0; i < MAX; i++) {
		y[i] = 0;
	}
	#pragma omp parallel for private(i,j) shared(M,x,y,total)  num_threads(10) 
		for (i=0; i < MAX; i++) {
			for (j=0; j < MAX; j++)
				y[i] += (M[i][j] * x[i]);
			#pragma omp atomic
				total += y[i];  // muss bei atomic += sein !!
		}

	t2 = GetTickCount2();
	z=  (t2.HighPart-t1.HighPart)  +  (t2.LowPart-t1.LowPart);
	printf("4. Gesamtsumme (2. atomic): y = %f  time: %u \n\n",total,z);




	t1 = GetTickCount2();
	total = 0;
	for (i=0; i < MAX; i++) {
		y[i] = 0;
	}
	#pragma omp parallel for private(i,j) shared(M,x,y,total)  num_threads(10) 
		for (i=0; i < MAX; i++) {
			for (j=0; j < MAX; j++)
				y[i] += (M[i][j] * x[i]);
			#pragma omp atomic
				total += y[i];  // muss bei atomic += sein !!
		}

	t2 = GetTickCount2();
	z=  (t2.HighPart-t1.HighPart)  +  (t2.LowPart-t1.LowPart);
	printf("5. Gesamtsumme (3. atomic): y = %f  time: %u \n\n",total,z);


	char ch[200];
	gets(ch);

    return 0;
}
