好的,所以我一直在使用操作符重载一些SSE/AVX本质,以便在向量处理有用的更简单的情况下使用它们。类定义如下所示:
#define Float16a float __attribute__((__aligned__(16)))
class sse
{
private:
__m128 vec __attribute__((__aligned__(16)));
Float16a *temp;
public:
//=================================================================
sse();
sse(float *value);
//=================================================================
void operator + (float *param);
void operator - (float *param);
void operator * (float *param);
void operator / (float *param);
void operator % (float *param);
void operator ^ (int number);
void operator = (float *param);
void operator == (float *param);
void operator += (float *param);
void operator -= (float *param);
void operator *= (float *param);
void operator /= (float *param);
};每一项职能都与以下各点相似:
void sse::operator + (float *param)
{
vec = _mm_add_ps(vec, _mm_load_ps(param));
_mm_store_ps(temp, vec);
}到目前为止,我在编写代码时很少遇到问题,但是我遇到了一些性能问题,当使用SSE/AVX代码时,与普通的标量代码相比,性能有了很大的提高。我知道这种类型的代码可能是困难的配置文件,但我甚至不确定瓶颈到底是什么。如果有任何指针可以扔到我身上,我将不胜感激。
请注意,这只是一个person项目,我写这个项目是为了进一步了解SSE/AVX,所以用外部库替换它不会有多大帮助。
发布于 2013-08-20 02:27:04
在我看来,您正在引入的开销数量很容易超过通过使用SSE操作获得的任何速度。
如果不看生成的程序集,我就不能确切地说出正在发生什么,但是这里有两种可能的开销形式。
调用函数(除非是内联的)涉及call和ret,很可能是push和pop等等。若要创建堆栈框架,请执行以下操作。
您正在为每个操作调用_mm_store_ps,如果您将一个以上的操作链接在一起,您将比需要支付更多的成本。
此外,从您的代码中还不清楚这是否是一个问题,但是请确保temp是一个有效的指针。
希望这能有所帮助。祝好运。
跟进,征求意见。
不确定这是不是好的C++,请教育我,如果不是,但这是我的建议,鉴于我的有限的知识。如果其他人有更好的建议我会很感兴趣的。
使用我认为被称为“转换操作符”的内容,但是由于返回不是一个浮点数,而是4个浮点数,所以还需要添加一个类型。
typedef struct float_data
{
float data[4];
};
class sse
{
...
float_data floatData;
...
operator float_data&();
...
};
sse::operator float_data&()
{
_mm_store_ps(floatData.data, vec);
return &float_data;
}发布于 2013-08-20 05:58:59
如果我的SSE库。在处理大量数据时,我总是使用SoA的SoA整数。M 128/M 256的操作符重载使C/C++算法易于转换为SIMD算法。
库不支持Load/Store,因为SSE/AVX对内存操作非常敏感。内存访问不佳会导致数十个CPU周期,并使您的计算延迟。
__forceinline __m128 operator+(__m128 l, __m128 r) { return _mm_add_ps(l,r); }
__forceinline __m128 operator-(__m128 l, __m128 r) { return _mm_sub_ps(l,r); }
__forceinline __m128 operator*(__m128 l, __m128 r) { return _mm_mul_ps(l,r); }
__forceinline __m128 operator/(__m128 l, __m128 r) { return _mm_div_ps(l,r); }
__forceinline __m128 operator&(__m128 l, __m128 r) { return _mm_and_ps(l,r); }
__forceinline __m128 operator|(__m128 l, __m128 r) { return _mm_or_ps(l,r); }
__forceinline __m128 operator<(__m128 l, __m128 r) { return _mm_cmplt_ps(l,r); }
__forceinline __m128 operator>(__m128 l, __m128 r) { return _mm_cmpgt_ps(l,r); }
__forceinline __m128 operator<=(__m128 l, __m128 r) { return _mm_cmple_ps(l,r); }
__forceinline __m128 operator>=(__m128 l, __m128 r) { return _mm_cmpge_ps(l,r); }
__forceinline __m128 operator!=(__m128 l, __m128 r) { return _mm_cmpneq_ps(l,r); }
__forceinline __m128 operator==(__m128 l, __m128 r) { return _mm_cmpeq_ps(l,r); }
__forceinline __m128 _mm_merge_ps(__m128 m, __m128 l, __m128 r)
{
return _mm_or_ps(_mm_andnot_ps(m, l), _mm_and_ps(m, r));
}
struct TPoint4
{
TPoint4() {}
TPoint4(const D3DXVECTOR3& a) :x(_mm_set1_ps(a.x)), y(_mm_set1_ps(a.y)), z(_mm_set1_ps(a.z)) {}
TPoint4(__m128 a, __m128 b, __m128 c) :x(a), y(b), z(c) {}
TPoint4(const __m128* a) :x(a[0]), y(a[1]), z(a[2]) {}
TPoint4(const D3DXVECTOR3& a, const D3DXVECTOR3& b, const D3DXVECTOR3& c, const D3DXVECTOR3& d) :x(_mm_set_ps(a.x,b.x,c.x,d.x)), y(_mm_set_ps(a.y,b.y,c.y,d.y)), z(_mm_set_ps(a.z,b.z,c.z,d.z)) {}
operator __m128* () { return &x; }
operator const __m128* () const { return &x; }
TPoint4 operator+(const TPoint4& r) const { return TPoint4(x+r.x, y+r.y, z+r.z); }
TPoint4 operator-(const TPoint4& r) const { return TPoint4(x-r.x, y-r.y, z-r.z); }
TPoint4 operator*(__m128 r) const { return TPoint4(x * r, y * r, z * r); }
TPoint4 operator/(__m128 r) const { return TPoint4(x / r, y / r, z / r); }
__m128 operator[](int index) const { return _val[index]; }
union
{
struct
{
__m128 x, y, z;
};
struct
{
__m128 _val[3];
};
};
};
__forceinline TPoint4* TPoint4Cross(TPoint4* result, const TPoint4* l, const TPoint4* r)
{
result->x = (l->y * r->z) - (l->z * r->y);
result->y = (l->z * r->x) - (l->x * r->z);
result->z = (l->x * r->y) - (l->y * r->x);
return result;
}
__forceinline __m128 TPoint4Dot(const TPoint4* l, const TPoint4* r)
{
return (l->x * r->x) + (l->y * r->y) + (l->z * r->z);
}
__forceinline TPoint4* TPoint4Normalize(TPoint4* result, const TPoint4* l)
{
__m128 rec_len = _mm_rsqrt_ps( (l->x * l->x) + (l->y * l->y) + (l->z * l->z) );
result->x = l->x * rec_len;
result->y = l->y * rec_len;
result->z = l->z * rec_len;
return result;
}
__forceinline __m128 TPoint4Length(const TPoint4* l)
{
return _mm_sqrt_ps( (l->x * l->x) + (l->y * l->y) + (l->z * l->z) );
}
__forceinline TPoint4* TPoint4Merge(TPoint4* result, __m128 mask, const TPoint4* l, const TPoint4* r)
{
result->x = _mm_merge_ps(mask, l->x, r->x);
result->y = _mm_merge_ps(mask, l->y, r->y);
result->z = _mm_merge_ps(mask, l->z, r->z);
return result;
}
extern __m128 g_zero4;
extern __m128 g_one4;
extern __m128 g_fltMax4;
extern __m128 g_mask4;
extern __m128 g_epsilon4;发布于 2015-09-12 13:27:37
如果您只是在学习SSE,我建议只使用原始的本质,而不使用任何结构。在这种情况下,您可以更容易地看到正在发生的事情,并将性能调整到最佳状态。内部编码与直接在汇编程序中编码几乎是一样的,只是编译器进行寄存器分配和管理内存负载/存储本身的区别。
谈到包装类,它有几个问题:
temp指针。它增加了不必要的数据,这些数据经常被移动。sse类型的参数。如果您传递float*,那么很可能必须从这个指针加载值。然而,在大多数情况下,这是不必要的:数据已经在登记。当您使用__m128类型的值时,编译器可以自行决定是否必须将数据保存/加载到内存中。sse类型的值。现在,您将结果存储到一个内存指针中,该指针是以丑陋的方式实现的。这迫使编译器将数据真正存储到内存中,而不是简单地将值保存在寄存器中。当您按值返回__m128时,编译器凸轮将决定何时保存/加载数据。下面是您为提高性能和可用性而重写的代码:
class sse {
private:
__m128 vec;
public:
explicit sse(float *ptr) { vec = _mm_loadu_ps(ptr); }
sse(__m128 reg) { vec = reg; }
void store(float *ptr) { _mm_storeu_ps(ptr, vec); }
sse operator + (sse other) const {
return sse(_mm_add_ps(vec, other.vec));
}
sse operator - (sse other) {...}
sse operator * (sse other) {...}
sse operator / (sse other) {...}
void operator += (sse other) {
vec = _mm_add_ps(vec, other.vec);
}
void operator -= (float *param) {...}
void operator *= (float *param) {...}
void operator /= (float *param) {...}
//I don't know what you mean by these operators:
//void operator ^ (int number);
//void operator == (float *param);
//sse operator % (sse other);
};在任何情况下,您都应该定期检查编译器生成的程序集,以确定它是否存在性能问题。
https://stackoverflow.com/questions/18325871
复制相似问题