1/*
2 Copyright (c) 2005-2019 Intel Corporation
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
17#include "tbb/parallel_while.h"
18#include "harness.h"
19
20const int N = 200;
21
22typedef int Element;
23
24//! Representation of an array index with only those signatures required by parallel_while.
25class MinimalArgumentType {
26 void operator=( const MinimalArgumentType& );
27 long my_value;
28 enum {
29 DEAD=0xDEAD,
30 LIVE=0x2718,
31 INITIALIZED=0x3141
32 } my_state;
33public:
34 ~MinimalArgumentType() {
35 ASSERT( my_state==LIVE||my_state==INITIALIZED, NULL );
36 my_state = DEAD;
37 }
38 MinimalArgumentType() {
39 my_state = LIVE;
40 }
41 void set_value( long i ) {
42 ASSERT( my_state==LIVE||my_state==INITIALIZED, NULL );
43 my_value = i;
44 my_state = INITIALIZED;
45 }
46 long get_value() const {
47 ASSERT( my_state==INITIALIZED, NULL );
48 return my_value;
49 }
50};
51
52class IntegerStream {
53 long my_limit;
54 long my_index;
55public:
56 IntegerStream( long n ) : my_limit(n), my_index(0) {}
57 bool pop_if_present( MinimalArgumentType& v ) {
58 if( my_index>=my_limit )
59 return false;
60 v.set_value( my_index );
61 my_index+=2;
62 return true;
63 }
64};
65
66class MatrixMultiplyBody: NoAssign {
67 Element (*a)[N];
68 Element (*b)[N];
69 Element (*c)[N];
70 const int n;
71 tbb::parallel_while<MatrixMultiplyBody>& my_while;
72public:
73 typedef MinimalArgumentType argument_type;
74 void operator()( argument_type i_arg ) const {
75 long i = i_arg.get_value();
76 if( (i&1)==0 && i+1<N ) {
77 MinimalArgumentType value;
78 value.set_value(i+1);
79 my_while.add( value );
80 }
81 for( int j=0; j<n; ++j )
82 c[i][j] = 0;
83 for( int k=0; k<n; ++k ) {
84 Element aik = a[i][k];
85 for( int j=0; j<n; ++j )
86 c[i][j] += aik*b[k][j];
87 }
88 }
89 MatrixMultiplyBody( tbb::parallel_while<MatrixMultiplyBody>& w, Element c_[N][N], Element a_[N][N], Element b_[N][N], int n_ ) :
90 a(a_), b(b_), c(c_), n(n_), my_while(w)
91 {}
92};
93
94void WhileMatrixMultiply( Element c[N][N], Element a[N][N], Element b[N][N], int n ) {
95 IntegerStream stream( N );
96 tbb::parallel_while<MatrixMultiplyBody> w;
97 MatrixMultiplyBody body(w,c,a,b,n);
98 w.run( stream, body );
99}
100
101#include "tbb/tick_count.h"
102#include <cstdlib>
103#include <cstdio>
104using namespace std;
105
106static long Iterations = 5;
107
108static void SerialMatrixMultiply( Element c[N][N], Element a[N][N], Element b[N][N], int n ) {
109 for( int i=0; i<n; ++i ) {
110 for( int j=0; j<n; ++j )
111 c[i][j] = 0;
112 for( int k=0; k<n; ++k ) {
113 Element aik = a[i][k];
114 for( int j=0; j<n; ++j )
115 c[i][j] += aik*b[k][j];
116 }
117 }
118}
119
120static void InitializeMatrix( Element x[N][N], int n, int salt ) {
121 for( int i=0; i<n; ++i )
122 for( int j=0; j<n; ++j )
123 x[i][j] = (i*n+j)^salt;
124}
125
126static Element A[N][N], B[N][N], C[N][N], D[N][N];
127
128static void Run( int nthread, int n ) {
129 /* Initialize matrices */
130 InitializeMatrix(A,n,5);
131 InitializeMatrix(B,n,10);
132 InitializeMatrix(C,n,0);
133 InitializeMatrix(D,n,15);
134
135 tbb::tick_count t0 = tbb::tick_count::now();
136 for( long i=0; i<Iterations; ++i ) {
137 WhileMatrixMultiply( C, A, B, n );
138 }
139 tbb::tick_count t1 = tbb::tick_count::now();
140 SerialMatrixMultiply( D, A, B, n );
141
142 // Check result
143 for( int i=0; i<n; ++i )
144 for( int j=0; j<n; ++j )
145 ASSERT( C[i][j]==D[i][j], NULL );
146 REMARK("time=%g\tnthread=%d\tn=%d\n",(t1-t0).seconds(),nthread,n);
147}
148
149#include "tbb/task_scheduler_init.h"
150#include "harness_cpu.h"
151
152int TestMain () {
153 if( MinThread<1 ) {
154 REPORT("number of threads must be positive\n");
155 exit(1);
156 }
157 for( int p=MinThread; p<=MaxThread; ++p ) {
158 tbb::task_scheduler_init init( p );
159 for( int n=N/4; n<=N; n+=N/4 )
160 Run(p,n);
161
162 // Test that all workers sleep when no work
163 TestCPUUserTime(p);
164 }
165 return Harness::Done;
166}
167
168