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 | |
20 | const int N = 200; |
21 | |
22 | typedef int Element; |
23 | |
24 | //! Representation of an array index with only those signatures required by parallel_while. |
25 | class MinimalArgumentType { |
26 | void operator=( const MinimalArgumentType& ); |
27 | long my_value; |
28 | enum { |
29 | DEAD=0xDEAD, |
30 | LIVE=0x2718, |
31 | INITIALIZED=0x3141 |
32 | } my_state; |
33 | public: |
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 | |
52 | class IntegerStream { |
53 | long my_limit; |
54 | long my_index; |
55 | public: |
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 | |
66 | class 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; |
72 | public: |
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 | |
94 | void 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> |
104 | using namespace std; |
105 | |
106 | static long Iterations = 5; |
107 | |
108 | static 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 | |
120 | static 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 | |
126 | static Element A[N][N], B[N][N], C[N][N], D[N][N]; |
127 | |
128 | static 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 | |
152 | int 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 | |