1
0
Fork 0
This repository has been archived on 2021-11-03. You can view files and clone it, but cannot push or open issues or pull requests.
uni-m.cds-adv-prog/ex0.1/spectral-norm/spectral-norm.cc

137 lines
3.5 KiB
C++

// The Computer Language Benchmarks Game
// https://salsa.debian.org/benchmarksgame-team/benchmarksgame/
//
// Original C contributed by Sebastien Loisel
// Conversion to C++ by Jon Harrop
// OpenMP parallelize by The Anh Tran
// Add SSE by The Anh Tran
// Additional SSE optimization by Krzysztof Jakubowski
// g++ -pipe -O3 -march=native -fopenmp -mfpmath=sse -msse2 \
// ./spec.c++ -o ./spec.run
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <sched.h>
#include <omp.h>
#include <emmintrin.h>
template <bool modei> int Index(int i, int j) {
return (((i + j) * (i + j + 1)) >> 1) + (modei? i : j) + 1;
}
template <bool modei>
void EvalPart(double *__restrict__ src, double *__restrict__ dst,
int begin, int end, int length) {
int i = begin;
for(; i + 1 < end; i += 2) {
__m128d sum = _mm_set_pd(
src[0] / double(Index<modei>(i + 1, 0)),
src[0] / double(Index<modei>(i + 0, 0)));
__m128d ti = modei?
_mm_set_pd(i + 1, i + 0) :
_mm_set_pd(i + 2, i + 1);
__m128d last = _mm_set_pd(
Index<modei>(i + 1, 0),
Index<modei>(i + 0, 0));
for(int j = 1; j < length; j++) {
__m128d idx = last + ti + _mm_set1_pd(j);
last = idx;
sum = sum + _mm_set1_pd(src[j]) / idx;
}
_mm_storeu_pd(dst + i + 0, sum);
}
for(; i < end; i++) {
double sum = 0;
for (int j = 0; j < length; j++)
sum += src[j] / double(Index<modei>(i, j));
dst[i] = sum;
}
}
void EvalATimesU(double *src, double *dst, int begin, int end, int N) {
EvalPart<1>(src, dst, begin, end, N);
}
void EvalAtTimesU(double *src, double *dst, int begin, int end, int N) {
EvalPart<0>(src, dst, begin, end, N);
}
void EvalAtATimesU(double *src, double *dst, double *tmp,
int begin, int end, int N) {
EvalATimesU (src, tmp, begin, end, N);
#pragma omp barrier
EvalAtTimesU(tmp, dst, begin, end, N);
#pragma omp barrier
}
int GetThreadCount() {
cpu_set_t cs;
CPU_ZERO(&cs);
sched_getaffinity(0, sizeof(cs), &cs);
int count = 0;
for (int i = 0; i < CPU_SETSIZE; ++i)
if (CPU_ISSET(i, &cs))
++count;
return count;
}
double spectral_game(int N) {
__attribute__((aligned(16))) double u[N];
__attribute__((aligned(16))) double v[N], tmp[N];
double vBv = 0.0;
double vv = 0.0;
#pragma omp parallel default(shared) num_threads(GetThreadCount())
{
// this block will be executed by NUM_THREADS
// variable declared in this block is private for each thread
int threadid = omp_get_thread_num();
int threadcount = omp_get_num_threads();
int chunk = N / threadcount;
// calculate each thread's working range [r1 .. r2) => static schedule
int begin = threadid * chunk;
int end = (threadid < (threadcount -1)) ? (begin + chunk) : N;
for(int i = begin; i < end; i++)
u[i] = 1.0;
#pragma omp barrier
for (int ite = 0; ite < 10; ++ite) {
EvalAtATimesU(u, v, tmp, begin, end, N);
EvalAtATimesU(v, u, tmp, begin, end, N);
}
double sumvb = 0.0, sumvv = 0.0;
for (int i = begin; i < end; i++) {
sumvv += v[i] * v[i];
sumvb += u[i] * v[i];
}
#pragma omp critical
{
vBv += sumvb;
vv += sumvv;
}
}
return sqrt(vBv / vv);
}
int main(int argc, char *argv[]) {
int N = ((argc >= 2) ? atoi(argv[1]) : 2000);
printf("%.9f\n", spectral_game(N));
return 0;
}