Arm 与x86 内存屏障测试
in with 0 comment

Arm 与x86 内存屏障测试

in with 0 comment

x86 强内存模型 (Strong Memory Model):

除了 StoreLoad 重排序外,处理器不会对内存访问进行重排序
写操作会立即对其他核心可见
ARM 弱内存模型 (Weak Memory Model):

允许更多类型的内存访问重排序
写操作可能不会立即对其他核心可见
需要显式使用内存屏障来保证顺序性
给予处理器更多优化空间,但增加了编程复杂度
Memory Model Performance TestClick to open code
这个测试程序通过以下方式展示了内存模型的差异:

测试原理:
创建两个并发线程,每个线程都写入一个变量并读取另一个变量
在弱内存模型中,由于内存访问重排序,可能会观察到更多的 r1=r2=0 的情况
在强内存模型中,这种情况会较少出现

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

// 共享变量
volatile int x = 0;
volatile int y = 0;
volatile int r1 = 0;
volatile int r2 = 0;

// 计数器,记录不同执行序列的发生次数
uint64_t count_none = 0;  // r1 = r2 = 0
uint64_t count_x = 0;     // r1 = 1, r2 = 0
uint64_t count_y = 0;     // r1 = 0, r2 = 1
uint64_t count_both = 0;  // r1 = r2 = 1

// 线程函数
void* thread1(void* arg) {
    x = 1;
    #ifdef ARM
        __asm__ volatile("dmb ish" ::: "memory");  // ARM内存屏障
    #endif
    r1 = y;
    return NULL;
}

void* thread2(void* arg) {
    y = 1;
    #ifdef ARM
        __asm__ volatile("dmb ish" ::: "memory");  // ARM内存屏障
    #endif
    r2 = x;
    return NULL;
}

// 执行单次测试
void run_once() {
    pthread_t t1, t2;
    
    // 重置共享变量
    x = y = r1 = r2 = 0;
    
    // 创建线程
    pthread_create(&t1, NULL, thread1, NULL);
    pthread_create(&t2, NULL, thread2, NULL);
    
    // 等待线程结束
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    
    // 统计结果
    if (r1 == 0 && r2 == 0) count_none++;
    else if (r1 == 1 && r2 == 0) count_x++;
    else if (r1 == 0 && r2 == 1) count_y++;
    else if (r1 == 1 && r2 == 1) count_both++;
}

int main() {
    const int iterations = 1000000;
    struct timespec start, end;
    
    // 获取开始时间
    clock_gettime(CLOCK_MONOTONIC, &start);
    
    // 运行多次测试
    for (int i = 0; i < iterations; i++) {
        run_once();
    }
    
    // 获取结束时间
    clock_gettime(CLOCK_MONOTONIC, &end);
    
    // 计算运行时间(毫秒)
    double time_ms = (end.tv_sec - start.tv_sec) * 1000.0 +
                    (end.tv_nsec - start.tv_nsec) / 1000000.0;
    
    // 输出结果
    printf("测试结果 (%d 次迭代):\n", iterations);
    printf("r1=0, r2=0: %lu (%.2f%%)\n", count_none, 
           (double)count_none/iterations*100);
    printf("r1=1, r2=0: %lu (%.2f%%)\n", count_x, 
           (double)count_x/iterations*100);
    printf("r1=0, r2=1: %lu (%.2f%%)\n", count_y, 
           (double)count_y/iterations*100);
    printf("r1=1, r2=1: %lu (%.2f%%)\n", count_both, 
           (double)count_both/iterations*100);
    printf("总运行时间: %.2f ms\n", time_ms);
    printf("平均每次迭代时间: %.3f us\n", time_ms*1000/iterations);
    
    return 0;
}

测试结果intel 8352Y
测试结果(1000000次选代):
r1=0, r2=0: 0 (0.00%)
r1=1, r2=0: 336 (0.03%)
r1=0, r2=1: 999664 (99.97%)
r1=1, r2=1: 0 (0.00%)
总运行时间:26725.53ms
平均每次迭代时间:26.726 us

测试结果ampere altra max
测试结果(10000000次选代):
r1=0,r2=0:0(0.00%
r1=1,r2=0:67902 (6.79%)
r1=0,r2=1:932084
r1=1, r2=1: 14 (0.00%)
总运行时间:57942.97ms
(93.21%)
平均每次选代时间:57.943 us

可以看出这两者之间差距ARM在增加DSB指令后执行的时间更长了

Responses