Вот еще одно решение, похожее на добавление 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")
Осторожно: _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 загрузочных дисков, но не если вы собираетесь проливать вектор для вызова функции.
Вы находитесь в Windows, где xmm6-15 защищены от вызова (только low128; верхние половины ymm6 -15 сбрасываются с помощью вызова). Это еще одна причина для начала работы с vextractf128
.
В SysV ABI все регистры xmm / ymm / zmm вызываются вызовом, поэтому для каждой функции print()
требуется пролитие / перезагрузка. Единственное, что нужно сделать, - сохранить память в памяти и вызвать print
с исходным вектором (т. Е. Напечатать нижний элемент, потому что он будет игнорировать остальную часть регистра). Затем movss xmm0, [rsp+4]
и называть print
на втором элементе и т. Д.
Вам нехорошо получить все 8 поплавков, красиво распакованных в 8 векторных рег, потому что все они должны разливаться отдельно так или иначе перед первым вызовом функции!
(Неоконченный ответ. В любом случае, если он помогает кому-либо, или в случае, если я вернусь к нему. Как правило, если вам нужно взаимодействовать со скаляром, который вы не можете векторизовать, неплохо просто сохранить вектор в локальном массив, а затем перезагрузите его по одному элементу за раз.)
См. мой другой ответ для деталей 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;
}
Предполагая, что у вас есть только 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]);
}