隨想錄(記憶體屏障範例程式碼)

來源:互聯網
上載者:User

【 聲明:著作權,歡迎轉載,請勿用於商業用途。  聯絡信箱:feixiaoxing @163.com】  

    記憶體屏障,英文稱之為memory barrier。產生記憶體屏障的原因很多,有的是因為編譯器最佳化亂碼造成的,有的是因為cpu亂序造成的,還有的是因為cpu cache沒有按順序同步造成的。編譯器最佳化的code很容易理解,但是cpu亂序是怎麼造成記憶體屏障的,卻很少在網上看到相關的說明代碼。今天,很偶然在網上看到網易的何登成同學寫的《CPU Cache and Memory Ordering 》,中間就包括了這一份代碼,啟發很大。大家可以下載下來,看看結果,思考思考。代碼編譯的環境是linux,這個需要注意一下。編譯命令在文中也一併包含了。

// compile with: g++ -o ordering -O2 ordering.cpp -lpthread#include <pthread.h>#include <semaphore.h>#include <stdio.h>#include <stdlib.h>// Set either of these to 1 to prevent CPU reordering#define USE_CPU_FENCE              0#define USE_SINGLE_HW_THREAD       0  // Supported on Linux, but not Cygwin or PS3#if USE_SINGLE_HW_THREAD#include <sched.h>#endif//-------------------------------------//  MersenneTwister//  A thread-safe random number generator with good randomness//  in a small number of instructions. We'll use it to introduce//  random timing delays.//-------------------------------------#define MT_IA  397#define MT_LEN 624class MersenneTwister{    unsigned int m_buffer[MT_LEN];    int m_index;public:    MersenneTwister(unsigned int seed);    // Declare noinline so that the function call acts as a compiler barrier:    unsigned int integer() __attribute__((noinline));};MersenneTwister::MersenneTwister(unsigned int seed){    // Initialize by filling with the seed, then iterating    // the algorithm a bunch of times to shuffle things up.    for (int i = 0; i < MT_LEN; i++)        m_buffer[i] = seed;    m_index = 0;    for (int i = 0; i < MT_LEN * 100; i++)        integer();}unsigned int MersenneTwister::integer(){    // Indices    int i = m_index;    int i2 = m_index + 1; if (i2 >= MT_LEN) i2 = 0; // wrap-around    int j = m_index + MT_IA; if (j >= MT_LEN) j -= MT_LEN; // wrap-around    // Twist    unsigned int s = (m_buffer[i] & 0x80000000) | (m_buffer[i2] & 0x7fffffff);    unsigned int r = m_buffer[j] ^ (s >> 1) ^ ((s & 1) * 0x9908B0DF);    m_buffer[m_index] = r;    m_index = i2;    // Swizzle    r ^= (r >> 11);    r ^= (r << 7) & 0x9d2c5680UL;    r ^= (r << 15) & 0xefc60000UL;    r ^= (r >> 18);    return r;}//-------------------------------------//  Main program, as decribed in the post//-------------------------------------sem_t beginSema1;sem_t beginSema2;sem_t endSema;int X, Y;int r1, r2;/* thread1Func, thread2Func for StoreLoad *//* thread3Func, thread4Func for StoreStore LoadLoad *//* thread5Func, thread6Func for LoadStore */void *thread1Func(void *param){    MersenneTwister random(1);    for (;;)    {        sem_wait(&beginSema1);  // Wait for signal        while (random.integer() % 8 != 0) {}  // Random delay        // ----- THE TRANSACTION! -----        X = 1;#if USE_CPU_FENCE        asm volatile("mfence" ::: "memory");  // Prevent CPU reordering#else        asm volatile("" ::: "memory");  // Prevent compiler reordering#endif        r1 = Y;        sem_post(&endSema);  // Notify transaction complete    }    return NULL;  // Never returns};void *thread2Func(void *param){    MersenneTwister random(2);    for (;;)    {        sem_wait(&beginSema2);  // Wait for signal        while (random.integer() % 8 != 0) {}  // Random delay        // ----- THE TRANSACTION! -----        Y = 1;#if USE_CPU_FENCE        asm volatile("mfence" ::: "memory");  // Prevent CPU reordering#else        asm volatile("" ::: "memory");  // Prevent compiler reordering#endif        r2 = X;        sem_post(&endSema);  // Notify transaction complete    }    return NULL;  // Never returns};void *thread3Func(void *param){MersenneTwister random(1);for (;;){sem_wait(&beginSema1);  // Wait for signalwhile (random.integer() % 8 != 0) {}  // Random delay// ----- THE TRANSACTION! -----X = 1;#if USE_CPU_FENCEasm volatile("mfence" ::: "memory");  // Prevent CPU reordering#elseasm volatile("" ::: "memory");  // Prevent compiler reordering only#endifY = 1;sem_post(&endSema);  // Notify transaction complete}return NULL;  // Never returns};void *thread4Func(void *param){MersenneTwister random(2);for (;;){sem_wait(&beginSema2);  // Wait for signalwhile (random.integer() % 8 != 0) {}  // Random delay// ----- THE TRANSACTION! -----r1 = Y;#if USE_CPU_FENCEasm volatile("mfence" ::: "memory");  // Prevent CPU reordering#elseasm volatile("" ::: "memory");  // Prevent compiler reordering only#endifr2 = X;sem_post(&endSema); // Notify transaction complete}return NULL;  // Never returns};void *thread5Func(void *param){MersenneTwister random(1);for (;;){sem_wait(&beginSema1);  // Wait for signalwhile (random.integer() % 8 != 0) {}  // Random delay// ----- THE TRANSACTION! -----r1 = X;#if USE_CPU_FENCEasm volatile("mfence" ::: "memory");  // Prevent CPU reordering#elseasm volatile("" ::: "memory");  // Prevent compiler reordering only#endifY = 1;sem_post(&endSema);  // Notify transaction complete}return NULL;  // Never returns};void *thread6Func(void *param){MersenneTwister random(2);for (;;){sem_wait(&beginSema2);  // Wait for signalwhile (random.integer() % 8 != 0) {}  // Random delay// ----- THE TRANSACTION! -----r2 = Y;#if USE_CPU_FENCEasm volatile("mfence" ::: "memory");  // Prevent CPU reordering#elseasm volatile("" ::: "memory");  // Prevent compiler reordering only#endifX = 1;sem_post(&endSema);  // Notify transaction complete}return NULL;  // Never returns};int main(int argc, char** argv){// Check Argumentif (argc > 2){printf("Too Many Arguments: Only Need One.\n");return 0;}if (argc == 1){printf("You Should Give an Argument: 1 or 2 or 3.\n");return 0;}int i;i = atoi(argv[1]);if (i < 1 || i > 3){printf("Wrong Argument: Only 1 or 2 or 3 Can be Input.\n");return 0;}    // Initialize the semaphores    sem_init(&beginSema1, 0, 0);    sem_init(&beginSema2, 0, 0);    sem_init(&endSema, 0, 0);    // Spawn the threads    pthread_t thread1, thread2;// 1. StoreLoad Reorder Testsif (i == 1) {pthread_create(&thread1, NULL, thread1Func, NULL);pthread_create(&thread2, NULL, thread2Func, NULL);}// 2. LoadLoad && StoreStore Reorder Testselse if (i == 2){pthread_create(&thread1, NULL, thread3Func, NULL);pthread_create(&thread2, NULL, thread4Func, NULL);}// 3. LoadStore Reorder Testselse{pthread_create(&thread1, NULL, thread5Func, NULL);pthread_create(&thread2, NULL, thread6Func, NULL);}#if USE_SINGLE_HW_THREAD    // Force thread affinities to the same cpu core.    cpu_set_t cpus;    CPU_ZERO(&cpus);    CPU_SET(0, &cpus);    pthread_setaffinity_np(thread1, sizeof(cpu_set_t), &cpus);    pthread_setaffinity_np(thread2, sizeof(cpu_set_t), &cpus);#endif    // Repeat the experiment ad infinitum    int detected = 0;    for (int iterations = 1; ; iterations++)    {        // Reset X and Y        X = 0;        Y = 0;        // Signal both threads        sem_post(&beginSema1);        sem_post(&beginSema2);        // Wait for both threads        sem_wait(&endSema);        sem_wait(&endSema);        // Check if there was a simultaneous reorder        // 1. StoreLoad Reorderif (i == 1){if (r1 == 0 && r2 == 0){detected++;printf("%d reorders detected after %d iterations\n", detected, iterations);}}// 2. LoadLoad && StoreStore Reorderelse if (i == 2){if (r1 == 1 && r2 == 0){detected++;printf("%d reorders detected after %d iterations\n", detected, iterations);}}// 3. LoadStore Reorderelse {if (r1 == 1 && r2 == 1){detected++;printf("%d reorders detected after %d iterations\n", detected, iterations);}}    }    return 0;  // Never returns}

    有機會的話,同學們可以在網上找到這篇文檔來看看。文中的一個觀點,我特別同意:所有的LOCK prefix指令的代碼,都會構成天生的memory barrier。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.