MPI并行遗传算法代码np>1运行时出现Segmentation Fault求助
我正在开发基于MPI的并行遗传算法求解N皇后问题,当进程数np>1时程序出现Segmentation Fault(段错误),未完成执行就退出,附上错误截图,恳请帮忙排查解决。
核心代码
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <mpi.h> #include <limits.h> #define N 8 #define POPULATION_SIZE 100 #define MUTATION_RATE 0.3 #define MAX_GENERATIONS 50 typedef struct { int board[N]; int fitness; } Individual; int calculateFitness(int board[]) { int conflicts = 0; for (int i = 0; i < N; i++) { for (int j = i + 1; j < N; j++) { if (board[i] == board[j] || abs(board[i] - board[j]) == j - i) { conflicts++; } } } return conflicts; } void initializePopulation(Individual population[]) { for (int i = 0; i < POPULATION_SIZE; i++) { for (int j = 0; j < N; j++) { population[i].board[j] = rand() % N; } population[i].fitness = calculateFitness(population[i].board); // Calculate fitness for each individual } } void selection(Individual population[], Individual matingPool[]) { // Tournament selection for (int i = 0; i < POPULATION_SIZE; i++) { int idx1 = rand() % POPULATION_SIZE; int idx2 = rand() % POPULATION_SIZE; if (population[idx1].fitness < population[idx2].fitness) { matingPool[i] = population[idx1]; } else { matingPool[i] = population[idx2]; } } } void crossover(Individual population[], Individual matingPool[]) { // MPI_Barrier(MPI_COMM_WORLD); for (int i = 0; i < POPULATION_SIZE; i += 2) { int crossoverPoint = rand() % (N - 1) + 1; // Random crossover point for (int j = 0; j < crossoverPoint; j++) { population[i].board[j] = matingPool[i].board[j]; population[i + 1].board[j] = matingPool[i + 1].board[j]; } for (int j = crossoverPoint; j < N; j++) { population[i].board[j] = matingPool[i + 1].board[j]; population[i + 1].board[j] = matingPool[i].board[j]; } population[i].fitness = calculateFitness(population[i].board); // Recalculate fitness after crossover population[i + 1].fitness = calculateFitness(population[i + 1].board); MPI_Barrier(MPI_COMM_WORLD); } } void mutate(Individual population[]) { for (int i = 0; i < POPULATION_SIZE; i++) { if ((double)rand() / RAND_MAX < MUTATION_RATE) { int mutationPoint = rand() % N; // Random mutation point int newGene = rand() % N; // New random gene population[i].board[mutationPoint] = newGene; population[i].fitness = calculateFitness(population[i].board); // Recalculate fitness after mutation } } } Individual population[POPULATION_SIZE]; int main(int argc, char *argv[]) { MPI_Init(&argc, &argv); int rank, size; // MPI_Comm_rank(MPI_COMM_WORLD, &rank); // MPI_Comm_size(MPI_COMM_WORLD, &size); // int totalPopulationSize = 100; // Replace with the actual total population size int numProcesses; int remainder; MPI_Comm_size(MPI_COMM_WORLD, &size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); int chunkSize = POPULATION_SIZE / size; remainder = POPULATION_SIZE % size; if (rank < remainder) { chunkSize += 1; } printf("Process %d: Chunk size = %d\n", rank, chunkSize); srand(time(NULL) + rank); // Adjust the random seed for each process printf("rank : %d\n", rank); // Divide the population among processes // int chunkSize = POPULATION_SIZE / size; Individual population[POPULATION_SIZE]; // Declare the population array Individual localPopulation[chunkSize]; Individual localMatingPool[chunkSize]; MPI_Datatype individualType; int blocklengths[2] = {N, 1}; MPI_Datatype types[2] = {MPI_INT, MPI_INT}; MPI_Aint offsets[2]; offsets[0] = offsetof(Individual, board); offsets[1] = offsetof(Individual, fitness); MPI_Type_create_struct(2, blocklengths, offsets, types, &individualType); MPI_Type_commit(&individualType); if (rank == 0) { initializePopulation(population); } MPI_Scatter(population, chunkSize, individualType, localPopulation, chunkSize, individualType, 0, MPI_COMM_WORLD); for (int generation = 1; generation <= MAX_GENERATIONS; generation++) { // Perform selection, crossover, and mutation in parallel selection(localPopulation, localMatingPool); crossover(localPopulation, localMatingPool); mutate(localPopulation); // Synchronize results within each process MPI_Gather(localPopulation, chunkSize, individualType, population, chunkSize, individualType, 0, MPI_COMM_WORLD); // Exchange the best individuals among processes Individual bestLocalIndividual; int bestLocalFitness = INT_MAX; for (int i = 0; i < chunkSize; i++) { if (localPopulation[i].fitness < bestLocalFitness) { bestLocalFitness = localPopulation[i].fitness; bestLocalIndividual = localPopulation[i]; } } // Reduce the best fitness and individual across all MPI processes Individual bestGlobalIndividual; int bestGlobalFitness; MPI_Reduce(&bestLocalFitness, &bestGlobalFitness, 1, MPI_INT, MPI_MIN, 0, MPI_COMM_WORLD); MPI_Gather(&bestLocalIndividual, sizeof(Individual), MPI_BYTE, &bestGlobalIndividual, sizeof(Individual), MPI_BYTE, 0, MPI_COMM_WORLD); // Exchange the best individuals between processes if (rank == 0) { for (int i = 0; i < size; i++) { if (i != rank) { MPI_Send(&bestGlobalIndividual, 1, individualType, i, 0, MPI_COMM_WORLD); } } } else { MPI_Recv(&bestGlobalIndividual, 1, individualType, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); } // Synchronize generation number MPI_Bcast(&generation, 1, MPI_INT, 0, MPI_COMM_WORLD); // Print the current generation and fitness of the best individual if (rank == 0) { printf("Generation %d, Best Fitness: %d, Solution: ", generation, bestGlobalFitness); for (int j = 0; j < N; j++) { printf("%d ", bestGlobalIndividual.board[j]); } printf("\n"); if (bestGlobalFitness == 0) { printf("Solution found in generation %d\n", generation); break; } else if (generation == MAX_GENERATIONS) { printf("Solution exceeds %d generations\n", MAX_GENERATIONS); } } } MPI_Type_free(&individualType); MPI_Finalize(); return 0; }
错误截图

问题排查与修复方案
1. 全局变量与局部变量重定义冲突
代码中全局定义了Individual population[POPULATION_SIZE];,main函数内又定义了同名局部变量,导致MPI_Scatter/MPI_Gather时内存寻址混乱,直接触发段错误。
修复:删除全局的population数组定义,仅保留main函数内的局部变量。
2. 种群操作函数的数组越界
selection、crossover、mutate函数硬编码使用POPULATION_SIZE遍历数组,但每个进程实际处理的是chunkSize大小的局部种群(当进程数>1时,chunkSize < POPULATION_SIZE),必然访问超出局部数组的内存。
修复:给这三个函数添加popSize参数,传入实际处理的局部种群大小:
void selection(Individual population[], Individual matingPool[], int popSize) { for (int i = 0; i < popSize; i++) { int idx1 = rand() % popSize; int idx2 = rand() % popSize; if (population[idx1].fitness < population[idx2].fitness) { matingPool[i] = population[idx1]; } else { matingPool[i] = population[idx2]; } } } void crossover(Individual population[], Individual matingPool[], int popSize) { for (int i = 0; i < popSize - (popSize % 2); i += 2) { int crossoverPoint = rand() % (N - 1) + 1; for (int j = 0; j < crossoverPoint; j++) { population[i].board[j] = matingPool[i].board[j]; population[i + 1].board[j] = matingPool[i + 1].board[j]; } for (int j = crossoverPoint; j < N; j++) { population[i].board[j] = matingPool[i + 1].board[j]; population[i + 1].board[j] = matingPool[i].board[j]; } population[i].fitness = calculateFitness(population[i].board); population[i + 1].fitness = calculateFitness(population[i + 1].board); } // 处理种群数量为奇数的情况,保留最后一个个体不变 if (popSize % 2 != 0) { population[popSize-1] = matingPool[popSize-1]; } } void mutate(Individual population[], int popSize) { for (int i = 0; i < popSize; i++) { if ((double)rand() / RAND_MAX < MUTATION_RATE) { int mutationPoint = rand() % N; int newGene = rand() % N; population[i].board[mutationPoint] = newGene; population[i].fitness = calculateFitness(population[i].board); } } }
main函数中调用时传入chunkSize:
selection(localPopulation, localMatingPool, chunkSize); crossover(localPopulation, localMatingPool, chunkSize); mutate(localPopulation, chunkSize);
3. MPI_Gather收集最优个体的内存错误
原代码用MPI_Gather将所有进程的bestLocalIndividual收集到单个bestGlobalIndividual结构体中,内存空间不足导致越界。
修复:定义数组存储所有进程的最优个体,再从中筛选全局最优:
// 收集所有进程的最优个体 Individual bestGlobalIndividuals[size]; MPI_Gather(&bestLocalIndividual, 1, individualType, bestGlobalIndividuals, 1, individualType, 0, MPI_COMM_WORLD); // 进程0筛选全局最优 Individual bestGlobalIndividual; int bestGlobalFitness; if (rank == 0) { bestGlobalFitness = INT_MAX; for (int i = 0; i < size; i++) { if (bestGlobalIndividuals[i].fitness < bestGlobalFitness) { bestGlobalFitness = bestGlobalIndividuals[i].fitness; bestGlobalIndividual = bestGlobalIndividuals[i]; } } } // 广播全局最优个体给所有进程 MPI_Bcast(&bestGlobalFitness, 1, MPI_INT, 0, MPI_COMM_WORLD); MPI_Bcast(&bestGlobalIndividual, 1, individualType, 0, MPI_COMM_WORLD);
4. 不必要的MPI_Barrier与冗余MPI_Bcast
- crossover函数内的
MPI_Barrier放在循环中,会导致进程死锁,直接删除。 - 原代码中
MPI_Bcast(&generation, 1, MPI_INT, 0, MPI_COMM_WORLD)属于冗余操作,每个进程的generation是循环变量,无需同步,直接删除。
5. MPI_Scatter/MPI_Gather的参数一致性
当POPULATION_SIZE不能被进程数整除时,不同进程的chunkSize不同,原代码的MPI_Scatter/MPI_Gather使用统一的chunkSize参数会出错。
修复:使用MPI_Scatterv和MPI_Gatherv替代,根据进程rank分配不同的发送/接收计数:
// 准备Scatterv的参数 int sendcounts[size]; int displs[size]; int offset = 0; for (int i = 0; i < size; i++) { sendcounts[i] = (i < remainder) ? (POPULATION_SIZE / size + 1) : (POPULATION_SIZE / size); displs[i] = offset; offset += sendcounts[i]; } // 分发种群 MPI_Scatterv(population, sendcounts, displs, individualType, localPopulation, chunkSize, individualType, 0, MPI_COMM_WORLD); // 收集种群 MPI_Gatherv(localPopulation, chunkSize, individualType, population, sendcounts, displs, individualType, 0, MPI_COMM_WORLD);
内容的提问来源于stack exchange,提问作者toughcoding

