2016-01-22 2 views
3

Приведенный ниже код при компиляции для xeon phi throws Error: cmovc is not supported on k1om.Атомный тестовый набор в x86: встроенный asm или сгенерированный компилятором блокировки bts?

Но он правильно компилируется для обычного процессора xeon.

#include<stdio.h> 
int main() 
{ 
    int in=5; 
    int bit=1; 
    int x=0, y=1; 
    int& inRef = in; 
    printf("in=%d\n",in); 
    asm("lock bts %2,%0\ncmovc %3,%1" : "+m" (inRef), "+r"(y) : "r" (bit), "r"(x)); 
    printf("in=%d\n",in); 
} 

Компилятор - icc (ICC) 13.1.0 20130121

Связанные вопрос: bit test and set (BTS) on a tbb atomic variable

+1

IIRC, first-gen Xeon Phi основан на ядрах P5 (Pentium и Pentium MMX). 'cmov' не вводился до P6 (ака Pentium Pro). Поэтому я считаю, что это нормально.Просто дайте компилятору выполнить свою работу, написав обычный тернарный оператор. Также обратите внимание, что 'bts' с операндом памяти является супер-медленным. Не делай этого. Кроме того, я думаю, что вы тестируете биты в адресе 'in', так как вы запрашиваете' & in' для хранения в памяти. Это то, что вы хотели? –

+1

И в чем ваш вопрос? –

+0

Я предполагал, что xeon phi также основан на x86, он должен работать нормально – arunmoezhi

ответ

3

IIRC, первое поколение Xeon Phi основан на P5 ядер (Pentium и Pentium MMX). cmov не был представлен до P6 (он же Pentium Pro). Поэтому я считаю, что это нормально.

Просто позвольте компилятору выполнять свою работу, написав обычный тернарный оператор.

Во-вторых, cmov - это гораздо худший выбор для этого, чем setc, так как вы хотите создать 0 или 1 на основе флага переноса. См. Мой код asm ниже.

Также обратите внимание, что bts с операндом памяти является супер-медленным, поэтому вы не хотите, чтобы он генерировал этот код в любом случае, особенно. на процессоре, который декодирует инструкции x86 в uops (например, современный Xeon). Согласно http://agner.org/optimize/, bts m, r намного медленнее, чем bts m, i даже на P5, поэтому не делайте этого.

Просто спросите компилятор для in, чтобы быть в регистре, или еще лучше, просто не используйте inline asm для этого.


Поскольку ОП, видимо, хочет, чтобы работать атомарно, лучшим решением является использование 11 C++ std::atomic::fetch_or-х, и оставить его на компилятор для генерации lock bts.

std::atomic_flag имеет функцию test_and_set, но IDK, если есть способ плотно упаковать их. Может быть, как битовые поля в структуре? Вряд ли. Я также не вижу атомных операций для std::bitset.

К сожалению, текущие версии gcc и clang не генерируют lock bts от fetch_or, даже если используется более быстрая форма быстрого операнда. Я придумал следующее (godbolt link):

#include <atomic> 
#include <stdio.h> 

// wastes instructions when the return value isn't used. 
// gcc 6.0 has syntax for using flags as output operands 

// IDK if lock BTS is better than lock cmpxchg. 
// However, gcc doesn't use lock BTS even with -Os 
int atomic_bts_asm(std::atomic<unsigned> *x, int bit) { 
    int retval = 0; // the compiler still provides a zeroed reg as input even if retval isn't used after the asm :/ 
    // Letting the compiler do the xor means we can use a m constraint, in case this is inlined where we're storing to already zeroed memory 
    // It unfortunately doesn't help for overwriting a value that's already known to be 0 or 1. 
    asm(// "xor  %[rv], %[rv]\n\t" 
     "lock bts %[bit], %[x]\n\t" 
     "setc  %b[rv]\n\t" // hope that the compiler zeroed with xor to avoid a partial-register stall 
     : [x] "+m" (*x), [rv] "+rm"(retval) 
     : [bit] "ri" (bit)); 
    return retval; 
} 

// save an insn when retval isn't used, but still doesn't avoid the setc 
// leads to the less-efficient setc/ movzbl sequence when the result is needed :/ 
int atomic_bts_asm2(std::atomic<unsigned> *x, int bit) { 
    uint8_t retval; 
    asm("lock bts %[bit], %[x]\n\t" 
     "setc  %b[rv]\n\t" 
     : [x] "+m" (*x), [rv] "=rm"(retval) 
     : [bit] "ri" (bit)); 
    return retval; 
} 


int atomic_bts(std::atomic<unsigned> *x, unsigned int bit) { 
    // bit &= 31; // stops gcc from using shlx? 
    unsigned bitmask = 1<<bit; 
    //int oldval = x->fetch_or(bitmask, std::memory_order_relaxed); 

    int oldval = x->fetch_or(bitmask, std::memory_order_acq_rel); 
    // acquire and release semantics are free on x86 
    // Also, any atomic rmw needs a lock prefix, which is a full memory barrier (seq_cst) anyway. 

    if (oldval & bitmask) 
    return 1; 
    else 
    return 0; 
} 

Как обсуждалось в What is the best way to set a register to zero in x86 assembly: xor, mov or and?, xor/Set-флагов/setc является оптимальной последовательности для всех современных процессоров, когда результат необходим в качестве 0-или-1 значения , Я на самом деле не рассматривал P5 для этого, но setcc быстро работает на P5, поэтому все должно быть хорошо.

Конечно, если вы хотите разветвиться на этом вместо того, чтобы хранить его, граница между встроенными asm и C является препятствием. Проводя две инструкции для хранения 0 или 1, только для проверки/ветвления на ней, будет довольно глупым.

Синтаксис флаг-операнд gcc6, безусловно, стоит посмотреть, если это вариант. (Возможно, нет, если вам нужен компилятор, который нацелен на Intel MIC.)

+0

В чем разница между' bts m, r' и 'bts m, i'? i' здесь означает режим немедленной адресации? – arunmoezhi

+0

@arunmoezhi: да, 'i' является немедленным. Если бит-позиция является константой времени компиляции, вы получите намного лучшие результаты таким образом, поскольку CPU знает окончательный адрес памяти намного раньше в конвейере, поэтому он декодирует намного меньше uops. Например, используйте ограничение 'ri'. Мне не очень повезло, что gcc генерирует« блокировку bts », даже если это может быть оптимальным. http://goo.gl/yKYTfY. http://en.cppreference.com/w/cpp/atomic/atomic_flag выглядит полезным (имеет атомную функцию test_and_set), но o nly для автономных флагов, а не для битов в int:/ –

+0

Спасибо. В функции 'atomic_bts' код сборки имеет' cmpxchg'. Но в многопоточной среде compare_and_exchange может быть медленнее, чем 'bts'. Например, два потока 'A' ​​и' B' пытаются «одновременно» устанавливать бит 'битA' и' битB' соответственно. Если я использую 'cmpxchg', то только один будет успешным. Но 'bts' на обоих битах будет успешным. – arunmoezhi

Смежные вопросы