Доступ к содержимому векторных регистров в сборке x86-64 [дубликат]

Вот еще одно решение, похожее на добавление bbox_extra_artists и bbox_inches, где вам не нужно иметь дополнительных исполнителей в области вашего вызова savefig. Я придумал это, так как я генерирую большую часть своего сюжета внутри функций.

Вместо того, чтобы добавлять все свои дополнения к ограничивающей рамке, когда вы хотите ее выписать, вы можете добавить их раньше времени в Figure. Используя что-то похожее на ответ Франка Дернонкур :

import matplotlib.pyplot as plt

# data 
all_x = [10,20,30]
all_y = [[1,3], [1.5,2.9],[3,2]]

# plotting function
def gen_plot(x, y):
    fig = plt.figure(1)
    ax = fig.add_subplot(111)
    ax.plot(all_x, all_y)
    lgd = ax.legend( [ "Lag " + str(lag) for lag in all_x], loc="center right", bbox_to_anchor=(1.3, 0.5))
    fig.artists.append(lgd) # Here's the change
    ax.set_title("Title")
    ax.set_xlabel("x label")
    ax.set_ylabel("y label")
    return fig

# plotting
fig = gen_plot(all_x, all_y)

# No need for `bbox_extra_artists`
fig.savefig("image_output.png", dpi=300, format="png", bbox_inches="tight")

Вот генерируемый сюжет.

8
задан zx485 3 June 2016 в 11:04
поделиться

3 ответа

Осторожно: _mm256_fmadd_ps не является частью AVX1. FMA3 имеет свой собственный бит функции и был представлен только на Intel с Haswell. AMD представила FMA3 с Piledriver (AVX1 + FMA4 + FMA3, без AVX2).


На уровне asm, если вы хотите получить восемь 32-битных элементов в целочисленные регистры, на самом деле быстрее хранить стек, а затем выполнять скалярные нагрузки. pextrd представляет собой 2-юп-инструкцию для семейства SnB и семейства Bulldozer. (и Nehalem и Silvermont, которые не поддерживают AVX).

Единственным процессором, где vextractf128 + 2x movd + 6x pextrd не страшно, является AMD Jaguar. (дешевый pextrd и только один порт нагрузки.) (см. таблицы insner Agner Fog )

Широкое ориентированное хранилище может переходить на перекрывающиеся узкие нагрузки. (Конечно, вы можете использовать movd для получения элемента low, поэтому у вас есть соединение с портом загрузки и портом ALU).


Конечно, вы, кажется, извлекаете float s, используя целочисленный экстракт, а затем преобразовывая его обратно в float. Это кажется ужасным.

Что вам действительно нужно, каждый float в нижнем элементе собственного xmm-регистра. vextractf128, очевидно, способ начать, приведя элемент 4 к нижней части новой xmm reg. Тогда 6x AVX shufps может легко получить остальные три элемента каждой половины. (Или movshdup и movhlps имеют более короткие кодировки: нет непосредственного байта).

7 shuffle uops заслуживают рассмотрения против 1 магазина и 7 загрузочных дисков, но не если вы собираетесь проливать вектор для вызова функции.


соображения ABI:

Вы находитесь в Windows, где xmm6-15 защищены от вызова (только low128; верхние половины ymm6 -15 сбрасываются с помощью вызова). Это еще одна причина для начала работы с vextractf128.

В SysV ABI все регистры xmm / ymm / zmm вызываются вызовом, поэтому для каждой функции print() требуется пролитие / перезагрузка. Единственное, что нужно сделать, - сохранить память в памяти и вызвать print с исходным вектором (т. Е. Напечатать нижний элемент, потому что он будет игнорировать остальную часть регистра). Затем movss xmm0, [rsp+4] и называть print на втором элементе и т. Д.

Вам нехорошо получить все 8 поплавков, красиво распакованных в 8 векторных рег, потому что все они должны разливаться отдельно так или иначе перед первым вызовом функции!

3
ответ дан Peter Cordes 31 August 2018 в 16:32
поделиться

(Неоконченный ответ. В любом случае, если он помогает кому-либо, или в случае, если я вернусь к нему. Как правило, если вам нужно взаимодействовать со скаляром, который вы не можете векторизовать, неплохо просто сохранить вектор в локальном массив, а затем перезагрузите его по одному элементу за раз.)


См. мой другой ответ для деталей asm. Этот ответ о C ++ стороне вещей.


Используя библиотеку векторных классов Agner Fog , его классы-оболочки перегружают operator[], чтобы работать точно так, как вы ожидали , даже для непостоянных аргументов. Это часто компилируется в хранилище / перезагрузке, но упрощает запись кода на C ++. При включенной оптимизации вы, вероятно, получите достойные результаты. (за исключением того, что нижний элемент может быть сохранен / перезагружен, а не просто использоваться на месте. Поэтому вам может понадобиться специальный случай vec[0] в _mm_cvtss_f32(vec) или что-то в этом роде.)

См. также мой github repo с большинством непроверенных изменений в VCL Agner, чтобы генерировать лучший код для некоторых функций.


Существует макрос оболочки _MM_EXTRACT_FLOAT , но это странно и определяется только SSE4.1. Я думаю, что он предназначен для SSE4.1 extractps (который может извлекать двоичное представление float в целочисленный регистр или хранить в памяти). Он gcc компилирует его в перетасовку FP, когда пункт назначения является float. Будьте внимательны, если другие компиляторы не скомпилируют его в действительную инструкцию extractps, если вы хотите получить результат как float, потому что это не то, что extractps . (Это то, что insertps делает , но более простая перетасовка FP займет меньше байтов команд, например shufps с AVX отлично.)

Странно, потому что требуется 3 args: _MM_EXTRACT_FLOAT(dest, src_m128, idx), поэтому вы даже не можете использовать его как инициализатор для локального float.


Чтобы перебрать вектор

, gcc развит петля для вас, но только с -O1 или выше. На -O0 вы получите сообщение об ошибке.

float bad_hsum(__m128 & fv) {
    float sum = 0;
    for (int i=0 ; i<4 ; i++) {
        float f;
        _MM_EXTRACT_FLOAT(f, fv, i);  // works only with -O1 or higher
        sum += f;
    }
    return sum;
}
1
ответ дан Community 31 August 2018 в 16:32
поделиться

Предполагая, что у вас есть только AVX (т. е. нет AVX2), вы можете сделать что-то вроде этого:

float extract_float(const __m128 v, const int i)
{
    float x;
    _MM_EXTRACT_FLOAT(x, v, i);
    return x;
}

void print(const __m128 v)
{
    print(extract_float(v, 0));
    print(extract_float(v, 1));
    print(extract_float(v, 2));
    print(extract_float(v, 3));
}

void print(const __m256 v)
{
    print(_mm256_extractf128_ps(v, 0));
    print(_mm256_extractf128_ps(v, 1));
}

Однако я думаю, что, вероятно, просто использовал бы союз:

union U256f {
    __m256 v;
    float a[8];
};

void print(const __m256 v)
{
    const U256f u = { v };

    for (int i = 0; i < 8; ++i)
        print(u.a[i]);
}
3
ответ дан Paul R 31 August 2018 в 16:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: