Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
313 views
in Technique[技术] by (71.8m points)

c++ - std::bad_alloc exception while using multithreading

I am writing a genetic algorithm (in C++) to tune some evaluation parameters for my chess engine, but since it takes a rather long time to find the fitness of each individual I want to make it multithreaded.

The fitness function is evaluated using some grandmaster games with a maximum of five of the best moves given for the position. If my chess engine with the parameters from the current chromosome finds one of these moves, its fitness function is incremented.

But the problem is that after making it multithreaded, it is using way more RAM than expected even though it is only using two threads. The code for searching one position is:

void SearchWac::search_position() { // sizeof(S_BOARD) = 73952
S_BOARD* pos = nullptr;
try {
    pos = new S_BOARD();
    BoardRep::parseFen(position_fen, *pos);
}
catch (std::bad_alloc& ba) {
    std::cout << "Exception occured when allocating heap for S_BOARD* pos: " << ba.what() << std::endl;
}


S_SEARCHINFO info;
passed = false; // By default.

// Now we'll parse the go-command using the UCI interface, but firstly we need to convert the std::string to a char* (not const)
char* goLine = new char[search_parameter.size() + 1];
std::copy(search_parameter.begin(), search_parameter.end(), goLine);
goLine[search_parameter.size()] = '';

ParseGo(goLine, &info, pos);

// goLine is declared on heap, so we'll need to delete it manually.
delete[] goLine;


// Now we can search the posiiton:
Search::searchPosition(pos, &info);

// Now we can check if the move found by the engine matches any of the ones in the WAC-test.
int bestMove = NOMOVE;

try {
    bestMove = pos->pvArray[0]; // Get the best move that the engine suggested.

    if (bestMove == NOMOVE) {
        throw 1;
    }
}
catch (int e) {
    std::cout << "Exception occured in SearcWac::search_position(): Copper didn't return any move." << std::endl;
}

for (int m = 0; m < 5; m++) {
    // Check if the move matches any of the good moves:
    if (printMove(bestMove) == best_moves[m]) { // The engine found a good move
        passed = true;
        break;
    }
}
delete pos;
}

Which is the function run by one thread. The SearchWac object gets an FEN, a UCI-command to specify how to search the position and a list of std::string for the best moves as its constructor.

The function for finding all fitness values in the population is:

/*
Helper function for running functions inside objects with multithreading
*/
void doRun(SearchWac* instance) {
    instance->search_position();
}


/*
The fitness function is simply the amount of WAC positions that the engine gets right with each 
chromosome's values.
*/

void Generation::get_generation_fitness() {

int passed;
generation_fitness = 0;

for (int c = 0; c < population_count; c++) {
    passed = 0;
    /*
    Before testing the engine with the values given from the current chromosome, we'll have to insert them.
    */
    for (int v = 0; v < population[c].genes.size(); v++) {
        *(tuning_parameters[v]->variable) = population[c].genes[v];
    }

    /*
    Now we can run through all the WAC positions with the engine.
    */

    int total_positions = 0;

    std::vector<SearchWac> searches_runnning;
    std::vector<std::thread> threads;

    // Only using fifty positions right now to see if the algorithm works. Later this will get significantly increased
    while (total_positions < 50) {
        searches_runnning.clear();
        threads.clear();

        int this_position = total_positions;

        // Initialise SearchWac objects.
        for (int n = 0; n < WAC_THREADS; n++) {
            searches_runnning.push_back(SearchWac("go depth 1", WAC_positions[this_position + n], WAC_moves[this_position + n]));
        }

        this_position += WAC_THREADS;  // WAC_THREADS is set to 2.

        for (int t = 0; t < searches_runnning.size(); t++) {
            std::thread worker(doRun, &searches_runnning[t]);
            threads.push_back(std::move(worker));
        }

        // Wait for the searches to finish and check the search results.
        for (int t = 0; t < threads.size(); t++) {
            threads[t].join();
        }

        for (int s = 0; s < searches_runnning.size(); s++) {
            passed += (searches_runnning[s].passed_position()) ? 1 : 0;
        }

        total_positions = this_position;
    }
    
    // After running the WAC test, set the fitness of this chromosome and increment the total fitness by the same value.
    population[c].fitness = passed * passed;
    generation_fitness += passed * passed;
}

// After having gone through all the chromosomes and having gotten their fitness, calculate their selection probability. Given by prob = fitness/total_fitness

// If the totalt fitness is zero, they should all have the same probability.
if (generation_fitness == 0) {
    for (int c = 0; c < population_count; c++) {
        population[c].selection_probability = double(1) / double(population_count);
    }
}
else {

    for (int c = 0; c < population_count; c++) {
        population[c].selection_probability = double(population[c].fitness) / double(generation_fitness);
    }
}
}

The part where the multithreading is done is inside the while-loop.

Can anybody point out what I am doing wrong? I don't understand why i get std::bad_alloc errors when running two threads at a time and thus only two S_BOARD objects... It shouldn't be that much considering i usually have around 4GB of available RAM. Am I mistaken?

Any help is very much appreciated!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
等待大神答复

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...