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 "concurrent_vector_v2.h" |
18 | #include <cstdio> |
19 | #include <cstdlib> |
20 | #include "../test/harness_assert.h" |
21 | |
22 | tbb::atomic<long> FooCount; |
23 | |
24 | //! Problem size |
25 | const size_t N = 500000; |
26 | |
27 | struct Foo { |
28 | int my_bar; |
29 | public: |
30 | enum State { |
31 | DefaultInitialized=0x1234, |
32 | CopyInitialized=0x89ab, |
33 | Destroyed=0x5678 |
34 | } state; |
35 | int& bar() { |
36 | ASSERT( state==DefaultInitialized||state==CopyInitialized, NULL ); |
37 | return my_bar; |
38 | } |
39 | int bar() const { |
40 | ASSERT( state==DefaultInitialized||state==CopyInitialized, NULL ); |
41 | return my_bar; |
42 | } |
43 | static const int initial_value_of_bar = 42; |
44 | Foo() { |
45 | state = DefaultInitialized; |
46 | ++FooCount; |
47 | my_bar = initial_value_of_bar; |
48 | } |
49 | Foo( const Foo& foo ) { |
50 | state = CopyInitialized; |
51 | ++FooCount; |
52 | my_bar = foo.my_bar; |
53 | } |
54 | ~Foo() { |
55 | ASSERT( state==DefaultInitialized||state==CopyInitialized, NULL ); |
56 | state = Destroyed; |
57 | my_bar = ~initial_value_of_bar; |
58 | --FooCount; |
59 | } |
60 | bool is_const() const {return true;} |
61 | bool is_const() {return false;} |
62 | }; |
63 | |
64 | class FooWithAssign: public Foo { |
65 | public: |
66 | void operator=( const FooWithAssign& x ) { |
67 | ASSERT( x.state==DefaultInitialized||x.state==CopyInitialized, NULL ); |
68 | ASSERT( state==DefaultInitialized||state==CopyInitialized, NULL ); |
69 | my_bar = x.my_bar; |
70 | } |
71 | }; |
72 | |
73 | inline void NextSize( int& s ) { |
74 | if( s<=32 ) ++s; |
75 | else s += s/10; |
76 | } |
77 | |
78 | static void CheckVector( const tbb::concurrent_vector<Foo>& cv, size_t expected_size, size_t old_size ) { |
79 | ASSERT( cv.size()==expected_size, NULL ); |
80 | ASSERT( cv.empty()==(expected_size==0), NULL ); |
81 | for( int j=0; j<int(expected_size); ++j ) { |
82 | if( cv[j].bar()!=~j ) |
83 | std::printf("ERROR on line %d for old_size=%ld expected_size=%ld j=%d\n" ,__LINE__,long(old_size),long(expected_size),j); |
84 | } |
85 | } |
86 | |
87 | void TestResizeAndCopy() { |
88 | typedef tbb::concurrent_vector<Foo> vector_t; |
89 | for( int old_size=0; old_size<=128; NextSize( old_size ) ) { |
90 | for( int new_size=old_size; new_size<=128; NextSize( new_size ) ) { |
91 | long count = FooCount; |
92 | vector_t v; |
93 | ASSERT( count==FooCount, NULL ); |
94 | v.grow_by(old_size); |
95 | ASSERT( count+old_size==FooCount, NULL ); |
96 | for( int j=0; j<old_size; ++j ) |
97 | v[j].bar() = j*j; |
98 | v.grow_to_at_least(new_size); |
99 | ASSERT( count+new_size==FooCount, NULL ); |
100 | for( int j=0; j<new_size; ++j ) { |
101 | int expected = j<old_size ? j*j : Foo::initial_value_of_bar; |
102 | if( v[j].bar()!=expected ) |
103 | std::printf("ERROR on line %d for old_size=%ld new_size=%ld v[%ld].bar()=%d != %d\n" ,__LINE__,long(old_size),long(new_size),long(j),v[j].bar(), expected); |
104 | } |
105 | ASSERT( v.size()==size_t(new_size), NULL ); |
106 | for( int j=0; j<new_size; ++j ) { |
107 | v[j].bar() = ~j; |
108 | } |
109 | const vector_t& cv = v; |
110 | // Try copy constructor |
111 | vector_t copy_of_v(cv); |
112 | CheckVector(cv,new_size,old_size); |
113 | v.clear(); |
114 | ASSERT( v.empty(), NULL ); |
115 | CheckVector(copy_of_v,new_size,old_size); |
116 | } |
117 | } |
118 | } |
119 | |
120 | void TestCapacity() { |
121 | for( size_t old_size=0; old_size<=10000; old_size=(old_size<5 ? old_size+1 : 3*old_size) ) { |
122 | for( size_t new_size=0; new_size<=10000; new_size=(new_size<5 ? new_size+1 : 3*new_size) ) { |
123 | long count = FooCount; |
124 | { |
125 | typedef tbb::concurrent_vector<Foo> vector_t; |
126 | vector_t v; |
127 | v.reserve( old_size ); |
128 | ASSERT( v.capacity()>=old_size, NULL ); |
129 | v.reserve( new_size ); |
130 | ASSERT( v.capacity()>=old_size, NULL ); |
131 | ASSERT( v.capacity()>=new_size, NULL ); |
132 | for( size_t i=0; i<2*new_size; ++i ) { |
133 | ASSERT( size_t(FooCount)==count+i, NULL ); |
134 | size_t j = v.grow_by(1); |
135 | ASSERT( j==i, NULL ); |
136 | } |
137 | } |
138 | ASSERT( FooCount==count, NULL ); |
139 | } |
140 | } |
141 | } |
142 | |
143 | struct AssignElement { |
144 | typedef tbb::concurrent_vector<int>::range_type::iterator iterator; |
145 | iterator base; |
146 | void operator()( const tbb::concurrent_vector<int>::range_type& range ) const { |
147 | for( iterator i=range.begin(); i!=range.end(); ++i ) { |
148 | if( *i!=0 ) |
149 | std::printf("ERROR for v[%ld]\n" , long(i-base)); |
150 | *i = int(i-base); |
151 | } |
152 | } |
153 | AssignElement( iterator base_ ) : base(base_) {} |
154 | }; |
155 | |
156 | struct CheckElement { |
157 | typedef tbb::concurrent_vector<int>::const_range_type::iterator iterator; |
158 | iterator base; |
159 | void operator()( const tbb::concurrent_vector<int>::const_range_type& range ) const { |
160 | for( iterator i=range.begin(); i!=range.end(); ++i ) |
161 | if( *i != int(i-base) ) |
162 | std::printf("ERROR for v[%ld]\n" , long(i-base)); |
163 | } |
164 | CheckElement( iterator base_ ) : base(base_) {} |
165 | }; |
166 | |
167 | #include "tbb/tick_count.h" |
168 | #include "tbb/parallel_for.h" |
169 | #include "../test/harness.h" |
170 | |
171 | //! Test parallel access by iterators |
172 | void TestParallelFor( int nthread ) { |
173 | typedef tbb::concurrent_vector<int> vector_t; |
174 | vector_t v; |
175 | v.grow_to_at_least(N); |
176 | tbb::tick_count t0 = tbb::tick_count::now(); |
177 | if( Verbose ) |
178 | std::printf("Calling parallel_for.h with %ld threads\n" ,long(nthread)); |
179 | tbb::parallel_for( v.range(10000), AssignElement(v.begin()) ); |
180 | tbb::tick_count t1 = tbb::tick_count::now(); |
181 | const vector_t& u = v; |
182 | tbb::parallel_for( u.range(10000), CheckElement(u.begin()) ); |
183 | tbb::tick_count t2 = tbb::tick_count::now(); |
184 | if( Verbose ) |
185 | std::printf("Time for parallel_for.h: assign time = %8.5f, check time = %8.5f\n" , |
186 | (t1-t0).seconds(),(t2-t1).seconds()); |
187 | for( long i=0; size_t(i)<v.size(); ++i ) |
188 | if( v[i]!=i ) |
189 | std::printf("ERROR for v[%ld]\n" , i); |
190 | } |
191 | |
192 | template<typename Iterator1, typename Iterator2> |
193 | void TestIteratorAssignment( Iterator2 j ) { |
194 | Iterator1 i(j); |
195 | ASSERT( i==j, NULL ); |
196 | ASSERT( !(i!=j), NULL ); |
197 | Iterator1 k; |
198 | k = j; |
199 | ASSERT( k==j, NULL ); |
200 | ASSERT( !(k!=j), NULL ); |
201 | } |
202 | |
203 | template<typename Iterator, typename T> |
204 | void TestIteratorTraits() { |
205 | AssertSameType( static_cast<typename Iterator::difference_type*>(0), static_cast<ptrdiff_t*>(0) ); |
206 | AssertSameType( static_cast<typename Iterator::value_type*>(0), static_cast<T*>(0) ); |
207 | AssertSameType( static_cast<typename Iterator::pointer*>(0), static_cast<T**>(0) ); |
208 | AssertSameType( static_cast<typename Iterator::iterator_category*>(0), static_cast<std::random_access_iterator_tag*>(0) ); |
209 | T x; |
210 | typename Iterator::reference xr = x; |
211 | typename Iterator::pointer xp = &x; |
212 | ASSERT( &xr==xp, NULL ); |
213 | } |
214 | |
215 | template<typename Vector, typename Iterator> |
216 | void CheckConstIterator( const Vector& u, int i, const Iterator& cp ) { |
217 | typename Vector::const_reference pref = *cp; |
218 | if( pref.bar()!=i ) |
219 | std::printf("ERROR for u[%ld] using const_iterator\n" , long(i)); |
220 | typename Vector::difference_type delta = cp-u.begin(); |
221 | ASSERT( delta==i, NULL ); |
222 | if( u[i].bar()!=i ) |
223 | std::printf("ERROR for u[%ld] using subscripting\n" , long(i)); |
224 | ASSERT( u.begin()[i].bar()==i, NULL ); |
225 | } |
226 | |
227 | template<typename Iterator1, typename Iterator2, typename V> |
228 | void CheckIteratorComparison( V& u ) { |
229 | Iterator1 i = u.begin(); |
230 | for( int i_count=0; i_count<100; ++i_count ) { |
231 | Iterator2 j = u.begin(); |
232 | for( int j_count=0; j_count<100; ++j_count ) { |
233 | ASSERT( (i==j)==(i_count==j_count), NULL ); |
234 | ASSERT( (i!=j)==(i_count!=j_count), NULL ); |
235 | ASSERT( (i-j)==(i_count-j_count), NULL ); |
236 | ASSERT( (i<j)==(i_count<j_count), NULL ); |
237 | ASSERT( (i>j)==(i_count>j_count), NULL ); |
238 | ASSERT( (i<=j)==(i_count<=j_count), NULL ); |
239 | ASSERT( (i>=j)==(i_count>=j_count), NULL ); |
240 | ++j; |
241 | } |
242 | ++i; |
243 | } |
244 | } |
245 | |
246 | //! Test sequential iterators for vector type V. |
247 | /** Also does timing. */ |
248 | template<typename V> |
249 | void TestSequentialFor() { |
250 | V v; |
251 | v.grow_by(N); |
252 | |
253 | // Check iterator |
254 | tbb::tick_count t0 = tbb::tick_count::now(); |
255 | typename V::iterator p = v.begin(); |
256 | ASSERT( !(*p).is_const(), NULL ); |
257 | ASSERT( !p->is_const(), NULL ); |
258 | for( int i=0; size_t(i)<v.size(); ++i, ++p ) { |
259 | if( (*p).state!=Foo::DefaultInitialized ) |
260 | std::printf("ERROR for v[%ld]\n" , long(i)); |
261 | typename V::reference pref = *p; |
262 | pref.bar() = i; |
263 | typename V::difference_type delta = p-v.begin(); |
264 | ASSERT( delta==i, NULL ); |
265 | ASSERT( -delta<=0, "difference type not signed?" ); |
266 | } |
267 | tbb::tick_count t1 = tbb::tick_count::now(); |
268 | |
269 | // Check const_iterator going forwards |
270 | const V& u = v; |
271 | typename V::const_iterator cp = u.begin(); |
272 | ASSERT( (*cp).is_const(), NULL ); |
273 | ASSERT( cp->is_const(), NULL ); |
274 | for( int i=0; size_t(i)<u.size(); ++i, ++cp ) { |
275 | CheckConstIterator(u,i,cp); |
276 | } |
277 | tbb::tick_count t2 = tbb::tick_count::now(); |
278 | if( Verbose ) |
279 | std::printf("Time for serial for: assign time = %8.5f, check time = %8.5f\n" , |
280 | (t1-t0).seconds(),(t2-t1).seconds()); |
281 | |
282 | // Now go backwards |
283 | cp = u.end(); |
284 | for( int i=int(u.size()); i>0; ) { |
285 | --i; |
286 | --cp; |
287 | if( i>0 ) { |
288 | typename V::const_iterator cp_old = cp--; |
289 | int here = (*cp_old).bar(); |
290 | ASSERT( here==u[i].bar(), NULL ); |
291 | typename V::const_iterator cp_new = cp++; |
292 | int prev = (*cp_new).bar(); |
293 | ASSERT( prev==u[i-1].bar(), NULL ); |
294 | } |
295 | CheckConstIterator(u,i,cp); |
296 | } |
297 | |
298 | // Now go forwards and backwards |
299 | cp = u.begin(); |
300 | ptrdiff_t k = 0; |
301 | for( size_t i=0; i<u.size(); ++i ) { |
302 | CheckConstIterator(u,int(k),cp); |
303 | typename V::difference_type delta = i*3 % u.size(); |
304 | if( 0<=k+delta && size_t(k+delta)<u.size() ) { |
305 | cp += delta; |
306 | k += delta; |
307 | } |
308 | delta = i*7 % u.size(); |
309 | if( 0<=k-delta && size_t(k-delta)<u.size() ) { |
310 | if( i&1 ) |
311 | cp -= delta; // Test operator-= |
312 | else |
313 | cp = cp - delta; // Test operator- |
314 | k -= delta; |
315 | } |
316 | } |
317 | |
318 | for( int i=0; size_t(i)<u.size(); i=(i<50?i+1:i*3) ) |
319 | for( int j=-i; size_t(i+j)<u.size(); j=(j<50?j+1:j*5) ) { |
320 | ASSERT( (u.begin()+i)[j].bar()==i+j, NULL ); |
321 | ASSERT( (v.begin()+i)[j].bar()==i+j, NULL ); |
322 | ASSERT( (i+u.begin())[j].bar()==i+j, NULL ); |
323 | ASSERT( (i+v.begin())[j].bar()==i+j, NULL ); |
324 | } |
325 | |
326 | CheckIteratorComparison<typename V::iterator, typename V::iterator>(v); |
327 | CheckIteratorComparison<typename V::iterator, typename V::const_iterator>(v); |
328 | CheckIteratorComparison<typename V::const_iterator, typename V::iterator>(v); |
329 | CheckIteratorComparison<typename V::const_iterator, typename V::const_iterator>(v); |
330 | |
331 | TestIteratorAssignment<typename V::const_iterator>( u.begin() ); |
332 | TestIteratorAssignment<typename V::const_iterator>( v.begin() ); |
333 | TestIteratorAssignment<typename V::iterator>( v.begin() ); |
334 | |
335 | // Check reverse_iterator |
336 | typename V::reverse_iterator rp = v.rbegin(); |
337 | for( size_t i=v.size(); i>0; --i, ++rp ) { |
338 | typename V::reference pref = *rp; |
339 | ASSERT( size_t(pref.bar())==i-1, NULL ); |
340 | ASSERT( rp!=v.rend(), NULL ); |
341 | } |
342 | ASSERT( rp==v.rend(), NULL ); |
343 | |
344 | // Check const_reverse_iterator |
345 | typename V::const_reverse_iterator crp = u.rbegin(); |
346 | for( size_t i=v.size(); i>0; --i, ++crp ) { |
347 | typename V::const_reference cpref = *crp; |
348 | ASSERT( size_t(cpref.bar())==i-1, NULL ); |
349 | ASSERT( crp!=u.rend(), NULL ); |
350 | } |
351 | ASSERT( crp==u.rend(), NULL ); |
352 | |
353 | TestIteratorAssignment<typename V::const_reverse_iterator>( u.rbegin() ); |
354 | TestIteratorAssignment<typename V::reverse_iterator>( v.rbegin() ); |
355 | } |
356 | |
357 | static const size_t Modulus = 7; |
358 | |
359 | typedef tbb::concurrent_vector<Foo> MyVector; |
360 | |
361 | class GrowToAtLeast { |
362 | MyVector& my_vector; |
363 | public: |
364 | void operator()( const tbb::blocked_range<size_t>& range ) const { |
365 | for( size_t i=range.begin(); i!=range.end(); ++i ) { |
366 | size_t n = my_vector.size(); |
367 | size_t k = n==0 ? 0 : i % (2*n+1); |
368 | my_vector.grow_to_at_least(k+1); |
369 | ASSERT( my_vector.size()>=k+1, NULL ); |
370 | } |
371 | } |
372 | GrowToAtLeast( MyVector& vector ) : my_vector(vector) {} |
373 | }; |
374 | |
375 | void TestConcurrentGrowToAtLeast() { |
376 | MyVector v; |
377 | for( size_t s=1; s<1000; s*=10 ) { |
378 | tbb::parallel_for( tbb::blocked_range<size_t>(0,1000000,100), GrowToAtLeast(v) ); |
379 | } |
380 | } |
381 | |
382 | //! Test concurrent invocations of method concurrent_vector::grow_by |
383 | class GrowBy { |
384 | MyVector& my_vector; |
385 | public: |
386 | void operator()( const tbb::blocked_range<int>& range ) const { |
387 | for( int i=range.begin(); i!=range.end(); ++i ) { |
388 | if( i%3 ) { |
389 | Foo& element = my_vector[my_vector.grow_by(1)]; |
390 | element.bar() = i; |
391 | } else { |
392 | Foo f; |
393 | f.bar() = i; |
394 | size_t k = my_vector.push_back( f ); |
395 | ASSERT( my_vector[k].bar()==i, NULL ); |
396 | } |
397 | } |
398 | } |
399 | GrowBy( MyVector& vector ) : my_vector(vector) {} |
400 | }; |
401 | |
402 | //! Test concurrent invocations of method concurrent_vector::grow_by |
403 | void TestConcurrentGrowBy( int nthread ) { |
404 | int m = 100000; |
405 | MyVector v; |
406 | tbb::parallel_for( tbb::blocked_range<int>(0,m,1000), GrowBy(v) ); |
407 | ASSERT( v.size()==size_t(m), NULL ); |
408 | |
409 | // Verify that v is a permutation of 0..m |
410 | int inversions = 0; |
411 | bool* found = new bool[m]; |
412 | memset( found, 0, m ); |
413 | for( int i=0; i<m; ++i ) { |
414 | int index = v[i].bar(); |
415 | ASSERT( !found[index], NULL ); |
416 | found[index] = true; |
417 | if( i>0 ) |
418 | inversions += v[i].bar()<v[i-1].bar(); |
419 | } |
420 | for( int i=0; i<m; ++i ) { |
421 | ASSERT( found[i], NULL ); |
422 | ASSERT( nthread>1 || v[i].bar()==i, "sequential execution is wrong" ); |
423 | } |
424 | delete[] found; |
425 | if( nthread>1 && inversions<m/10 ) |
426 | std::printf("Warning: not much concurrency in TestConcurrentGrowBy\n" ); |
427 | } |
428 | |
429 | //! Test the assignment operator |
430 | void TestAssign() { |
431 | typedef tbb::concurrent_vector<FooWithAssign> vector_t; |
432 | for( int dst_size=1; dst_size<=128; NextSize( dst_size ) ) { |
433 | for( int src_size=2; src_size<=128; NextSize( src_size ) ) { |
434 | vector_t u; |
435 | u.grow_to_at_least(src_size); |
436 | for( int i=0; i<src_size; ++i ) |
437 | u[i].bar() = i*i; |
438 | vector_t v; |
439 | v.grow_to_at_least(dst_size); |
440 | for( int i=0; i<dst_size; ++i ) |
441 | v[i].bar() = -i; |
442 | v = u; |
443 | u.clear(); |
444 | ASSERT( u.size()==0, NULL ); |
445 | ASSERT( v.size()==size_t(src_size), NULL ); |
446 | for( int i=0; i<src_size; ++i ) |
447 | ASSERT( v[i].bar()==(i*i), NULL ); |
448 | } |
449 | } |
450 | } |
451 | |
452 | //------------------------------------------------------------------------ |
453 | // Regression test for problem where on oversubscription caused |
454 | // concurrent_vector::grow_by to run very slowly (TR#196). |
455 | //------------------------------------------------------------------------ |
456 | |
457 | #include "tbb/task_scheduler_init.h" |
458 | #include <math.h> |
459 | |
460 | typedef unsigned long Number; |
461 | |
462 | static tbb::concurrent_vector<Number> Primes; |
463 | |
464 | class FindPrimes { |
465 | bool is_prime( Number val ) const { |
466 | int limit, factor = 3; |
467 | if( val<5u ) |
468 | return val==2; |
469 | else { |
470 | limit = long(sqrtf(float(val))+0.5f); |
471 | while( factor<=limit && val % factor ) |
472 | ++factor; |
473 | return factor>limit; |
474 | } |
475 | } |
476 | public: |
477 | void operator()( const tbb::blocked_range<Number>& r ) const { |
478 | for( Number i=r.begin(); i!=r.end(); ++i ) { |
479 | if( i%2 && is_prime(i) ) { |
480 | Primes[Primes.grow_by(1)] = i; |
481 | } |
482 | } |
483 | } |
484 | }; |
485 | |
486 | static double TimeFindPrimes( int nthread ) { |
487 | Primes.clear(); |
488 | tbb::task_scheduler_init init(nthread); |
489 | tbb::tick_count t0 = tbb::tick_count::now(); |
490 | tbb::parallel_for( tbb::blocked_range<Number>(0,1000000,500), FindPrimes() ); |
491 | tbb::tick_count t1 = tbb::tick_count::now(); |
492 | return (t1-t0).seconds(); |
493 | } |
494 | |
495 | static void TestFindPrimes() { |
496 | // Time fully subscribed run. |
497 | double t2 = TimeFindPrimes( tbb::task_scheduler_init::automatic ); |
498 | |
499 | // Time parallel run that is very likely oversubscribed. |
500 | double t128 = TimeFindPrimes(128); |
501 | |
502 | if( Verbose ) |
503 | std::printf("TestFindPrimes: t2==%g t128=%g\n" , t2, t128 ); |
504 | |
505 | // We allow the 128-thread run a little extra time to allow for thread overhead. |
506 | // Theoretically, following test will fail on machine with >128 processors. |
507 | // But that situation is not going to come up in the near future, |
508 | // and the generalization to fix the issue is not worth the trouble. |
509 | if( t128>1.10*t2 ) { |
510 | std::printf("Warning: grow_by is pathetically slow: t2==%g t128=%g\n" , t2, t128); |
511 | } |
512 | } |
513 | |
514 | //------------------------------------------------------------------------ |
515 | // Test compatibility with STL sort. |
516 | //------------------------------------------------------------------------ |
517 | |
518 | #include <algorithm> |
519 | |
520 | void TestSort() { |
521 | for( int n=1; n<100; n*=3 ) { |
522 | tbb::concurrent_vector<int> array; |
523 | array.grow_by( n ); |
524 | for( int i=0; i<n; ++i ) |
525 | array[i] = (i*7)%n; |
526 | std::sort( array.begin(), array.end() ); |
527 | for( int i=0; i<n; ++i ) |
528 | ASSERT( array[i]==i, NULL ); |
529 | } |
530 | } |
531 | |
532 | //------------------------------------------------------------------------ |
533 | |
534 | int TestMain () { |
535 | if( MinThread<1 ) { |
536 | std::printf("ERROR: MinThread=%d, but must be at least 1\n" ,MinThread); |
537 | } |
538 | |
539 | TestIteratorTraits<tbb::concurrent_vector<Foo>::iterator,Foo>(); |
540 | TestIteratorTraits<tbb::concurrent_vector<Foo>::const_iterator,const Foo>(); |
541 | TestSequentialFor<tbb::concurrent_vector<Foo> > (); |
542 | TestResizeAndCopy(); |
543 | TestAssign(); |
544 | TestCapacity(); |
545 | for( int nthread=MinThread; nthread<=MaxThread; ++nthread ) { |
546 | tbb::task_scheduler_init init( nthread ); |
547 | TestParallelFor( nthread ); |
548 | TestConcurrentGrowToAtLeast(); |
549 | TestConcurrentGrowBy( nthread ); |
550 | } |
551 | TestFindPrimes(); |
552 | TestSort(); |
553 | return Harness::Done; |
554 | } |
555 | |