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

AWS Spot实例多核心绑定运行程序性能超100%下降求助

AWS Spot实例多核心绑定性能暴跌的原因分析与优化方案

问题与测试观察

  • 核心问题:在AWS Spot实例中,同一程序绑定所有核心运行时,性能比绑定单个核心下降幅度超100%
  • 测试数据:m7i.8xlarge(禁用超线程)实例上,单核心执行时间17秒,绑定全部16核心时平均执行时间35秒;该现象覆盖m7a.8xlarge、m6i.8xlarge、m6a.8xlarge、c7i.8xlarge、c7a.8xlarge、c6i.8xlarge、c6a.8xlarge、r7i.8xlarge、r7a.8xlarge、r6i.8xlarge、r6a.8xlarge等多款Spot实例
  • 本地对比:禁用超线程的本地机器上,多核心绑定性能下降最高仅20%

测试程序

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define SIZE 1500

void initialize_matrix(double **matrix) 
{
    for (int i = 0; i < SIZE; i++) 
    {
        for (int j = 0; j < SIZE; j++) 
        {
            matrix[i][j] = (double)rand() / RAND_MAX; 
        }
    }
}

void multiply_matrices(double **a, double **b, double **result) 
{
    for (int i = 0; i < SIZE; i++) 
    {
        for (int j = 0; j < SIZE; j++) 
        {
            result[i][j] = 0.0;
            for (int k = 0; k < SIZE; k++) 
            {
                result[i][j] += a[i][k] * b[k][j];
            }
        }
    }
}

int main() 
{
    double **a = (double **)malloc(SIZE * sizeof(double *));
    double **b = (double **)malloc(SIZE * sizeof(double *));
    double **result = (double **)malloc(SIZE * sizeof(double *));
    
    for (int i = 0; i < SIZE; i++) 
    {
        a[i] = (double *)malloc(SIZE * sizeof(double));
        b[i] = (double *)malloc(SIZE * sizeof(double));
        result[i] = (double *)malloc(SIZE * sizeof(double));
    }

    srand(time(NULL));

    initialize_matrix(a);
    initialize_matrix(b);

    clock_t start = clock();

    multiply_matrices(a, b, result);

    clock_t end = clock();

    double time_taken = (double)(end - start) / CLOCKS_PER_SEC;
    printf("Time taken for matrix multiplication: %f seconds\n", time_taken);

    for (int i = 0; i < SIZE; i++) 
    {
        free(a[i]);
        free(b[i]);
        free(result[i]);
    }
    free(a);
    free(b);
    free(result);

    return 0;
}

原因分析

  1. 程序无并行化,多核心绑定无意义且添开销:当前代码是纯单线程实现,绑定多核心时,操作系统会尝试将单线程调度到不同核心,不仅无法利用多核心算力,反而会因核心间上下文切换、缓存失效(线程迁移时需重新加载缓存数据)产生额外开销。AWS Spot实例的CPU调度机制更激进,导致该开销被放大。
  2. Spot实例的硬件资源共享特性:Spot实例依托AWS闲置物理机资源,即使禁用超线程,底层物理机仍可能有其他租户实例共享内存带宽、L3缓存等硬件资源。绑定所有核心时,程序占用更多共享资源,触发资源竞争,导致性能骤降;而本地机器为独占资源,竞争极少,性能下降幅度可控。
  3. 非连续内存布局导致缓存效率极低:代码使用二维指针数组(double **),内存为非连续分布,缓存命中率极低。多核心调度时,每个核心的缓存需频繁加载分散的内存块,加上Spot实例的内存带宽可能因共享被限制,进一步恶化性能;本地机器内存带宽独占,缓存效率的影响相对较小。
  4. 时间统计方式的偏差:clock()统计的是CPU时间而非墙上时间,单线程时CPU时间接近墙上时间;但多核心绑定单线程时,操作系统可能将线程调度到多个核心,导致CPU时间累积了多核心的调度时长,统计出的时间被高估。

优化方案

  1. 实现多线程并行计算:使用OpenMP、pthread等框架对矩阵乘法并行化,让每个核心处理独立计算任务,真正利用多核心算力。例如给multiply_matrices添加OpenMP指令:
#include <omp.h>

void multiply_matrices(double **a, double **b, double **result) 
{
    #pragma omp parallel for collapse(2)
    for (int i = 0; i < SIZE; i++) 
    {
        for (int j = 0; j < SIZE; j++) 
        {
            result[i][j] = 0.0;
            for (int k = 0; k < SIZE; k++) 
            {
                result[i][j] += a[i][k] * b[k][j];
            }
        }
    }
}

编译时需添加-fopenmp参数启用并行支持。

  1. 优化内存布局为连续内存:将二维指针数组改为一维连续数组,大幅提升缓存命中率。修改后的初始化与乘法函数示例:
void initialize_matrix(double *matrix) 
{
    for (int i = 0; i < SIZE; i++) 
    {
        for (int j = 0; j < SIZE; j++) 
        {
            matrix[i*SIZE + j] = (double)rand() / RAND_MAX; 
        }
    }
}

void multiply_matrices(double *a, double *b, double *result) 
{
    #pragma omp parallel for collapse(2)
    for (int i = 0; i < SIZE; i++) 
    {
        for (int j = 0; j < SIZE; j++) 
        {
            double sum = 0.0;
            for (int k = 0; k < SIZE; k++) 
            {
                sum += a[i*SIZE + k] * b[k*SIZE + j];
            }
            result[i*SIZE + j] = sum;
        }
    }
}
  1. 避免无意义的多核心绑定:单线程程序无需绑定多核心,直接让操作系统默认调度,或绑定到单个核心,避免核心迁移带来的额外开销。

  2. 选用资源独占型实例(可选):若需稳定性能,可考虑AWS Dedicated Host或Dedicated Instance,确保实例独占物理机资源,避免共享带来的性能波动,不过这类实例成本高于Spot,需权衡成本与性能需求。

  3. 使用精准的时间统计方式:替换clock()为clock_gettime(CLOCK_MONOTONIC)统计墙上时间,避免CPU时间统计的偏差:

#include <time.h>

// ...

struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);

multiply_matrices(a, b, result);

clock_gettime(CLOCK_MONOTONIC, &end);

double time_taken = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
printf("Time taken for matrix multiplication: %f seconds\n", time_taken);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 11:50:55