parallel_foreach.hpp 15.7 KB
Newer Older
Sylvain Thery's avatar
Sylvain Thery committed
1 2 3
/*******************************************************************************
* CGoGN: Combinatorial and Geometric modeling with Generic N-dimensional Maps  *
* version 0.1                                                                  *
4
* Copyright (C) 2009-2012, IGG Team, LSIIT, University of Strasbourg           *
Sylvain Thery's avatar
Sylvain Thery committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
*                                                                              *
* This library is free software; you can redistribute it and/or modify it      *
* under the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation; either version 2.1 of the License, or (at your     *
* option) any later version.                                                   *
*                                                                              *
* This library is distributed in the hope that it will be useful, but WITHOUT  *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or        *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License  *
* for more details.                                                            *
*                                                                              *
* You should have received a copy of the GNU Lesser General Public License     *
* along with this library; if not, write to the Free Software Foundation,      *
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.           *
*                                                                              *
20
* Web site: http://cgogn.unistra.fr/                                           *
Sylvain Thery's avatar
Sylvain Thery committed
21 22 23 24 25
* Contact information: cgogn@unistra.fr                                        *
*                                                                              *
*******************************************************************************/
#include <boost/thread.hpp>
#include <boost/thread/barrier.hpp>
sylvain thery's avatar
sylvain thery committed
26
#include <vector>
Sylvain Thery's avatar
Sylvain Thery committed
27

28

Sylvain Thery's avatar
Sylvain Thery committed
29 30 31 32 33 34 35 36 37
namespace CGoGN
{

namespace Algo
{

namespace Parallel
{

38
/// internal functor for boost call
39
class ThreadFunctionAttrib
40 41
{
protected:
42
	std::vector<unsigned int>& m_ids;
43 44 45
	boost::barrier& m_sync1;
	boost::barrier& m_sync2;
	bool& m_finished;
46 47 48
	unsigned int m_id;
	FunctorAttribThreaded* m_functor;

49
public:
50 51
	ThreadFunctionAttrib(FunctorAttribThreaded* func, std::vector<unsigned int>& vid, boost::barrier& s1, boost::barrier& s2, bool& finished, unsigned int id):
		m_ids(vid), m_sync1(s1), m_sync2(s2), m_finished(finished), m_id(id), m_functor(func)
52 53 54
	{
	}

55 56
	ThreadFunctionAttrib(const ThreadFunctionAttrib& tf):
		m_ids(tf.m_ids), m_sync1(tf.m_sync1), m_sync2(tf.m_sync2), m_finished(tf.m_finished), m_id(tf.m_id), m_functor(tf.m_functor){}
57 58 59 60 61

	void operator()()
	{
		while (!m_finished)
		{
62
			for (std::vector<unsigned int>::const_iterator it = m_ids.begin(); it != m_ids.end(); ++it)
63
				m_functor->run(*it,m_id);
64 65 66 67
			m_sync1.wait();
			m_sync2.wait();
		}
	}
68 69
};

70

71
/// internal functor for boost call
72 73
template<typename MAP>
class ThreadFunction
Sylvain Thery's avatar
Sylvain Thery committed
74 75
{
protected:
76
	std::vector<Dart>& m_darts;
Sylvain Thery's avatar
Sylvain Thery committed
77 78 79
	boost::barrier& m_sync1;
	boost::barrier& m_sync2;
	bool& m_finished;
80 81
	unsigned int m_id;
	FunctorMapThreaded<MAP>* m_functor;
Sylvain Thery's avatar
Sylvain Thery committed
82
public:
83 84
	ThreadFunction(FunctorMapThreaded<MAP>* func, std::vector<Dart>& vd, boost::barrier& s1, boost::barrier& s2, bool& finished, unsigned int id):
		m_darts(vd), m_sync1(s1), m_sync2(s2), m_finished(finished), m_id(id), m_functor(func)
85 86 87
	{
	}

88 89
	ThreadFunction(const ThreadFunction<MAP>& tf):
		m_darts(tf.m_darts), m_sync1(tf.m_sync1), m_sync2(tf.m_sync2), m_finished(tf.m_finished), m_id(tf.m_id), m_functor(tf.m_functor){}
Sylvain Thery's avatar
Sylvain Thery committed
90 91 92 93 94 95

	void operator()()
	{
		while (!m_finished)
		{
			for (std::vector<Dart>::const_iterator it = m_darts.begin(); it != m_darts.end(); ++it)
96
				m_functor->run(*it,m_id);
Sylvain Thery's avatar
Sylvain Thery committed
97 98 99 100 101 102
			m_sync1.wait();
			m_sync2.wait();
		}
	}
};

103

104 105 106 107 108 109
inline unsigned int nbThreads()
{
	return boost::thread::hardware_concurrency();
}


110
inline unsigned int optimalNbThreads(NbParam p)
Sylvain Thery's avatar
Sylvain Thery committed
111
{
112 113 114 115 116 117 118 119
	if (p==NB_HIGHCOMPUTE)
		return nbThreads();
	if (p==NB_VERYHIGHMEMORY)
		return 2;

 // NB_HIGHMEMORY
	if (NBCORES != 0)
		return NBCORES;
120 121 122 123
	unsigned int nb = nbThreads();
	if (nb>4)
		return nb/2 ;
	return nb;
124

125
}
Sylvain Thery's avatar
Sylvain Thery committed
126 127


128

129
template <typename MAP, unsigned int ORBIT>
130
void foreach_cell(MAP& map, FunctorMapThreaded<MAP>& func, unsigned int nbth, bool needMarkers, const FunctorSelect& good, unsigned int currentThread)
131 132 133
{
	if (nbth == 0)
		nbth = optimalNbThreads();
Sylvain Thery's avatar
Sylvain Thery committed
134

135 136
	std::vector<FunctorMapThreaded<MAP>*> funcs;
	funcs.reserve(nbth);
137

138 139 140
	FunctorMapThreaded<MAP>* ptr = func.duplicate();
	bool shared = (ptr == NULL);

141
	if (shared)
142
	{
143
		for (unsigned int i = 0; i < nbth; ++i)
144
			funcs.push_back(&func);
145 146 147
	}
	else
	{
148 149
		funcs.push_back(ptr);
		for (unsigned int i = 1; i < nbth; ++i)
150
			funcs.push_back(func.duplicate());
151
	}
Sylvain Thery's avatar
Sylvain Thery committed
152

153
	foreach_cell<MAP,ORBIT>(map,funcs,nbth,needMarkers,good,currentThread);
Sylvain Thery's avatar
Sylvain Thery committed
154

155
	if (!shared)
156
		for (unsigned int i = 0; i < nbth; ++i)
157
			delete funcs[i];
Sylvain Thery's avatar
Sylvain Thery committed
158 159
}

160 161
template <typename MAP, unsigned int ORBIT>
void foreach_cell(MAP& map, std::vector<FunctorMapThreaded<MAP>*>& funcs, unsigned int nbth, bool needMarkers, const FunctorSelect& good, unsigned int currentThread)
Sylvain Thery's avatar
Sylvain Thery committed
162
{
163 164
	assert(funcs.size() ==  nbth);

sylvain thery's avatar
sylvain thery committed
165 166
	std::vector<Dart>* vd = new std::vector<Dart>[nbth];
	boost::thread** threads = new boost::thread*[nbth];
Sylvain Thery's avatar
Sylvain Thery committed
167 168

	// nbth new functions, new thread (with good darts !)
169
	for (unsigned int i = 0; i < nbth; ++i)
170
		vd[i].reserve(SIZE_BUFFER_THREAD);
Sylvain Thery's avatar
Sylvain Thery committed
171

172 173 174 175 176 177 178 179 180 181
	AttributeContainer* cont = NULL;
	DartMarker* dmark = NULL;
	CellMarker<ORBIT>* cmark = NULL;
	AttributeMultiVector<Dart>* quickTraversal = map.template getQuickTraversal<ORBIT>() ;

	// fill each vd buffers with SIZE_BUFFER_THREAD darts
	Dart d;
	unsigned int di=0;

	if(quickTraversal != NULL)
Sylvain Thery's avatar
Sylvain Thery committed
182
	{
183 184 185 186 187
		cont = &(map.template getAttributeContainer<ORBIT>()) ;

		di = cont->begin();
		unsigned int nb = 0;
		while ((di != cont->end()) && (nb < nbth*SIZE_BUFFER_THREAD) )
Sylvain Thery's avatar
Sylvain Thery committed
188
		{
189 190 191 192 193 194 195
			d = quickTraversal->operator[](di);
			if (good(d))
			{
				vd[nb%nbth].push_back(d);
				nb++;
			}
			cont->next(di);
Sylvain Thery's avatar
Sylvain Thery committed
196 197
		}
	}
198
	else
199
	{
200 201 202 203 204 205 206 207
		if(map.template isOrbitEmbedded<ORBIT>())
		{
			cmark = new CellMarker<ORBIT>(map, currentThread) ;

			d = map.begin();
			unsigned int nb = 0;
			while ((d != map.end()) && (nb < nbth*SIZE_BUFFER_THREAD) )
			{
208
				if (good(d) && (!map.isBoundaryMarked(d)) && (!cmark->isMarked(d)))
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
				{
					cmark->mark(d);
					vd[nb%nbth].push_back(d);
					nb++;
				}
				map.next(d);
			}
		}
		else
		{
			dmark = new DartMarker(map, currentThread) ;
			d = map.begin();
			unsigned int nb = 0;
			while ((d != map.end()) && (nb < nbth*SIZE_BUFFER_THREAD) )
			{
224
				if (good(d) && (!map.isBoundaryMarked(d)) && (!dmark->isMarked(d)))
225 226 227 228 229 230 231 232
				{
					dmark->markOrbit<ORBIT>(d);
					vd[nb%nbth].push_back(d);
					nb++;
				}
				map.next(d);
			}
		}
233 234
	}

Sylvain Thery's avatar
Sylvain Thery committed
235 236 237 238
	boost::barrier sync1(nbth+1);
	boost::barrier sync2(nbth+1);
	bool finished=false;
	// lauch threads
239 240
	if (needMarkers)
	{
241 242 243
		unsigned int nbth_prec = map.getNbThreadMarkers();
		if (nbth_prec < nbth+1)
			map.addThreadMarker(nbth+1-nbth_prec);
244
	}
245 246 247 248

	for (unsigned int i = 0; i < nbth; ++i)
		threads[i] = new boost::thread(ThreadFunction<MAP>(funcs[i], vd[i],sync1,sync2, finished,1+i));

Sylvain Thery's avatar
Sylvain Thery committed
249
	// and continue to traverse the map
sylvain thery's avatar
sylvain thery committed
250 251
	std::vector<Dart>* tempo = new std::vector<Dart>[nbth];

252
	for (unsigned int i = 0; i < nbth; ++i)
253
		tempo[i].reserve(SIZE_BUFFER_THREAD);
Sylvain Thery's avatar
Sylvain Thery committed
254 255


256 257 258
	if (cont)
	{
		while (di != cont->end())
Sylvain Thery's avatar
Sylvain Thery committed
259
		{
260 261 262 263
			for (unsigned int i = 0; i < nbth; ++i)
				tempo[i].clear();
			unsigned int nb = 0;
			while ((di != cont->end()) && (nb < nbth*SIZE_BUFFER_THREAD) )
Sylvain Thery's avatar
Sylvain Thery committed
264
			{
265 266 267 268 269 270 271
				d = quickTraversal->operator[](di);
				if (good(d))
				{
					tempo[nb%nbth].push_back(d);
					nb++;
				}
				cont->next(di);
Sylvain Thery's avatar
Sylvain Thery committed
272
			}
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
			sync1.wait();
			for (unsigned int i = 0; i < nbth; ++i)
				vd[i].swap(tempo[i]);
			sync2.wait();
		}
	}
	else if (cmark)
	{
		while (d != map.end())
		{
			for (unsigned int i = 0; i < nbth; ++i)
				tempo[i].clear();
			unsigned int nb = 0;
			while ((d != map.end()) && (nb < nbth*SIZE_BUFFER_THREAD) )
			{
288
				if (good(d) && (!map.isBoundaryMarked(d)) && (!cmark->isMarked(d)))
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
				{
					cmark->mark(d);
					tempo[nb%nbth].push_back(d);
					nb++;
				}
				map.next(d);
			}
			sync1.wait();
			for (unsigned int i = 0; i < nbth; ++i)
				vd[i].swap(tempo[i]);
			sync2.wait();
		}
	}
	else
	{
		while (d != map.end())
		{
			for (unsigned int i = 0; i < nbth; ++i)
				tempo[i].clear();
			unsigned int nb = 0;
			while ((d != map.end()) && (nb < nbth*SIZE_BUFFER_THREAD) )
			{
311
				if (good(d) && (!map.isBoundaryMarked(d)) && (!dmark->isMarked(d)))
312 313 314 315 316 317 318 319 320 321 322
				{
					dmark->markOrbit<ORBIT>(d);
					tempo[nb%nbth].push_back(d);
					nb++;
				}
				map.next(d);
			}
			sync1.wait();
			for (unsigned int i = 0; i < nbth; ++i)
				vd[i].swap(tempo[i]);
			sync2.wait();
Sylvain Thery's avatar
Sylvain Thery committed
323 324 325 326 327 328 329 330
		}
	}

	sync1.wait();
	finished = true;
	sync2.wait();

	//wait for all theads to be finished
331
	for (unsigned int i = 0; i < nbth; ++i)
Sylvain Thery's avatar
Sylvain Thery committed
332 333 334 335
	{
		threads[i]->join();
		delete threads[i];
	}
sylvain thery's avatar
sylvain thery committed
336 337 338
	delete[] threads;
	delete[] vd;
	delete[] tempo;
Sylvain Thery's avatar
Sylvain Thery committed
339

340 341
	if (cmark != NULL)
		delete cmark;
Sylvain Thery's avatar
Sylvain Thery committed
342

343 344 345
	if (dmark != NULL)
		delete dmark;
}
Sylvain Thery's avatar
Sylvain Thery committed
346 347 348



349
template <typename MAP>
350
void foreach_dart(MAP& map, FunctorMapThreaded<MAP>& func, unsigned int nbth, bool needMarkers, const FunctorSelect& good)
351 352 353
{
	if (nbth == 0)
		nbth = optimalNbThreads();
354

355 356 357
	std::vector<FunctorMapThreaded<MAP>*> funcs;
	funcs.reserve(nbth);

358 359 360
	FunctorMapThreaded<MAP>* ptr = func.duplicate();
	bool shared = (ptr == NULL);

361
	if (shared)
362
	{
363
		for (unsigned int i = 0; i < nbth; ++i)
364
			funcs.push_back(&func);
365 366 367
	}
	else
	{
368 369
		funcs.push_back(ptr);
		for (unsigned int i = 1; i < nbth; ++i)
370
			funcs.push_back(func.duplicate());
371
	}
Sylvain Thery's avatar
Sylvain Thery committed
372

373
	foreach_dart<MAP>(map,funcs,nbth,needMarkers,good);
Sylvain Thery's avatar
Sylvain Thery committed
374

375
	if (!shared)
376
		for (unsigned int i = 0; i < nbth; ++i)
377
			delete funcs[i];
Sylvain Thery's avatar
Sylvain Thery committed
378 379
}

380 381 382

template <typename MAP>
void foreach_dart(MAP& map, std::vector<FunctorMapThreaded<MAP>*> funcs, unsigned int nbth, bool needMarkers, const FunctorSelect& good)
383
{
384 385
	assert(funcs.size() ==  nbth);

386 387 388
	std::vector<Dart>* vd = new std::vector<Dart>[nbth];
	boost::thread** threads = new boost::thread*[nbth];

389
	Dart d = map.begin();
390 391

	// nbth new functions, new thread (with good darts !)
392
	for (unsigned int i = 0; i < nbth; ++i)
393
		vd[i].reserve(SIZE_BUFFER_THREAD);
394 395

	// fill each vd buffers with 4096 darts
396
	unsigned int nb = 0;
397
	while ((d != map.end()) && (nb < nbth*SIZE_BUFFER_THREAD) )
398
	{
399
		if (good(d))
400 401 402 403 404 405 406
		{
			vd[nb%nbth].push_back(d);
			nb++;
		}
		map.next(d);
	}

407 408 409 410
	boost::barrier sync1(nbth+1);
	boost::barrier sync2(nbth+1);
	bool finished = false;
	// lauch threads
411 412 413 414 415 416 417
	if (needMarkers)
	{
		unsigned int nbth_prec = map.getNbThreadMarkers();
		if (nbth_prec < nbth+1)
			map.addThreadMarker(nbth+1-nbth_prec);
	}

418
	for (unsigned int i = 0; i < nbth; ++i)
419
	{
420
		threads[i] = new boost::thread(ThreadFunction<MAP>(funcs[i], vd[i],sync1,sync2, finished,1+i));
421 422 423 424
	}

	// and continue to traverse the map
	std::vector<Dart>* tempo = new std::vector<Dart>[nbth];
425
	for (unsigned int i = 0; i < nbth; ++i)
426
		tempo[i].reserve(SIZE_BUFFER_THREAD);
427

428
	while (d != map.end())
429
	{
430
		for (unsigned int i = 0; i < nbth; ++i)
431 432
			tempo[i].clear();

433 434
		unsigned int nb =0;
		while ((d != map.end()) && (nb < nbth*SIZE_BUFFER_THREAD) )
435
		{
436
			if (good(d))
437 438 439 440 441 442 443 444
			{
				tempo[nb%nbth].push_back(d);
				nb++;
			}
			map.next(d);
		}

		sync1.wait();
445
		for (unsigned int i = 0; i < nbth; ++i)
446 447 448 449 450 451 452 453
			vd[i].swap(tempo[i]);
		sync2.wait();
	}

	sync1.wait();
	finished = true;
	sync2.wait();

454
	//wait for all theads to be finished
455
	for (unsigned int i = 0; i < nbth; ++i)
456 457 458 459
	{
		threads[i]->join();
		delete threads[i];
	}
460 461 462 463
	
	delete vd;
	delete threads;
	delete tempo;
464 465 466
}


467
// TODO same modification for transparent usage of dart marker / cell marker / quick traversal
468

469 470
template <typename MAP, unsigned int CELL>
void foreach_cell2Pass(MAP& map, std::vector<FunctorMapThreaded<MAP>*>& funcsFrontnBack, unsigned int nbLoops, unsigned int nbth, bool needMarkers, const FunctorSelect& good)
471
{
472
	std::vector<Dart>* vd = new std::vector<Dart>[2*nbth];
473
	for (unsigned int i = 0; i < nbth; ++i)
474
		vd[i].reserve(SIZE_BUFFER_THREAD);
475

476
	boost::thread** threadsAB = new boost::thread*[2*nbth];
477 478 479 480 481 482 483 484 485

	if (needMarkers)
	{
		// ensure that there is enough threads
		unsigned int nbth_prec = map.getNbThreadMarkers();
		if (nbth_prec < nbth+1)
			map.addThreadMarker(nbth+1-nbth_prec);
	}

486 487 488 489
	CellMarkerNoUnmark<CELL> cm(map); // for 2 pass front mark / back unmark

	boost::barrier sync1(nbth+1);
	boost::barrier sync2(nbth+1);
490 491 492 493 494 495

	for (unsigned int loop=0; loop< nbLoops; ++loop)
	{
		// PASS FRONT (A)
		{
			Dart d = map.begin();
496
			// fill each vd buffers with SIZE_BUFFER_THREAD darts
497
			unsigned int nb = 0;
498
			while ((d != map.end()) && (nb < nbth*SIZE_BUFFER_THREAD) )
499 500 501 502 503 504 505 506 507 508 509
			{
				if (good(d) && (!cm.isMarked(d)))
				{
					cm.mark(d);
					vd[nb%nbth].push_back(d);
					nb++;
				}
				map.next(d);
			}

			bool finished=false;
510 511 512 513 514
			// lauch threads funcsFrontnBack

			for (unsigned int i = 0; i < nbth; ++i)
				threadsAB[i] = new boost::thread(ThreadFunction<MAP>(funcsFrontnBack[i], vd[i], sync1, sync2, finished,1+i));

515 516 517 518 519
			// and continue to traverse the map

			while (d != map.end())
			{
				for (unsigned int i = 0; i < nbth; ++i)
520
					vd[nbth+i].clear();
521 522

				unsigned int nb = 0;
523
				while ((d != map.end()) && (nb < nbth*SIZE_BUFFER_THREAD) )
524 525 526 527
				{
					if (good(d) && (!cm.isMarked(d)))
					{
						cm.mark(d);
528
						vd[nbth+nb%nbth].push_back(d);
529 530 531 532 533 534 535
						nb++;
					}
					map.next(d);
				}

				sync1.wait();
				for (unsigned int i = 0; i < nbth; ++i)
536
					vd[i].swap(vd[nbth+i]);
537 538 539 540 541 542 543 544 545
				sync2.wait();
			}

			sync1.wait();
			finished = true;
			sync2.wait();

			//wait for all theads to be finished
			for (unsigned int i = 0; i < nbth; ++i)
546
				threadsAB[i]->join();
547 548 549 550 551 552
		}
		// PASS BACK (B)
		{
			for (unsigned int i = 0; i < nbth; ++i)
				vd[i].clear();
			Dart d = map.begin();
553
			// fill each vd buffers with SIZE_BUFFER_THREAD darts
554
			unsigned int nb = 0;
555
			while ((d != map.end()) && (nb < nbth*SIZE_BUFFER_THREAD) )
556 557 558 559 560 561 562 563 564 565 566
			{
				if (good(d) && (cm.isMarked(d)))
				{
					cm.unmark(d);
					vd[nb%nbth].push_back(d);
					nb++;
				}
				map.next(d);
			}

			bool finished=false;
567 568 569
			for (unsigned int i = 0; i < nbth; ++i)
				threadsAB[nbth+i] = new boost::thread(ThreadFunction<MAP>(funcsFrontnBack[nbth+i], vd[i],sync1,sync2, finished,1+i));

570 571 572 573 574
			// and continue to traverse the map

			while (d != map.end())
			{
				for (unsigned int i = 0; i < nbth; ++i)
575
					vd[nbth+i].clear();
576 577

				unsigned int nb = 0;
578
				while ((d != map.end()) && (nb < nbth*SIZE_BUFFER_THREAD) )
579 580 581 582
				{
					if (good(d) && (cm.isMarked(d)))
					{
						cm.unmark(d);
583
						vd[nbth+nb%nbth].push_back(d);
584 585 586 587 588 589 590
						nb++;
					}
					map.next(d);
				}

				sync1.wait();
				for (unsigned int i = 0; i < nbth; ++i)
591
					vd[i].swap(vd[nbth+i]);
592 593 594 595 596 597 598 599 600
				sync2.wait();
			}

			sync1.wait();
			finished = true;
			sync2.wait();

			//wait for all theads to be finished
			for (unsigned int i = 0; i < nbth; ++i)
601
				threadsAB[nbth+i]->join();
602 603 604 605
		}
	}

	// free buffers and threads
606
	for (unsigned int i = 0; i < 2*nbth; ++i)
607
	{
608
		delete threadsAB[i];
609
	}
610
	delete[] threadsAB;
611 612 613 614 615
	delete[] vd;
}



616
template <typename MAP, unsigned int CELL>
617
void foreach_cell2Pass(MAP& map, FunctorMapThreaded<MAP>& funcFront, FunctorMapThreaded<MAP>& funcBack, unsigned int nbLoops, unsigned int nbth, bool needMarkers, const FunctorSelect& good)
618 619 620
{
	if (nbth == 0)
		nbth = optimalNbThreads();
621

622 623
	std::vector<FunctorMapThreaded<MAP>*> funcs;
	funcs.reserve(nbth);
624

625 626 627
	FunctorMapThreaded<MAP>* ptr = funcFront.duplicate();
	bool shared = (ptr == NULL);

628 629 630 631 632 633 634 635 636
	if (shared)
	{
		for (unsigned int i = 0; i < nbth; ++i)
			funcs.push_back(&funcFront);
		for (unsigned int i = 0; i < nbth; ++i)
			funcs.push_back(&funcBack);
	}
	else
	{
637 638
		funcs.push_back(ptr);
		for (unsigned int i = 1; i < nbth; ++i)
639 640 641
			funcs.push_back(funcFront.duplicate());
		for (unsigned int i = 0; i < nbth; ++i)
			funcs.push_back(funcBack.duplicate());
642

643
	}
644

645
	foreach_cell2Pass<MAP,CELL>(map,funcs,nbLoops,nbth,needMarkers,good);
646

647
	if (!shared)
648
		for (unsigned int i = 0; i < 2*nbth; ++i)
649 650
			delete funcs[i];
}
651 652


653
} // namespace Parallel
654

655
} // namespace Algo
656

657
} // namespace CGoGN