青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

posts - 9,  comments - 19,  trackbacks - 0

0x0

前些天組里老司機@梁希在jvm的項目榨干機器性能之余,為了檢查下gcc編譯器和Intel Xoen CPU的正確性,寫了一組測試代碼測試了下mfence指令的效果

`
mfence Opcode : 0F AE /6

Performs a serializing operation on all load-from-memory and store-to-memory instructions that were issued prior the MFENCE instruction. This serializing operation guarantees that every load and store instruction that precedes in program order the MFENCE instruction is globally visible before any load or store instruction that follows the MFENCE instruction is globally visible. The MFENCE instruction is ordered with respect to all load and store instructions, other MFENCE instructions, any SFENCE and LFENCE instructions, and any serializing instructions (such as the CPUID instruction).
Weakly ordered memory types can be used to achieve higher processor performance through such techniques as out-of-order issue, speculative reads, write-combining, and write-collapsing.
The degree to which a consumer of data recognizes or knows that the data is weakly ordered varies among applications and may be unknown to the producer of this data. The MFENCE instruction provides a performance-efficient way of ensuring load and store ordering between routines that produce weakly-ordered results and routines that consume that data.
It should be noted that processors are free to speculatively fetch and cache data from system memory regions that are assigned a memory-type that permits speculative reads (that is, the WB, WC, and WT memory types). The PREFETCHh instruction is considered a hint to this speculative behavior. Because this speculative fetching can occur at any time and is not tied to instruction execution, the MFENCE instruction is not ordered with respect to PREFETCHh instructions or any other speculative fetching mechanism (that is, data could be speculatively loaded into the cache just before, during, or after the execution of an MFENCE instruction).
`

簡單來說就是一個可以在CPU亂序執行中保證真實的load/store順序的指令

0x1
老司機寫了一個小程序(注:有誤版)
// file: order.c

#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

union p64 {
    int i;
    char padding[64];
    long align8;
};

volatile union p64 v1, v2;
int b;

void *
run1(void *ignore)
{
    for (;;) {
        while (!b);
        if (v1.i || v2.i) {
            puts("assert error 1");
            exit(-1);
        }
        v1.i = 1;
        asm ("sfence": : :"memory");
        v2.i = 1;
        asm ("sfence": : :"memory");
        b = 0; 
    }
}

int
main()
{
    pthread_t p;
    pthread_create(&p, NULL, run1, NULL);
    int cnt = 0;

    for (;; cnt++) {
        v1.i = v2.i = 0;
        asm ("sfence": : :"memory");
        b = 1;
        asm ("sfence": : :"memory");
        int icnt = 0;
        for (;; icnt++) {
            int i1 = v1.i;
            asm ("lfence": : :"memory");
            int i2 = v2.i;
            if (i1 && i2)   break;
            if (i1 < i2) {
                printf("assert error, cnt = %d, icnt = %d, i1 = %d, i2 = %d\n", cnt, icnt, i1, i2);
                exit(-1);
            }
        }
    }
    return 0;
}

大概邏輯是: 一共有3個變量,v1.iv2.ib ,起了2個線程,一個順序寫入v1和v2,一個讀v1和v2,互相通過改變b的值來通訊,然后兩個線程不停循環。

這個程序會掛在
printf("assert error, cnt = %d, icnt = %d, i1 = %d, i2 = %d\n", cnt, icnt, i1, i2); 
這條斷言上,意思是線程1在順序寫入v1和v2,但是主線程卻出現讀到 v1=0,v2=1的情況。

0x2

然后我幫忙去看了一下,覺得這種寫法甚是粗暴,于是原樣照搬了一個c++11版:

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

#include <atomic>
#include <thread>

using namespace std;

union p64 {
    atomic<int> i;
    char padding[64];
    long align8;
};

volatile union p64 v1, v2;
atomic<int> b;

void *
run1()
{
    int rcnt = 0;
    for (;; rcnt++) {
        while (!b.load());
        if (v1.i.load() || v2.i.load()) {
            puts("assert error 1");
            exit(-1);
        }
        v1.i.store(1);
        v2.i.store(1);
        b.store(0);
    }
}

int
main()
{
    // init
    v1.i.store(0);
    v2.i.store(0);
    thread t(run1);
    int cnt = 0;
    for (;; cnt++) {
        v1.i.store(0);
        v2.i.store(0);
        b.store(1);
        int icnt = 0;
        for (;; icnt++) {
            int b2 = b.load();
            int i1 = v1.i.load();       // *****
            int i2 = v2.i.load();       // *****
            if (i1 && i2)   break;
            if (i1 < i2) {
                printf("assert error, cnt = %d, icnt = %d, i1 = %d, i2 = %d\n", cnt, icnt, i1, i2);
                exit(-1);
            }
            if (i1 == 0 && i2 == 0 && b2 == 0) break;
        }
    }
    return 0;
}

因為是原樣照搬,所以肯定還是會掛,但是畢竟語義上更好理解了

我們先來分析一下為什么會掛

  • 線程1對于v1,v2的寫入順序一定是一致的
  • Memory Barrier也保證了他們寫入順序對其他線程的可見性(很有迷惑性的一點)
  • 但是主線程卻可以讀到 v1=0,v2=1的情況
  • 所以情況就是雖然順序寫入了,但是別的線程沒有看到正確的順序?
  • Intel: 并不是!
  • 原因是搞錯了因果關系,他真正保證的順序是當你讀到v2的new value的時候,那么v1也一定被寫入了。
  • 解決方案就是互換上面代碼中我用**星號**標注出的兩行
  • done

在舊寫法中,掛掉的情況是線程1寫入v1 = 1,主線程讀v1,沒有讀到,那么主線程認為v1是0,然后線程1繼續寫入v2,主線程讀到了,主線程認為v2是1。 然后掛在了斷言上。

兩行互換后,主線程首先讀取v2,如果v2已經是1了,那么v1也一定是1,反之亦然。

0x3

當然,想讓跑通那個例子不需要那么多的atomic<>,精簡之后利用c++11的memory_order可以寫成如下:

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

#include <atomic>
#include <thread>

using namespace std;

union p64 {
    int i;
    char padding[64];
    long align8;
};

volatile union p64 v1, v2;
atomic<int> b;    // variable b as a guard

void *
run1()
{
    int rcnt = 0;
    for (;; rcnt++) {
        while (!b.load());
        if (v1.i || v2.i) {
            puts("assert error 1");
            exit(-1);
        }
        v1.i = 1;
        v2.i = 1;
        b.store(0, memory_order_release);
    }
}
int
main()
{
    // init
    v1.i = 0;
    v2.i = 0;
    thread t(run1);
    int cnt = 0;

    for (;; cnt++) {
        v1.i = 0;
        v2.i = 0;
        b.store(1, memory_order_release);
        int icnt = 0;
        for (;; icnt++) {
            int b2 = b.load(memory_order_acquire);
            if (b2 != 0) {
                continue
            }
            int i1 = v1.i;
            int i2 = v2.i;
            if (i1 && i2)   break;
            if (i1 < i2) {
                printf("assert error 2, cnt = %d, icnt =  %d, i1 = %d, i2 = %d\n", cnt, icnt, i1, i2);
                exit(-1);
            }
        }
    }
    return 0;
}

利用變量b在兩個線程之間同步,如下圖

 (Thead 1)

   v1.i = 1;
   v2.i = 1;
   
   b.store(0, memory_order_release) <---+
                                                             |
                                                synchronize with b
                                                 (happend before)
                                                             |
                                                            +----->  b.load(memory_order_acquire)
                                                                          
                                                                        i1 = v1.i
                                                                        i2 = v2.i

                                                                       (Thread 2)

我們查看下生成的代碼
g++ -std=c++11 -pthread -g -O2 order.cpp

 v1.i = 1;
  400be6:       c7 05 d0 10 20 00 01    movl   $0x1,0x2010d0(%rip)        # 601cc0 <v1>
  400bed:       00 00 00 
        v2.i = 1;
  400bf0:       c7 05 86 10 20 00 01    movl   $0x1,0x201086(%rip)        # 601c80 <v2>
  400bf7:       00 00 00 
        memory_order __b = __m & __memory_order_mask;
        __glibcxx_assert(__b != memory_order_acquire);
        __glibcxx_assert(__b != memory_order_acq_rel);
        __glibcxx_assert(__b != memory_order_consume);

        __atomic_store_n(&_M_i, __i, __m);
  400bfa:       c7 05 5c 10 20 00 00    movl   $0x0,0x20105c(%rip)        # 601c60 <b>
  400c01:       00 00 00 
        b.store(0, memory_order_release);

  

  400a58:       8b 05 02 12 20 00       mov    0x201202(%rip),%eax        # 601c60 <b>
            int b2 = b.load(memory_order_consume);
            if (b2 != 0) {
  400a5e:       85 c0                   test   %eax,%eax
  400a60:       75 f3                   jne    400a55 <main+0x55>
                continue
            }
            int i1 = v1.i;
  400a62:       8b 0d 58 12 20 00       mov    0x201258(%rip),%ecx        # 601cc0 <v1>
            int i2 = v2.i;
  400a68:       44 8b 05 11 12 20 00    mov    0x201211(%rip),%r8d        # 601c80 <v2>

看來Intel的Strong Memory Model已經保證了這一點,Memory Barrier都不需要了

(雖然標題里面有MemoryBarrier,但是內容里面根本沒涉及的樣子。。)

posted on 2016-01-19 16:13 右席 閱讀(16802) 評論(1)  編輯 收藏 引用 所屬分類: 搬磚之路
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美成年人视频网站| 欧美第十八页| 欧美88av| 国产精品高潮呻吟久久| 久久婷婷人人澡人人喊人人爽| 久久久一区二区| 欧美日韩久久不卡| 国产精品嫩草99av在线| 欧美亚洲综合另类| 亚洲精品一区在线观看| 国产毛片精品视频| 国产精品嫩草影院av蜜臀| 欧美精品在欧美一区二区少妇| 亚洲调教视频在线观看| 久久久亚洲一区| 美女视频黄a大片欧美| 亚洲日本va午夜在线影院| 亚洲美女福利视频网站| 一区二区三区|亚洲午夜| 欧美激情视频在线免费观看 欧美视频免费一 | 好看的日韩视频| 美女主播视频一区| 国产欧美另类| 欧美一区二区免费| 欧美国产日韩亚洲一区| 国产在线高清精品| 国产一区视频网站| 亚洲国产毛片完整版| 亚洲视频在线一区| 老色批av在线精品| 午夜在线成人av| 欧美三级视频在线播放| 在线成人性视频| 久久精品国产2020观看福利| 牛牛影视久久网| 老司机成人网| 国内精品视频一区| 欧美在线资源| 午夜精品免费| 国产一区二区三区高清播放| 亚洲永久免费| 亚洲午夜久久久久久尤物| 国产欧美日韩一区二区三区在线| 国产精品伊人日日| 欧美日韩精品一区二区三区四区| 一区二区高清在线观看| 牛牛影视久久网| 一本久久综合| 国产精品午夜在线| 欧美激情自拍| 亚洲第一福利社区| 欧美在线免费视屏| 久色婷婷小香蕉久久| 久久精品色图| 欧美国产日韩a欧美在线观看| 91久久久久久国产精品| 久久精品成人一区二区三区| 久久九九久精品国产免费直播| 欧美在线不卡| 一区二区三区av| 蜜臀a∨国产成人精品| 免费中文日韩| 99国产精品久久久久久久| 久久久国产精品一区二区中文 | 国产午夜亚洲精品羞羞网站| 久久这里有精品15一区二区三区| 亚洲特色特黄| 国产精品美女诱惑| 亚洲欧美久久久久一区二区三区| 欧美影院在线| 一区二区电影免费观看| 亚洲一区二区三区视频| 黄色一区三区| 亚洲一区二区免费在线| 国产一区二区三区在线播放免费观看 | 亚洲丰满在线| 亚洲电影免费观看高清完整版在线| 久久精品五月婷婷| 久久国产加勒比精品无码| 玖玖视频精品| 久久久久久久精| 久久久xxx| 国产伊人精品| 欧美一区二区在线视频| 午夜一级久久| 伊人成年综合电影网| 久久免费视频在线观看| 久久综合久久综合久久综合| 亚洲第一在线| 欧美精选一区| 性做久久久久久久久| 久久综合九色| 亚洲在线观看| 亚洲大片精品永久免费| 国产精品国产成人国产三级| 午夜精品一区二区三区四区| 久久久久在线| 亚洲少妇中出一区| 国产综合网站| 欧美性大战久久久久| 久久久久久电影| 亚洲片国产一区一级在线观看| 亚洲视频高清| 亚洲国产精品成人va在线观看| 欧美特黄一区| 欧美激情一区二区三级高清视频| 日韩图片一区| 91久久国产精品91久久性色| 久久se精品一区二区| 正在播放亚洲| 亚洲激情视频网站| 亚洲欧美日韩一区二区三区在线 | 免费久久久一本精品久久区| 欧美一区二视频| 亚洲一区日韩在线| 在线中文字幕一区| 亚洲美女性视频| 日韩午夜在线| 亚洲午夜免费福利视频| 老司机午夜精品视频| 亚洲专区欧美专区| 亚洲精品在线免费| 亚洲精品1区2区| 亚洲国产成人精品久久久国产成人一区| 国产精品人人做人人爽| 国产精品你懂的在线欣赏| 国产精品久久久久久久久久免费| 欧美日韩一区在线| 国产精品嫩草99av在线| 国产欧美亚洲一区| 一色屋精品视频在线看| 欧美 日韩 国产在线| 欧美手机在线| 黑人极品videos精品欧美裸| 依依成人综合视频| 在线中文字幕不卡| 久久久一本精品99久久精品66| 欧美 日韩 国产一区二区在线视频 | 亚洲国产美女精品久久久久∴| 尹人成人综合网| 欧美中文字幕精品| 亚洲激情小视频| 篠田优中文在线播放第一区| 欧美福利精品| 亚洲黄色大片| 久久久久久久久久久久久久一区 | 在线亚洲美日韩| 欧美成人免费小视频| 国产一区二区三区网站| 亚洲在线观看视频网站| 欧美激情视频免费观看| 欧美一区二区三区视频免费播放| 欧美日韩成人综合天天影院| 久久久精品一区| 免费看亚洲片| 狠狠色综合一区二区| 久久久噜噜噜久噜久久 | 亚洲一区国产| 欧美午夜一区二区三区免费大片 | 欧美a级片网| 久久一二三国产| 亚洲伦理自拍| 一本色道婷婷久久欧美| 欧美午夜不卡在线观看免费 | 亚洲一区二区成人| 亚洲综合视频一区| 狠狠久久婷婷| 亚洲精品综合久久中文字幕| 欧美精品在线视频观看| 午夜精品久久久久久久男人的天堂| 国产精品99久久久久久宅男| 国产日韩免费| 亚洲全部视频| 精品96久久久久久中文字幕无| 美女脱光内衣内裤视频久久网站| 欧美搞黄网站| 久久久999成人| 欧美国产三区| 久久久久五月天| 国产精品v片在线观看不卡| 久久看片网站| 欧美新色视频| 亚洲精品视频啊美女在线直播| 国产欧美欧美| 亚洲欧美日本视频在线观看| 久久综合九色欧美综合狠狠| 亚洲视频免费| 欧美午夜国产| 亚洲精品影视| 一本久道久久综合婷婷鲸鱼| 狼狼综合久久久久综合网 | 亚洲欧洲综合另类| 伊人久久成人| 久久综合色播五月| 免费成人黄色av| 亚洲狼人精品一区二区三区| 久久综合久久久| 亚洲乱亚洲高清| 午夜精品久久| 激情亚洲一区二区三区四区|