首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C#中的数学优化

C#中的数学优化
EN

Stack Overflow用户
提问于 2009-01-05 00:57:54
回答 13查看 26.3K关注 0票数 61

我一整天都在分析一个应用程序,在优化了几个代码后,我的待办事项清单上就剩下这个了。这是一个神经网络的激活函数,它被调用了超过1亿次。根据dotTrace的说法,它相当于整个功能时间的60%左右。

您将如何优化此功能?

代码语言:javascript
运行
复制
public static float Sigmoid(double value) {
    return (float) (1.0 / (1.0 + Math.Pow(Math.E, -value)));
}
EN

回答 13

Stack Overflow用户

发布于 2009-01-05 01:05:56

在1亿次调用时,我会开始怀疑分析器的开销是否会影响结果。将计算结果替换为no-op,看看是否仍报告消耗了60%的执行时间……

或者更好的是,创建一些测试数据,并使用秒表计时器来分析大约一百万个呼叫。

票数 8
EN

Stack Overflow用户

发布于 2009-01-05 11:15:18

如果您能够与C++进行互操作,则可以考虑将所有值存储在一个数组中,并使用SSE遍历它们,如下所示:

代码语言:javascript
运行
复制
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纹理并运行非常简单的片段着色器)。

票数 8
EN

Stack Overflow用户

发布于 2009-01-05 03:35:55

女高音有一些很好的优化你的电话:

代码语言:javascript
运行
复制
public static float Sigmoid(double value) 
{
    float k = Math.Exp(value);
    return k / (1.0f + k);
}

如果您尝试查找表,并且发现它使用了太多的内存,那么您可以始终查看每个连续调用的参数值,并使用一些缓存技术。

例如,尝试缓存最后一个值和结果。如果下一次调用与上一次调用具有相同的值,则不需要计算它,因为您已经缓存了上一次结果。如果当前调用与上一次调用相同,即使100次中只有1次,您可能会为自己节省100万次计算。

或者,您可能会发现,在10个连续调用中,value参数平均相同2次,因此您可以尝试缓存最后10个值/答案。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/412019

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档