如何用Pthread高效分配[16-bit][16-bit][8-bit]组合计算任务?
多线程高效分配大规模组合计算任务
我有两个文件:一个包含全部16-bit数字,另一个包含144个自定义8-bit数字。目标是生成[16-bit][16-bit][8-bit]格式的40位组合(总数量达数十亿至万亿级),希望用Pthread多线程分配计算任务。我的设备是32核64线程、256GB内存+8TB存储,当前代码存在问题,希望得到高效任务分配的指导。
注:每个组合为40位,即2个16-bit数字和1个8-bit数字,示例组合为[34628,37562,4096],其中4096是1000的末尾补零表示,最后一个数组仅含144个值(范围10至9f)。
现有代码实现
1. 文件数据读取
FILE *fil = fopen("pair.txt","r"); FILE *fd = fopen("pair2.txt","r"); size_t aln = 32768; size_t aln1 = 144; size_t numb[aln]; size_t numb1[aln]; size_t lump[aln1]; int count = 0; char line[32768]; while(fgets(line, sizeof(line), fil) != NULL) { size_t num; if(sscanf(line, "%zu", &num) == 1) { if(count < aln) { numb[count] = num; numb1[count] = num; count++; } } } count = 0; char line1[144]; while(fgets(line1, sizeof(line1), fd) != NULL) { size_t num; if(sscanf(line1, "%zu", &num) == 1) { if(count < aln1) { lump[count] = num; count++; } } }
2. 任务分配与线程生成
thread ts[NUM_WORKERS]; for (size_t i = 0; i < NUM_WORKERS; i++) { size_t x, y, end, d; //get value from arrays for(size_t a = 0; a < sizeof(numb); a++) { x = numb[a]; for(size_t b = 0; b < sizeof(numb1); b++) { y = numb1[b]; for(size_t c = 0; c < sizeof(numb2); c++) { end = numb2[c]; for(size_t f = 0; f < sizeof(lump); f++) { d = lump[f]; ts[i] = spawn_worker(x, y, end, d); } } } } } for (size_t i = 0; i < NUM_WORKERS; i++) join_thread(ts[i]);
3. 线程创建函数
static thread spawn_worker(size_t x, size_t y, size_t end, size_t d) { struct info *info = (struct info *)malloc(sizeof(struct info)); assert(info != NULL); //info->n = n; info->x = x; info->y = y; info->end = end; info->d = d; thread t = spawn_thread(worker, info); if (t == (thread)NULL) { fprintf(stderr, "error: failed to spawn thread"); exit(EXIT_FAILURE); } return t; }
4. 工作线程函数
static void *worker(void *arg) { struct info *info = (struct info *)arg; size_t n = info->n; size_t x = info->x; size_t y = info->y; size_t end = info->end; size_t d = info->d; free(info); uint160_t target; memcpy(&target, &targ, sizeof(target)); // COMPUTE WORK: if (stop) return NULL; uint160_t ad = f(x, y, end, d); if (is_equal(n, ad, target)) { size_t w = 80; printf("\n"); uint256_t key0 = gen_priv_key(x, y, end, d); for (size_t i = 0; i < sizeof(key0); i++) { printf("%.2X", key0.i8[i]); } printf("\n"); if (!is_equal(w, ad, target)) { stop = true; uint256_t key1 = gen_priv_key(x, y, end, d); //print private key here printf("=====================================================\n"); for (size_t i = 0; i < sizeof(key1); i++) { printf("%.2X", key1.i8[i]); } printf("=======================================================\n"); } } }
核心问题与优化方案
当前代码的致命问题
- 线程创建爆炸:给每个组合单独创建线程,总线程数达数十亿级,直接耗尽系统资源,完全无法运行。
- 数组遍历错误:用
sizeof(numb)获取元素个数是错误的,sizeof返回的是数组总字节数,应该用定义好的aln(32768),否则会触发内存越界。 - 任务分配逻辑混乱:外层
NUM_WORKERS循环没有起到任务分配作用,所有线程都被覆盖到同一个数组元素,最终只等待NUM_WORKERS个线程,剩余线程全部泄漏。
高效任务分配方案
针对64线程的硬件配置,最优方式是固定线程池+任务分片,避免动态创建大量线程,同时保证负载均衡。
方案一:按第一个16-bit数组分片
把第一个数组(32768个元素)拆分成64份,每个线程负责一段连续的x值,线程内部完成y和8-bit值的嵌套循环:
- 定义线程池大小
NUM_WORKERS = 64,和硬件线程数匹配。 - 计算每个线程负责的
x范围:chunk_size = 32768 / 64,最后一个线程处理剩余元素。 - 给每个线程传递
x的起始/结束索引,以及全局的数组(或用结构体打包传递,避免大内存拷贝)。
示例代码框架:
// 全局变量:stop需加volatile保证线程可见,数组提前读取完成 volatile int stop = 0; size_t numb[32768]; size_t numb1[32768]; size_t lump[144]; pthread_mutex_t print_mutex; // 用于同步打印输出 typedef struct { size_t start_idx; size_t end_idx; } ThreadTask; static void *worker(void *arg) { ThreadTask *task = (ThreadTask*)arg; uint160_t target; memcpy(&target, &targ, sizeof(target)); for (size_t a = task->start_idx; a < task->end_idx && !stop; a++) { size_t x = numb[a]; for (size_t b = 0; b < 32768 && !stop; b++) { size_t y = numb1[b]; for (size_t f = 0; f < 144 && !stop; f++) { size_t d = lump[f]; // 执行计算逻辑 uint160_t ad = f(x, y, d); if (is_equal(/* 对应参数 */, ad, target)) { // 同步打印,避免输出混乱 pthread_mutex_lock(&print_mutex); uint256_t key0 = gen_priv_key(x, y, d); for (size_t i = 0; i < sizeof(key0); i++) { printf("%.2X", key0.i8[i]); } printf("\n"); // 触发全局终止 stop = 1; pthread_mutex_unlock(&print_mutex); goto exit_thread; // 直接退出线程 } } } } exit_thread: free(task); return NULL; } int main() { // 先完成文件读取(修正原代码的sizeof遍历错误) // ... pthread_mutex_init(&print_mutex, NULL); pthread_t threads[64]; size_t chunk_size = 32768 / 64; for (size_t i = 0; i < 64; i++) { ThreadTask *task = malloc(sizeof(ThreadTask)); task->start_idx = i * chunk_size; // 最后一个线程处理剩余元素 task->end_idx = (i == 63) ? 32768 : (i+1)*chunk_size; pthread_create(&threads[i], NULL, worker, task); } // 等待所有线程结束 for (size_t i = 0; i < 64; i++) { pthread_join(threads[i], NULL); } pthread_mutex_destroy(&print_mutex); return 0; }
方案二:线程安全任务队列(负载均衡优化)
如果不同组合的计算耗时差异较大,用任务队列让空闲线程主动取任务:
- 实现线程安全的队列,存放待处理的
x索引(仅存索引可大幅节省内存)。 - 启动64个线程,每个线程循环从队列取
x索引,处理对应的y和8-bit循环,直到队列为空且stop未触发。 - 主线程将所有
x索引放入队列后,等待所有线程结束。
该方案能自动平衡负载,但需要额外实现队列的锁和条件变量,适合计算耗时波动大的场景。
其他关键优化点
- 修正数组遍历:所有数组遍历必须用元素个数(
32768、144),禁止用sizeof。 - 线程安全的全局变量:
stop变量必须加volatile修饰,确保线程能及时感知终止信号。 - 输出同步:多个线程同时
printf会导致输出混乱,必须用互斥锁包裹所有打印操作。 - 提前终止逻辑:找到目标后立即设置
stop=1,线程在循环开头检查stop,避免无效计算。
内容的提问来源于stack exchange,提问作者diviserbyzero
相关产品推荐
相关产品推荐

