You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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值的嵌套循环:

  1. 定义线程池大小NUM_WORKERS = 64,和硬件线程数匹配。
  2. 计算每个线程负责的x范围:chunk_size = 32768 / 64,最后一个线程处理剩余元素。
  3. 给每个线程传递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;
}

方案二:线程安全任务队列(负载均衡优化)

如果不同组合的计算耗时差异较大,用任务队列让空闲线程主动取任务:

  1. 实现线程安全的队列,存放待处理的x索引(仅存索引可大幅节省内存)。
  2. 启动64个线程,每个线程循环从队列取x索引,处理对应的y和8-bit循环,直到队列为空且stop未触发。
  3. 主线程将所有x索引放入队列后,等待所有线程结束。

该方案能自动平衡负载,但需要额外实现队列的锁和条件变量,适合计算耗时波动大的场景。

其他关键优化点

  • 修正数组遍历:所有数组遍历必须用元素个数(32768、144),禁止用sizeof。
  • 线程安全的全局变量:stop变量必须加volatile修饰,确保线程能及时感知终止信号。
  • 输出同步:多个线程同时printf会导致输出混乱,必须用互斥锁包裹所有打印操作。
  • 提前终止逻辑:找到目标后立即设置stop=1,线程在循环开头检查stop,避免无效计算。

内容的提问来源于stack exchange,提问作者diviserbyzero

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 16:35:54