Feature Selection Toolbox FST4
UTIA · Czech Academy of Sciences
Loading...
Searching...
No Matches
candidate_evaluator_threaded.hpp
Go to the documentation of this file.
1#ifndef FSTCANDIDATEEVALUATORTHREADED_H
2#define FSTCANDIDATEEVALUATORTHREADED_H
3 /*
27=========================================================================
28Copyright and license:
29 * FST4 software (with the exception of any bundled or externally linked
30 third-party components) is Copyright (C) 2000-present Institute of
31 Information Theory and Automation of the Czech Academy of Sciences (UTIA).
32 * FST4 is free software: you can redistribute it and/or modify it under
33 the terms of the GNU Affero General Public License as published by the
34 Free Software Foundation, version 3. The full text of the license is in
35 the file LICENSE in the distribution root and at
36 https://www.gnu.org/licenses/agpl-3.0.html.
37 * FST4 is distributed in the hope that it will be useful, but WITHOUT ANY
38 WARRANTY; without even the implied warranty of MERCHANTABILITY or
39 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
40 License for more details.
41 * Closed-source use without the AGPL's copyleft obligations requires a
42 commercial license from UTIA; in case of interest write to
43 utia@utia.cas.cz. LICENSING.md in the distribution root explains both
44 options, the contributor license agreement that every contribution
45 requires, and the contacts (technical questions and pull requests:
46 somol@utia.cas.cz, with "FST" in the subject line).
47========================================================================== */
48
49#include <thread>
50#include <mutex>
51#include <condition_variable>
52#include <functional>
53#include <memory>
54#include <cassert>
55#include <iostream>
56#include <list>
57#include <vector>
58#include <cstddef>
59#include <atomic>
60#include <exception>
61#include "error.hpp"
62#include "global.hpp"
63#include "thread_pool.hpp"
64#include "result_tracker.hpp"
66
67/*============== Template parameter type naming conventions ==============
68--------- Numeric types: -------------------------------------------------
69DATATYPE - data sample values - usually real numbers (but may be integers
70 in text processing etc.)
71REALTYPE - must be real numbers - for representing intermediate results of
72 calculations like mean, covariance etc.
73IDXTYPE - index values for enumeration of data samples - (nonnegative) integers,
74 extent depends on numbers of samples in data
75DIMTYPE - index values for enumeration of features (dimensions), or classes (not
76 class sizes) - (nonnegative) integers, usually lower extent than IDXTYPE,
77 but be aware of expressions like _classes*_features*_features !
78 in linearized representations of feature matrices for all classes
79BINTYPE - feature selection marker type - represents ca. <10 different feature
80 states (selected, deselected, sel./desel. temporarily 1st nested loop, 2nd...)
81RETURNTYPE - criterion value: real value, but may be extended in future to support
82 multiple values
83--------- Class types: ---------------------------------------------------
84SUBSET - class of class type Subset
85CLASSIFIER - class implementing interface defined in abstract class Classifier
86EVALUATOR - class implementing interface defined in abstract class Sequential_Step
87DISTANCE - class implementing interface defined in abstract class Distance
88DATAACCESSOR - class implementing interface defined in abstract class Data_Accessor
89INTERVALCONTAINER - class of class type Data_Intervaller
90CONTAINER - STL container of class type Data_Interval
91========================================================================== */
92
93namespace FST {
94
100
155template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION>
156class Candidate_Evaluator_Threaded : public Candidate_Evaluator<RETURNTYPE,DIMTYPE,SUBSET,CRITERION> {
157public:
159 typedef std::shared_ptr<CRITERION> PCriterion;
160 typedef std::shared_ptr<SUBSET> PSubset;
161 typedef std::shared_ptr<Result_Tracker<RETURNTYPE,SUBSET> > PResultTracker;
162 typedef typename parent::Generator Generator;
166
168 explicit Candidate_Evaluator_Threaded(const unsigned int max_threads=2) : _max_threads(max_threads), initialized(false), working(false), tlocal(max_threads), _processed_candidates(0), _available_candidates(0), _completed_candidates(0), _pool_started(false), _pshutdown(false), _reading_result(0), _callbacks_fired(false), _batch_failed(false) {
169 assert(max_threads>0);
170 for(unsigned int i=0;i<_max_threads;i++) tlocal[i].status=CRIT_NONEXISTENT; // not available, not allocated
171 notify("Candidate_Evaluator_Threaded constructor."); // deliberately thread-free: the persistent batch pool starts lazily at first initialize()
172 }
173
174 virtual unsigned int get_max_threads() const override {return _max_threads;}
175 virtual bool is_threaded() const override {return true;}
177 if(_pool_started) { // park-protocol shutdown: wake everyone, workers drain any pending work, then exit
178 {
179 std::unique_lock<std::mutex> lock(mutex_candidate_getter);
180 _pshutdown=true;
181 }
182 condition_available.notify_all();
183 for(std::thread &w : _pworkers) if(w.joinable()) w.join();
184 }
185 notify("Candidate_Evaluator_Threaded destructor.");
186 }
187
192 virtual void initialize(const PCriterion &crit, const DIMTYPE max_candidates, ResultCallback on_result=ResultCallback()) override;
193 virtual bool add_candidate(const PSubset &sub, const DIMTYPE feature=0) override;
194
195 // the two counters below are read without taking the mutex: they are progress indicators only,
196 // and a value read while workers are running may already be out of date when it is returned
197
198 // any of the following readers first waits the threads to finish the complete job. Further calls to add_candidate will be treated as part of a new job
199 virtual bool collect() override;
200 virtual bool getBestResult(RETURNTYPE &result, PSubset &sub, DIMTYPE &feature) override;
201 bool getFirstResult(RETURNTYPE &result, PSubset &sub, DIMTYPE &feature);
202 bool getNextResult(RETURNTYPE &result, PSubset &sub, DIMTYPE &feature);
203
204 bool results_to_tracker(const PResultTracker tracker);
205
206 // NOTE: crit is expected to be already initialize()d before use here
207 virtual bool evaluate_stream(Generator generator, const PCriterion &crit, const PResultTracker tracker, BetterPredicate better, BestCallback on_best, RETURNTYPE &result, const PSubset bestsub) override;
208 virtual void invalidate_clones() override;
209
210 virtual std::ostream& print(std::ostream& os) const override {os << "Candidate_Evaluator_Threaded(max_threads="<<_max_threads<<")"; return os;}
211protected:
212 const unsigned int _max_threads;
215 bool working;
216
222 std::vector<ThreadLocal> tlocal;
223
231 std::vector<std::thread> _pworkers;
232 void pooled_worker(unsigned int tidx);
238
240 std::condition_variable condition_available;
241 std::condition_variable condition_batch_done;
242
244 typedef struct {
245 RETURNTYPE result;
247 DIMTYPE feature;
249 std::vector<CandidateResult> cres;
250
252
255 void finish_batch();
256
257 std::atomic<bool> _batch_failed;
258 std::exception_ptr _worker_exception;
260 void record_worker_exception() {std::unique_lock<std::mutex> lock(mutex_worker_exception); if(!_worker_exception) _worker_exception=std::current_exception();}
261
268 enum StreamSlotState {SLOT_EMPTY=0, SLOT_PENDING, SLOT_RUNNING, SLOT_DONE, SLOT_FAILED};
269 void stream_worker(unsigned int tidx);
270 std::vector<PSubset> _sslot_sub;
271 std::vector<RETURNTYPE> _sslot_val;
272 std::vector<unsigned char> _sslot_state;
273 std::list<std::size_t> _spending;
274 std::mutex mutex_stream;
275 std::condition_variable _scv_work;
276 std::condition_variable _scv_done;
277 bool _sstop;
278};
279
280template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION>
282{ // max_candidates sizes the result storage up front: it must be at least the number of
283 // candidates this batch will receive, so that add_candidate() never reallocates cres
284 // while workers hold references into it
285 assert(crit);
286 assert(max_candidates>0);
287 _on_result=on_result;
288 _callbacks_fired=false;
289
290 if(working) { // initialize() called while a batch is still open: outstanding evaluations
291 // are allowed to complete (workers drain the queue), their results are
292 // discarded and their callbacks never fire
293 std::unique_lock<std::mutex> lock(mutex_candidate_getter);
295 working=false;
296 }
297 // workers are parked here (nothing pending), so the master may safely reset
298 // counters, resize cres and touch the clone array
303 working=true;
304 _batch_failed=false;
305 _worker_exception=nullptr;
306
307 if(!initialized || crit!=template_crit) {// criterion template changed (or first use): invalidate the per-thread clone array
308 template_crit=crit;
309 for(unsigned int i=0;i<_max_threads;i++) {tlocal[i].crit.reset(); tlocal[i].status=CRIT_NONEXISTENT;} // 0=not available, not allocated
310 }
311 // master-side clone creation for ALL workers: the persistent pool decouples worker
312 // index from candidate index, so there is no way to tell in add_candidate() which
313 // workers will run and clone lazily for those only. Clones are cached across batches
314 // via status while the criterion pointer is unchanged, so this pays only at first use
315 // or on a criterion change (see the staleness warning in the class documentation)
316 for(unsigned int i=0;i<_max_threads;i++) if(tlocal[i].status==CRIT_NONEXISTENT) {tlocal[i].crit=clone_unique(*template_crit); tlocal[i].status=CRIT_READY;}
317 if((DIMTYPE)(cres.size())<max_candidates) cres.resize(max_candidates);
318
319 if(!_pool_started) { // lazy spawn, ONCE per engine lifetime; workers park between batches
320 _pshutdown=false;
321 _pworkers.reserve(_max_threads);
322 for(unsigned int i=0;i<_max_threads;i++) _pworkers.emplace_back(&Candidate_Evaluator_Threaded::pooled_worker,this,i);
323 _pool_started=true;
324 }
325
326 initialized=true;
327}
328
329template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION>
331{ // the persistent worker loop: parks on condition_available between batches, exits only
332 // at destructor shutdown (draining any pending candidates first). Result visibility to
333 // the master follows from the mutex acquire/release around the completion counter.
334 assert(tidx<_max_threads);
335 for(;;)
336 {
337 DIMTYPE idx;
338 {
339 std::unique_lock<std::mutex> lock(mutex_candidate_getter);
341 if(_processed_candidates==_available_candidates) return; // shutdown and nothing pending
343 }
344 if(!_batch_failed) { // after a failure the rest of the batch drains unevaluated
345 CandidateResult &cr=cres[idx];
346 assert(tlocal[tidx].crit); // clones exist for every worker since initialize()
347 bool ok=false;
348 try {
349 ok=tlocal[tidx].crit->evaluate(cr.result,cr.sub);
350 } catch(...) {
351 record_worker_exception(); // rethrown on the master in finish_batch(); escaping the worker would std::terminate
352 }
353 if(!ok) _batch_failed=true; // the sequential engine's _failed semantics
354 }
355 {
356 std::unique_lock<std::mutex> lock(mutex_candidate_getter);
358 }
359 condition_batch_done.notify_one(); // the master is the only waiter
360 }
361}
362
363template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION>
365{
366 assert(initialized);
367 assert(sub);
368 assert(sub->get_d()>0);
369
370 DIMTYPE local_available;
371 {
372 std::unique_lock<std::mutex> lock(mutex_candidate_getter);
373 if(!working) {//start with new group of candidates (implicit re-arm after collect();
374 //clones and the persistent pool are guaranteed by the last initialize())
378 working=true;
379 _callbacks_fired=false;
380 _batch_failed=false;
381 _worker_exception=nullptr;
382 }
383
384 if(_available_candidates>=cres.size()) return false;
385 local_available = _available_candidates;
386 }
387
388 if(!cres[local_available].sub || cres[local_available].sub->get_n()!=sub->get_n()) cres[local_available].sub.reset(new SUBSET(sub->get_n()));
389 cres[local_available].sub->stateless_copy(*sub);
390 cres[local_available].feature=feature;
391
392 {
393 std::unique_lock<std::mutex> lock(mutex_candidate_getter);
395 }
396 condition_available.notify_one(); // wakes one parked persistent worker (busy workers re-check the counter anyway)
397 return true;
398}
399
400template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION>
402{// waits for the batch to complete (the persistent workers park themselves once the queue
403 // drains - no join), then fires pending per-result callbacks (once, in add-order). The
404 // mutexed completion-counter wait is what makes the workers' writes to cres[] visible
405 // here: every worker releases the mutex after incrementing the counter, the master
406 // acquires it before reading
407 if(working) {
408 {
409 std::unique_lock<std::mutex> lock(mutex_candidate_getter);
411 }
412 working=false;
413 }
414 if(_worker_exception) { // rethrow a worker-thread criterion exception on the master (fst4 main catches it like any sequential failure)
415 std::exception_ptr e=_worker_exception;
416 _worker_exception=nullptr;
417 _callbacks_fired=true; // a failed batch must not fire per-result callbacks
418 std::rethrow_exception(e);
419 }
420 if(!_callbacks_fired) {
421 _callbacks_fired=true;
422 if(_on_result && !_batch_failed) for(DIMTYPE i=0;i<_available_candidates;i++) _on_result(cres[i].result,cres[i].sub,cres[i].feature);
423 }
424}
425
426template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION>
432
433template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION>
435{// returns whichever candidate yielded the highest criterion value (waits for all threads to
436 // finish). The strict > below means that among equal values the EARLIEST added candidate wins
437 finish_batch();
438 if(_available_candidates==0 || _batch_failed) return false;
439 DIMTYPE bestidx=0;
440 for(DIMTYPE i=1;i<_available_candidates;i++) if(cres[i].result>cres[bestidx].result) bestidx=i;
441 sub=cres[bestidx].sub;
442 result=cres[bestidx].result;
443 feature=cres[bestidx].feature;
444 return true;
445}
446
447template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION>
449{// starts iteration over ALL results of the batch in ADD-ORDER, returning the first one
450 // (waits for all threads to finish); use getBestResult() to get the best-valued candidate
451 if(_available_candidates==0 || cres.size()==0) return false;
452 finish_batch();
453 if(_batch_failed) return false;
455 sub=cres[_reading_result].sub;
456 result=cres[_reading_result].result;
457 feature=cres[_reading_result].feature;
459 return true;
460}
461
462template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION>
464{// returns the next result in ADD-ORDER, false once the batch is exhausted (getFirstResult()
465 // has already waited for the threads, so no wait is needed here)
467 assert(_reading_result<cres.size());
468 sub=cres[_reading_result].sub;
469 result=cres[_reading_result].result;
470 feature=cres[_reading_result].feature;
472 return true;
473}
474
475template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION>
477{
478 if(!tracker || _available_candidates==0) return false;
479 finish_batch();
480 if(_batch_failed) return false; // sequential semantics: a failed batch tracks nothing
481 for(DIMTYPE i=0;i<_available_candidates;i++) tracker->add(cres[i].result,cres[i].sub);
482 return true;
483}
484
485template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION>
487{
488 assert(tidx<_max_threads);
489 assert(tlocal[tidx].crit);
490 PCriterion crit=tlocal[tidx].crit;
491 for(;;)
492 {
493 std::size_t idx;
494 {
495 std::unique_lock<std::mutex> lock(mutex_stream);
496 _scv_work.wait(lock, [this]{return _sstop || !_spending.empty();});
497 if(_spending.empty()) return; // _sstop set and no work left
498 idx=_spending.front(); _spending.pop_front();
499 assert(_sslot_state[idx]==SLOT_PENDING);
500 _sslot_state[idx]=SLOT_RUNNING;
501 }
502 RETURNTYPE val=RETURNTYPE();
503 bool ok=false;
504 try {
505 ok=crit->evaluate(val,_sslot_sub[idx]); // concurrent part: criterion clone on the master-generated candidate
506 } catch(...) {
507 record_worker_exception(); // slot goes SLOT_FAILED below; the master rethrows after join
508 }
509 {
510 std::unique_lock<std::mutex> lock(mutex_stream);
511 _sslot_val[idx]=val;
512 _sslot_state[idx]= ok? SLOT_DONE : SLOT_FAILED;
513 }
514 _scv_done.notify_one();
515 }
516}
517
518template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION>
519bool Candidate_Evaluator_Threaded<RETURNTYPE,DIMTYPE,SUBSET,CRITERION>::evaluate_stream(Generator generator, const PCriterion &crit, const PResultTracker tracker, BetterPredicate better, BestCallback on_best, RETURNTYPE &result, const PSubset bestsub)
520{
521 assert(generator);
522 assert(crit);
523 assert(better);
524 assert(bestsub);
525 assert(bestsub->get_n()>0);
526 assert(!working); // batch and streaming modes must not overlap
527 const DIMTYPE n=bestsub->get_n();
528
529 // (re)create criterion clones only if the master criterion object changed - existing
530 // clones are reused otherwise (see the criterion-clone warning in the class documentation)
531 if(crit!=template_crit) {
532 for(unsigned int i=0;i<_max_threads;i++) {tlocal[i].crit.reset(); tlocal[i].status=CRIT_NONEXISTENT;}
533 template_crit=crit;
534 }
535 for(unsigned int i=0;i<_max_threads;i++) if(tlocal[i].status==CRIT_NONEXISTENT) {tlocal[i].crit=clone_unique(*template_crit); tlocal[i].status=CRIT_READY;}
536
537 // ordered streaming (see the class note): the master generates candidates into a
538 // bounded ring and replays completed slots strictly in generation order, so the
539 // results equal those of the sequential engine at the same seed, for any thread count
540 const std::size_t W=4*(std::size_t)_max_threads; // ring capacity: enough to keep workers fed while the master replays
541 if(_sslot_sub.size()<W) _sslot_sub.resize(W);
542 for(std::size_t i=0;i<W;i++) if(!_sslot_sub[i] || _sslot_sub[i]->get_n()!=n) _sslot_sub[i].reset(new SUBSET(n));
543 _sslot_val.assign(W,RETURNTYPE());
544 _sslot_state.assign(W,(unsigned char)SLOT_EMPTY);
545 _spending.clear();
546 _sstop=false;
547 _worker_exception=nullptr;
548
550 for(unsigned int i=0;i<_max_threads;i++) {
551 Candidate_Evaluator_Threaded *self=this; unsigned int tidx=i;
552 stp.go([self,tidx]{self->stream_worker(tidx);});
553 }
554
555 bool best_available=false;
556 bool failed=false;
557 bool gen_done=false;
558 // 64-bit on every platform ON PURPOSE: Windows is LLP64 (unsigned long is 32 bits there),
559 // and head/tail index the ring by head%W — a wrap would desynchronize the slot mapping
560 // whenever W is not a power of two, silently corrupting the ordered replay
561 unsigned long long head=0, tail=0; // generation-ordered window of in-flight slots: [head,tail)
562 unsigned long long ordinal=0; // 0-based generation ordinal of the slot being replayed
563 for(;;) {
564 // fill phase: generate on the MASTER thread (master RNG stream) while the ring has room
565 while(!gen_done && tail-head<W) {
566 const std::size_t t=(std::size_t)(tail%W);
567 assert(_sslot_state[t]==SLOT_EMPTY);
568 if(!generator(*_sslot_sub[t])) {gen_done=true; break;}
569 {
570 std::unique_lock<std::mutex> lock(mutex_stream);
571 _sslot_state[t]=SLOT_PENDING;
572 _spending.push_back(t);
573 }
574 ++tail;
575 _scv_work.notify_one();
576 }
577 if(head==tail) break; // gen_done and everything replayed
578 // reap phase: wait for the OLDEST slot, then replay it in generation order
579 const std::size_t h=(std::size_t)(head%W);
580 {
581 std::unique_lock<std::mutex> lock(mutex_stream);
582 _scv_done.wait(lock, [this,h]{return _sslot_state[h]==SLOT_DONE || _sslot_state[h]==SLOT_FAILED;});
583 if(_sslot_state[h]==SLOT_FAILED) {failed=true;}
584 }
585 if(failed) break; // sequential semantics: candidates after a failed evaluation are neither tracked nor reported
586 const RETURNTYPE val=_sslot_val[h];
587 const PSubset &cand=_sslot_sub[h];
588 if(tracker) tracker->add(val,cand);
589 if(!best_available || better(val,cand,result,bestsub)) {
590 result=val;
591 bestsub->stateless_copy(*cand);
592 best_available=true;
593 if(on_best) on_best(-1,ordinal,val,cand);
594 }
595 ++ordinal;
596 {
597 std::unique_lock<std::mutex> lock(mutex_stream);
598 _sslot_state[h]=SLOT_EMPTY;
599 }
600 ++head;
601 }
602
603 // shutdown: wake all workers; on failure drop undispatched work first
604 {
605 std::unique_lock<std::mutex> lock(mutex_stream);
606 if(failed) _spending.clear();
607 _sstop=true;
608 }
609 _scv_work.notify_all();
610 stp.join_all();
611
612 if(_worker_exception) { // rethrow a worker-thread criterion exception on the master (fst4 main catches it like any sequential failure)
613 std::exception_ptr e=_worker_exception;
614 _worker_exception=nullptr;
615 std::rethrow_exception(e);
616 }
617 if(failed) return false;
618 return best_available;
619}
620
621template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION>
623{
624 if(working) { // a batch is still open: let its outstanding evaluations drain first, as
625 // initialize() does; the workers are parked afterwards
626 std::unique_lock<std::mutex> lock(mutex_candidate_getter);
628 }
629 // the workers are parked, so the master may touch the clone array; the next initialize()
630 // or evaluate_stream() finds every slot CRIT_NONEXISTENT and re-clones from the criterion
631 // it is handed - in that criterion's CURRENT context
632 for(unsigned int i=0;i<_max_threads;i++) {tlocal[i].crit.reset(); tlocal[i].status=CRIT_NONEXISTENT;}
633}
634
635} // namespace
636#endif // FSTCANDIDATEEVALUATORTHREADED_H ///:~
Abstract candidate-evaluation strategy: the seam through which one search algorithm body evaluates ca...
Implements concurrent evaluation of a criterion on a set of subset candidates.
Definition candidate_evaluator_threaded.hpp:156
std::condition_variable condition_available
wakes parked workers: new candidate available (or shutdown)
Definition candidate_evaluator_threaded.hpp:240
void finish_batch()
stops and joins workers, then fires pending per-result callbacks (once per batch)
Definition candidate_evaluator_threaded.hpp:401
std::list< std::size_t > _spending
pending slot indices in generation order
Definition candidate_evaluator_threaded.hpp:273
bool _pshutdown
destructor flag: workers drain pending work and exit
Definition candidate_evaluator_threaded.hpp:237
std::shared_ptr< SUBSET > PSubset
shared pointer to a candidate feature subset
Definition candidate_evaluator_threaded.hpp:160
parent::ResultCallback ResultCallback
see Candidate_Evaluator::ResultCallback
Definition candidate_evaluator_threaded.hpp:163
DIMTYPE _completed_candidates
candidates whose result is stored - batch done when it reaches _available_candidates
Definition candidate_evaluator_threaded.hpp:235
std::mutex mutex_candidate_getter
guards the candidate counters (workers claim/complete, master adds/waits)
Definition candidate_evaluator_threaded.hpp:239
PCriterion template_crit
to detect change of employed criterion inbetween search() calls
Definition candidate_evaluator_threaded.hpp:213
DIMTYPE _reading_result
iterating index for get*Result readers
Definition candidate_evaluator_threaded.hpp:251
void stream_worker(unsigned int tidx)
claims pending slots, evaluates with the worker's criterion clone
Definition candidate_evaluator_threaded.hpp:486
std::atomic< bool > _batch_failed
batch-mode twin of the sequential engine's _failed: a worker saw evaluate()==false
Definition candidate_evaluator_threaded.hpp:257
bool _sstop
shutdown flag for stream workers
Definition candidate_evaluator_threaded.hpp:277
virtual bool collect() override
completes the batch: waits for all evaluations and fires pending per-result callbacks in add-order
Definition candidate_evaluator_threaded.hpp:427
virtual std::ostream & print(std::ostream &os) const override
writes a human-readable identification of the engine; base default writes nothing
Definition candidate_evaluator_threaded.hpp:210
std::vector< ThreadLocal > tlocal
per-worker criterion clone + status, one entry per worker index; sized once in the constructor,...
Definition candidate_evaluator_threaded.hpp:222
std::condition_variable condition_batch_done
wakes the master: a candidate completed (finish_batch waits on the counter)
Definition candidate_evaluator_threaded.hpp:241
bool working
true while a batch is open (between initialize()/add_candidate() and finish_batch())
Definition candidate_evaluator_threaded.hpp:215
virtual bool is_threaded() const override
true for the threaded engine (any --threads value), false for the sequential one – distinguishes the ...
Definition candidate_evaluator_threaded.hpp:175
virtual bool add_candidate(const PSubset &sub, const DIMTYPE feature=0) override
adds candidate subset to the evaluation pool; returns false once the result storage reserved by initi...
Definition candidate_evaluator_threaded.hpp:364
std::mutex mutex_stream
guards the ring bookkeeping
Definition candidate_evaluator_threaded.hpp:274
bool initialized
true after the first initialize() call (guards clone (re)creation logic)
Definition candidate_evaluator_threaded.hpp:214
std::condition_variable _scv_work
wakes workers on new pending slots / shutdown
Definition candidate_evaluator_threaded.hpp:275
bool results_to_tracker(const PResultTracker tracker)
completes the batch and feeds every candidate's (value,subset) into tracker in add-order; false if th...
Definition candidate_evaluator_threaded.hpp:476
std::condition_variable _scv_done
wakes the master when a slot completes
Definition candidate_evaluator_threaded.hpp:276
parent::BestCallback BestCallback
see Candidate_Evaluator::BestCallback
Definition candidate_evaluator_threaded.hpp:164
std::exception_ptr _worker_exception
first exception a criterion threw on a worker; rethrown on the master after join
Definition candidate_evaluator_threaded.hpp:258
std::mutex mutex_worker_exception
guards _worker_exception (shared by batch and streaming workers; the modes never overlap)
Definition candidate_evaluator_threaded.hpp:259
Candidate_Evaluator< RETURNTYPE, DIMTYPE, SUBSET, CRITERION > parent
the abstract Candidate_Evaluator interface this class implements
Definition candidate_evaluator_threaded.hpp:158
ResultCallback _on_result
optional per-candidate callback, fired in add-order when the batch completes
Definition candidate_evaluator_threaded.hpp:253
Candidate_Evaluator_Threaded(const unsigned int max_threads=2)
Definition candidate_evaluator_threaded.hpp:168
parent::BetterPredicate BetterPredicate
see Candidate_Evaluator::BetterPredicate
Definition candidate_evaluator_threaded.hpp:165
virtual void initialize(const PCriterion &crit, const DIMTYPE max_candidates, ResultCallback on_result=ResultCallback()) override
Opens a new batch to be evaluated against crit, which must already be initialize()d....
Definition candidate_evaluator_threaded.hpp:281
const unsigned int _max_threads
size of the persistent worker pool, as given to the constructor
Definition candidate_evaluator_threaded.hpp:212
std::vector< CandidateResult > cres
one entry per batch candidate, indexed by add-order (the position add_candidate() assigned it)
Definition candidate_evaluator_threaded.hpp:249
virtual bool getBestResult(RETURNTYPE &result, PSubset &sub, DIMTYPE &feature) override
completes the batch, then returns (without copying sub contents) the candidate that yielded the highe...
Definition candidate_evaluator_threaded.hpp:434
std::vector< std::thread > _pworkers
Definition candidate_evaluator_threaded.hpp:231
parent::Generator Generator
see Candidate_Evaluator::Generator
Definition candidate_evaluator_threaded.hpp:162
DIMTYPE _available_candidates
to sychronize workers with adding new candidates to cres[]
Definition candidate_evaluator_threaded.hpp:234
void record_worker_exception()
captures the currently-handled exception (first one wins) for rethrow on the master by finish_batch()...
Definition candidate_evaluator_threaded.hpp:260
bool getNextResult(RETURNTYPE &result, PSubset &sub, DIMTYPE &feature)
advances the getFirstResult() iteration by one result; false once the batch is exhausted
Definition candidate_evaluator_threaded.hpp:463
std::shared_ptr< CRITERION > PCriterion
shared pointer to the criterion this engine evaluates candidates against
Definition candidate_evaluator_threaded.hpp:159
virtual void invalidate_clones() override
drops the cached per-worker criterion clones (see Candidate_Evaluator::invalidate_clones()); the next...
Definition candidate_evaluator_threaded.hpp:622
DIMTYPE _processed_candidates
candidates with finished processing, or being currently processed
Definition candidate_evaluator_threaded.hpp:233
StreamSlotState
Definition candidate_evaluator_threaded.hpp:268
void pooled_worker(unsigned int tidx)
the persistent worker loop: park, claim, evaluate, count, repeat
Definition candidate_evaluator_threaded.hpp:330
virtual unsigned int get_max_threads() const override
1 in the sequential implementation
Definition candidate_evaluator_threaded.hpp:174
bool _pool_started
the persistent pool exists (lazy spawn at first initialize())
Definition candidate_evaluator_threaded.hpp:236
std::shared_ptr< Result_Tracker< RETURNTYPE, SUBSET > > PResultTracker
shared pointer to the result tracker fed during evaluate_stream()
Definition candidate_evaluator_threaded.hpp:161
std::vector< PSubset > _sslot_sub
ring: master-generated candidates
Definition candidate_evaluator_threaded.hpp:270
std::vector< RETURNTYPE > _sslot_val
ring: evaluation results
Definition candidate_evaluator_threaded.hpp:271
std::vector< unsigned char > _sslot_state
ring: StreamSlotState per slot
Definition candidate_evaluator_threaded.hpp:272
bool _callbacks_fired
ensures _on_result fires exactly once per batch
Definition candidate_evaluator_threaded.hpp:254
virtual bool evaluate_stream(Generator generator, const PCriterion &crit, const PResultTracker tracker, BetterPredicate better, BestCallback on_best, RETURNTYPE &result, const PSubset bestsub) override
streaming mode: the master thread calls generator until exhaustion, workers evaluate concurrently,...
Definition candidate_evaluator_threaded.hpp:519
bool getFirstResult(RETURNTYPE &result, PSubset &sub, DIMTYPE &feature)
completes the batch and starts iteration over ALL of its results in ADD-ORDER (not by value); returns...
Definition candidate_evaluator_threaded.hpp:448
Abstract candidate-evaluation strategy (sequential-inline vs. thread-pooled).
Definition candidate_evaluator.hpp:86
std::function< void(const RETURNTYPE, const PSubset &, const DIMTYPE)> ResultCallback
fired once per evaluated batch candidate: (criterion value, candidate, feature tag as passed to add_c...
Definition candidate_evaluator.hpp:94
std::function< bool(SUBSET &)> Generator
fills sub with the next candidate, returns false when no candidate remains (in the threaded implement...
Definition candidate_evaluator.hpp:92
std::function< bool(const RETURNTYPE, const PSubset &, const RETURNTYPE, const PSubset &)> BetterPredicate
returns true if (value,subset) is better than (best value,best subset) - encodes per-search tie-break...
Definition candidate_evaluator.hpp:99
std::function< void(const int, const unsigned long long, const RETURNTYPE, const PSubset &)> BestCallback
Definition candidate_evaluator.hpp:97
Implements thread scheduler that assigns jobs up to maximum number of threads allowed.
Definition thread_pool.hpp:73
unsigned int go(const std::function< void()> &threadfunc)
Launches threadfunc in a free pool slot, blocking the caller until one becomes available if the pool ...
Definition thread_pool.hpp:94
void join_all()
Joins every currently running worker thread and resets the pool to empty (all slots free)....
Definition thread_pool.hpp:126
FST4 specific error passing class.
Global definitions.
The Feature Selection Toolbox 4 library: every library component – data access, criteria and classifi...
Definition data_accessor.hpp:80
void notify(const std::string msg)
Prints msg followed by a newline via syncout::print(); a no-op unless the preprocessor macro DEBUG is...
Definition global.hpp:112
CriterionStatus
Lifecycle state of a worker's thread-local criterion clone (see Candidate_Evaluator_Threaded::ThreadL...
Definition candidate_evaluator_threaded.hpp:96
@ CRIT_READY
a clone exists and is not currently evaluating
Definition candidate_evaluator_threaded.hpp:98
@ CRIT_NONEXISTENT
no clone allocated yet for this worker slot
Definition candidate_evaluator_threaded.hpp:97
std::unique_ptr< T > clone_unique(const T &src)
Definition clonable.hpp:234
Defines interface for classes that enable collecting multiple results.
temporary storage of subset candidate evaluation results
Definition candidate_evaluator_threaded.hpp:244
DIMTYPE feature
the optional feature tag passed to add_candidate()
Definition candidate_evaluator_threaded.hpp:247
PSubset sub
the candidate subset (owned copy, not shared with the caller's subset)
Definition candidate_evaluator_threaded.hpp:246
RETURNTYPE result
the criterion value obtained for sub
Definition candidate_evaluator_threaded.hpp:245
thread-local storage of current subset candidate, criterion clone, and tracker clone
Definition candidate_evaluator_threaded.hpp:218
CriterionStatus status
CRIT_NONEXISTENT or CRIT_READY (see CriterionStatus).
Definition candidate_evaluator_threaded.hpp:220
PCriterion crit
this worker's private clone of the criterion (created lazily, reused across batches while the criteri...
Definition candidate_evaluator_threaded.hpp:219
Implements thread scheduler that assigns jobs up to maximum number of threads allowed.
Copyright © 2000–present ÚTIA, Institute of Information Theory and Automation of the Czech Academy of Sciences · Contacts · This site sets no cookies and uses no analytics.