标题没有更好的名字,我不确定我是否能够足够清楚地解释自己。我正在寻找一种通过索引访问“数据类型”的方法,但不强制编译器将其保存在数组中。问题发生在编写基于 SSE/AVX 内在函数的低级代码时。
为了便于编程,我想编写如下代码,在“寄存器”(数据类型__m512)上使用固定长度循环:
inline void load(__m512 *vector, const float *in)
{
for(int i=0; i<24; i++)
vector[i] = _mm512_load_ps((in + i*SIMD_WIDTH));
}
// similarely: inline add(...) and inline store(...)
void add(float *in_out, const float *in)
{
__m512 vector1[24];
__m512 vector2[24];
load(vector1, in_out);
load(vector2, in);
add(vector1, vector2);
store(in_out, vector1);
}
vector1 和 vector2 被定义为数组的事实对编译器来说似乎很麻烦(在我的例子中是 icc):看起来被迫使其“可寻址”,将其保存在堆栈中,从而生成大量我不需要的 load 和 store 指令。据我所知,这是为了允许使用 vector1 或 vector2 进行指针运算。
我希望编译器将所有内容仅保存在寄存器中。我知道这是是可能的:如果我编写的代码没有__m512 数组但有许多个人 __m512 变量编译器生成更好的代码。
解决方案:
我(未成功)尝试使用如下类(我曾希望 switch 语句和其他所有内容都得到优化):
class Vector
{
inline __m512 & operator[](int i)
{
switch(i)
case 0: return component0;
// ...
case 23: return component23;
}
__m512 component0;
// ...
__m512 component23;
};
我也考虑过宏,但找不到好的解决方案。
有什么建议吗?
谢谢,
西蒙
在下面的答案中的评论之后,这里是我想要做的更详细的示例(尽管这仍然是一个简化):
inline void project(__m512 *projected_vector, __m512 *vector)
{
for(int i=0; i<3; i++)
projected_vector[i] = _mm512_add_ps(vector[i], vector[i+3]);
}
inline void matrix_multiply(__m512 *out, const float *matrix, __m512 *in)
{
for(int i=0; i<3; i++)
{
out[i] = _mm512_mul_ps( matrix[3*i+0], in[0]);
out[i] = _mm512_fmadd_ps(matrix[3*i+1], in[1], out[i]);
out[i] = _mm512_fmadd_ps(matrix[3*i+2], in[2], out[i]);
}
}
inline void reconstruct(__m512 *vector, __m512 *projected_vector)
{
for(int i=0; i<3; i++)
vector[i] = _mm512_add_ps(vector[i], projected_vector[i]);
for(int i=0; i<3; i++)
vector[i+3] = _mm512_sub_ps(vector[i], projected_vector[i]);
}
inline void hopping_term(float *in_out, const float *matrix_3x3, const float *in)
{
__m512 vector_in[6];
__m512 vector_out[6];
__m512 half_vector1[3];
__m512 half_vector2[3];
load(vector_in, in);
project(half_vector1, vector_in);
matrix_multiply(half_vector2, matrix_3x3, half_vector1);
load(vector_out, in_out);
reconstruct(vector_out, half_vector2);
store(in_out, vector_out);
}
最佳答案
我最近使用模板元编程做了一些与此非常相似的事情。在下面的代码中,我认为您只需将 _mm256 替换为 _mm512 并将 SIMD_WIDTH 更改为 16。这应该展开你的循环 24 次。
#include <x86intrin.h>
#define SIMD_WIDTH 8
#define LEN 24*SIMD_WIDTH
template<int START, int N>
struct Repeat {
static void add (float * x, float * y, float * z) {
_mm256_store_ps(&z[START],_mm256_add_ps(_mm256_load_ps(&x[START]) ,_mm256_load_ps(&y[START])));
Repeat<START+SIMD_WIDTH, N>::add(x,y,z);
}
};
template<int N>
struct Repeat<LEN, N> {
static void add (float * x, float * y, float * z) {}
};
void sum_unroll(float *x, float *y, float *z, const int n) {
Repeat<0,LEN>::add(x,y,z);
}
当我用 g++ -mavx -O3 -S -masm=intel 编译时,程序集看起来像
vmovaps ymm0, YMMWORD PTR [rdi]
vaddps ymm0, ymm0, YMMWORD PTR [rsi]
vmovaps YMMWORD PTR [rdx], ymm0
vmovaps ymm0, YMMWORD PTR [rdi+32]
vaddps ymm0, ymm0, YMMWORD PTR [rsi+32]
vmovaps YMMWORD PTR [rdx+32], ymm0
vmovaps ymm0, YMMWORD PTR [rdi+64]
vaddps ymm0, ymm0, YMMWORD PTR [rsi+64]
vmovaps YMMWORD PTR [rdx+64], ymm0
vmovaps ymm0, YMMWORD PTR [rdi+96]
vaddps ymm0, ymm0, YMMWORD PTR [rsi+96]
vmovaps YMMWORD PTR [rdx+96], ymm0
vmovaps ymm0, YMMWORD PTR [rdi+128]
vaddps ymm0, ymm0, YMMWORD PTR [rsi+128]
vmovaps YMMWORD PTR [rdx+128], ymm0
...
vmovaps ymm0, YMMWORD PTR [rdi+736]
vaddps ymm0, ymm0, YMMWORD PTR [rsi+736]
vmovaps YMMWORD PTR [rdx+736], ymm0
我最近成功地使用了这种方法,我试图一次推送 4 个微操作(更多使用融合)。例如,在一个时钟周期内执行两次加载、一次存储和一次两次 FMA。我检查了结果以确保没有错误。在执行此操作的一些测试中,我能够稍微提高效率。
编辑:这是一个基于 OP 更新问题的解决方案。我在 pass 中对 SIMD 变量使用数组时也遇到了问题,所以我通常不对它们使用数组。此外,由于寄存器重命名,我很少需要使用许多 SIMD 寄存器(我认为我使用最多的是 11 个)。在这个例子中,只有五个是必需的。
#include <x86intrin.h>
#define SIMD_WIDTH 8
static inline __m256 load(const float *in) {
return _mm256_loadu_ps(in);
}
inline void store(float *out, __m256 const &vector) {
_mm256_storeu_ps(out, vector);
}
inline __m256 project(__m256 const &a, __m256 const &b) {
return _mm256_add_ps(a, b);
}
inline void reconstruct(__m256 &vector1, __m256 &vector2, __m256 &projected_vector) {
vector1 = _mm256_add_ps(vector1, projected_vector);
vector2 = _mm256_sub_ps(vector1, projected_vector);
}
class So {
public:
__m256 half_vector[3];
So(const float *in) {
for(int i=0; i<3; i++)
half_vector[i] = project(load(&in[i*SIMD_WIDTH]), load(&in[(3+i)*SIMD_WIDTH]));
}
__m256 matrix_multiply(const float *matrix) {
__m256 out;
out = _mm256_mul_ps(_mm256_loadu_ps(&matrix[0]), half_vector[0]);
out = _mm256_fmadd_ps(_mm256_loadu_ps(&matrix[1]), half_vector[1], out);
out = _mm256_fmadd_ps(_mm256_loadu_ps(&matrix[2]), half_vector[2], out);
return out;
}
};
void hopping_term(float *in_out, const float *matrix_3x3, const float *in)
{
So so(in);
for(int i=0; i<3; i++) {
__m256 vector_out1, vector_out2;
__m256 half_vector2 = so.matrix_multiply(&matrix_3x3[3*i]);
vector_out1 = load(&in_out[i*SIMD_WIDTH]);
reconstruct(vector_out1, vector_out2, half_vector2);
store(&in_out[(0+i)*SIMD_WIDTH], vector_out1);
store(&in_out[(3+i)*SIMD_WIDTH], vector_out2);
}
}
这只使用了五个 AVX 寄存器。这是程序集
vmovups ymm3, YMMWORD PTR [rdx]
vmovups ymm2, YMMWORD PTR [rdx+32]
vaddps ymm3, ymm3, YMMWORD PTR [rdx+96]
vmovups ymm0, YMMWORD PTR [rdx+64]
vaddps ymm2, ymm2, YMMWORD PTR [rdx+128]
vaddps ymm0, ymm0, YMMWORD PTR [rdx+160]
vmulps ymm1, ymm3, YMMWORD PTR [rsi]
vfmadd231ps ymm1, ymm2, YMMWORD PTR [rsi+4]
vfmadd231ps ymm1, ymm0, YMMWORD PTR [rsi+8]
vaddps ymm4, ymm1, YMMWORD PTR [rdi]
vsubps ymm1, ymm4, ymm1
vmovups YMMWORD PTR [rdi], ymm4
vmovups YMMWORD PTR [rdi+96], ymm1
vmulps ymm1, ymm3, YMMWORD PTR [rsi+12]
vfmadd231ps ymm1, ymm2, YMMWORD PTR [rsi+16]
vfmadd231ps ymm1, ymm0, YMMWORD PTR [rsi+20]
vaddps ymm4, ymm1, YMMWORD PTR [rdi+32]
vsubps ymm1, ymm4, ymm1
vmovups YMMWORD PTR [rdi+32], ymm4
vmovups YMMWORD PTR [rdi+128], ymm1
vmulps ymm3, ymm3, YMMWORD PTR [rsi+24]
vfmadd132ps ymm2, ymm3, YMMWORD PTR [rsi+28]
vfmadd132ps ymm0, ymm2, YMMWORD PTR [rsi+32]
vaddps ymm1, ymm0, YMMWORD PTR [rdi+64]
vsubps ymm0, ymm1, ymm0
vmovups YMMWORD PTR [rdi+64], ymm1
vmovups YMMWORD PTR [rdi+160], ymm0
关于c++ - 临时/"non-addressable"固定大小数组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25812252/
我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,
我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou
我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-
我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代
我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby数组,我们在StackOverflow上找到一
我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
我正在使用puppet为ruby程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这
这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife