我一整天都在分析一个应用程序,在优化了几个代码后,我的待办事项清单上就剩下这个了。这是一个神经网络的激活函数,它被调用了超过1亿次。根据dotTrace的说法,它相当于整个功能时间的60%左右。
您将如何优化此功能?
public static float Sigmoid(double value) {
return (float) (1.0 / (1.0 + Math.Pow(Math.E, -value)));
}发布于 2009-01-05 01:05:56
在1亿次调用时,我会开始怀疑分析器的开销是否会影响结果。将计算结果替换为no-op,看看是否仍报告消耗了60%的执行时间……
或者更好的是,创建一些测试数据,并使用秒表计时器来分析大约一百万个呼叫。
发布于 2009-01-05 11:15:18
如果您能够与C++进行互操作,则可以考虑将所有值存储在一个数组中,并使用SSE遍历它们,如下所示:
void sigmoid_sse(float *a_Values, float *a_Output, size_t a_Size){
__m128* l_Output = (__m128*)a_Output;
__m128* l_Start = (__m128*)a_Values;
__m128* l_End = (__m128*)(a_Values + a_Size);
const __m128 l_One = _mm_set_ps1(1.f);
const __m128 l_Half = _mm_set_ps1(1.f / 2.f);
const __m128 l_OneOver6 = _mm_set_ps1(1.f / 6.f);
const __m128 l_OneOver24 = _mm_set_ps1(1.f / 24.f);
const __m128 l_OneOver120 = _mm_set_ps1(1.f / 120.f);
const __m128 l_OneOver720 = _mm_set_ps1(1.f / 720.f);
const __m128 l_MinOne = _mm_set_ps1(-1.f);
for(__m128 *i = l_Start; i < l_End; i++){
// 1.0 / (1.0 + Math.Pow(Math.E, -value))
// 1.0 / (1.0 + Math.Exp(-value))
// value = *i so we need -value
__m128 value = _mm_mul_ps(l_MinOne, *i);
// exp expressed as inifite series 1 + x + (x ^ 2 / 2!) + (x ^ 3 / 3!) ...
__m128 x = value;
// result in l_Exp
__m128 l_Exp = l_One; // = 1
l_Exp = _mm_add_ps(l_Exp, x); // += x
x = _mm_mul_ps(x, x); // = x ^ 2
l_Exp = _mm_add_ps(l_Exp, _mm_mul_ps(l_Half, x)); // += (x ^ 2 * (1 / 2))
x = _mm_mul_ps(value, x); // = x ^ 3
l_Exp = _mm_add_ps(l_Exp, _mm_mul_ps(l_OneOver6, x)); // += (x ^ 3 * (1 / 6))
x = _mm_mul_ps(value, x); // = x ^ 4
l_Exp = _mm_add_ps(l_Exp, _mm_mul_ps(l_OneOver24, x)); // += (x ^ 4 * (1 / 24))
#ifdef MORE_ACCURATE
x = _mm_mul_ps(value, x); // = x ^ 5
l_Exp = _mm_add_ps(l_Exp, _mm_mul_ps(l_OneOver120, x)); // += (x ^ 5 * (1 / 120))
x = _mm_mul_ps(value, x); // = x ^ 6
l_Exp = _mm_add_ps(l_Exp, _mm_mul_ps(l_OneOver720, x)); // += (x ^ 6 * (1 / 720))
#endif
// we've calculated exp of -i
// now we only need to do the '1.0 / (1.0 + ...' part
*l_Output++ = _mm_rcp_ps(_mm_add_ps(l_One, l_Exp));
}
}但是,请记住,您将使用的数组应该使用_aligned_malloc(some_size * sizeof(float),16)进行分配,因为SSE需要与边界对齐的内存。
使用SSE,我可以在大约半秒内计算出所有1亿个元素的结果。然而,一次分配这么多的内存将花费您近三分之二的at,所以我建议一次处理更多但更小的数组。您甚至可能想要考虑使用具有100K或更多元素的双缓冲方法。
此外,如果元素的数量开始大幅增加,您可能希望选择在图形处理器上处理这些东西(只需创建一维float4纹理并运行非常简单的片段着色器)。
发布于 2009-01-05 03:35:55
女高音有一些很好的优化你的电话:
public static float Sigmoid(double value)
{
float k = Math.Exp(value);
return k / (1.0f + k);
}如果您尝试查找表,并且发现它使用了太多的内存,那么您可以始终查看每个连续调用的参数值,并使用一些缓存技术。
例如,尝试缓存最后一个值和结果。如果下一次调用与上一次调用具有相同的值,则不需要计算它,因为您已经缓存了上一次结果。如果当前调用与上一次调用相同,即使100次中只有1次,您可能会为自己节省100万次计算。
或者,您可能会发现,在10个连续调用中,value参数平均相同2次,因此您可以尝试缓存最后10个值/答案。
https://stackoverflow.com/questions/412019
复制相似问题