Reputation: 57
I have a problem while using multiple threads in this code (this is a fraction of it):
template <typename Heuristic, typename Meta>
vector<double> kfold(Heuristic &heu,Meta &metaheu,mat &data,Col<int> &results,int k,int knearest,double pNeigh,double pCost,double pIni,bool st,bool units){
//Some stuff...
for (int j=0; j<k;j++){
mat auxMat = folds.slice(j);
Col<int> auxCol = res.col(j);
Instance auxI(auxU,pNeigh,pCost,&auxMat,&auxMat,&auxCol,&auxCol,un.n_rows);
threads1[j] = thread(searchInstanceStrat<Heuristic,Meta>,heu,metaheu,auxI,knearest,ref(tiempo),ref(reduction),j);
}
for (int b = 0; b<k; b++) threads1[b].join();
More stuff to do...
}
the function called with thread is:
template <typename Heuristic, typename Meta>
void searchInstanceStrat(Heuristic heu,Meta metaheu, Instance auxI, int knearest, vec &tiempo, vector<Instance> &reduction,int index){
auto start = chrono::high_resolution_clock::now();
pair<double,Instance> pairAux = heu.find(auxI,knearest);
//pair<double,Instance> pairAux = metaheu.find(pairAux2.second,knearest);
auto stop = chrono::high_resolution_clock::now();
using fpSeconds = chrono::duration<float,chrono::seconds::period>;
tiempo(index) = (double)(fpSeconds(stop - start).count());
reduction[index] = pairAux.second;
}
The heuristic class is:
template<typename MetricType>
struct CNN{
/*Perform CNN, constructs a reduced data set beginning from one instance and adding each instance not
classified correctly */
MetricType* metric;
CNN(MetricType* met):metric(met){}
pair<double,Instance> find(Instance &initial, int knearest){
bool flag = false, flag2 = false;
Instance current = initial;
int j = 0;
vector <int> indexes(initial.units.n_rows);
for (int i=0;i<initial.units.n_rows;i++) indexes[i] = i;
random_shuffle(indexes.begin(),indexes.end());
for (int i = 0; (i < initial.units.n_rows) && !flag; i++){
Knn knn(current.training,current.trainResults,current.unique);
flag2 = false;
while ((j < current.originalTraining->n_rows) && !flag2){
mat query(current.originalTraining->row(indexes[j]));
Col<int> prediction(knn.search(query,knearest,*metric));
if (prediction(0) != (*(current.originaltrainResults))(indexes[j])){
Col<int> nunits(current.units);
nunits(indexes[j]) = 1;
current.units = nunits;
current.changeTrainingSet();
flag2 = true;
}
j++;
}
if (!flag2) flag = true; //flag becomes true when every point is classified correctly
}
Knn knn(current.training,current.trainResults,current.unique);
double costResult = knn.score(*(current.originalTraining),knearest,*metric,*(current.originaltrainResults));
return make_pair(costResult,current);
}
};
The instance class is:
struct Instance{
Col <int> units;
double percenVecinity, percenCost;
mat* originalTraining;
mat* test;
Col<int>* originaltrainResults;
Col<int>* testResults;
mat training;
Col<int> trainResults;
int unique,totalInstances;
Instance(){}
Instance(Col<int> &u,double p1,double p2,mat* tr,mat* te,Col<int>* trr,Col<int>* ter,int un):
units(u),percenVecinity(p1),percenCost(p2),test(te),testResults(ter),unique(un),
originalTraining(tr),originaltrainResults(trr){
totalInstances = tr->n_rows;
int count = 0,index=0;
for (int i=0;i<u.n_rows;i++){ if (u(i)==1) count++; }
training.set_size(count,tr->n_cols);
trainResults.set_size(count);
for (int i=0;i<u.n_rows;i++){
if (u(i)==1){
training.row(index) = tr->row(i);
trainResults(index) = (*trr)(i);
index++;
}
}
}
}
There is also the Knn class and the metric class but I don't think they are important to the case.
The thing is when I call kfold and create the k threads so each one computes its thing in parallel there is a segmentation fault and it happens when in searchInstanceStrat the find function is called
pair<double,Instance> pairAux = heu.find(auxI,knearest);
But when I put the threads[j].join() after every thread creation (effectively making it serialized) The code works perfectly. The problem is then the concurrency. But I don't understand why, should not the thread() initialization copy every parameter not in ref so each thread has a working copy of the data passed to it?
It is true that the classes have pointers to other data, but if it is a copy, could it be the thing affecting my code? But if there is a copy per thread I don't see why my code is bad.
Thanks in advance for the help
Upvotes: 2
Views: 77
Reputation: 32732
The problem is with auxMat
and auxCol
. Pointers to these local variables are passed to your thread function using the Instance
class. These locals are destroyed at the end of the j
loop, right after the thread is created. When the thread tries to access them it could be accessing destroyed objects, partially constructed ones for the next thread, or objects that have been constructed for another thread.
You'll want to store the auxMat
and auxCol
values in Instance
, or possibly they could be const pointers into the original data in folds
and res
.
Upvotes: 2