c# - 通过 C 和/或汇编帮助优化 C# 函数

标签 c# c optimization assembly

我正在尝试优化此 C# 方法:

// assume arrays are same dimensions
private void DoSomething(int[] bigArray1, int[] bigArray2)
{
    int data1;
    byte A1, B1, C1, D1;
    int data2;
    byte A2, B2, C2, D2;
    for (int i = 0; i < bigArray1.Length; i++)
    {
        data1 = bigArray1[i];
        data2 = bigArray2[i];

        A1 = (byte)(data1 >> 0);
        B1 = (byte)(data1 >> 8);
        C1 = (byte)(data1 >> 16);
        D1 = (byte)(data1 >> 24);

        A2 = (byte)(data2 >> 0);
        B2 = (byte)(data2 >> 8);
        C2 = (byte)(data2 >> 16);
        D2 = (byte)(data2 >> 24);

        A1 = A1 > A2 ? A1 : A2;
        B1 = B1 > B2 ? B1 : B2;
        C1 = C1 > C2 ? C1 : C2;
        D1 = D1 > D2 ? D1 : D2;

        bigArray1[i] = (A1 << 0) | (B1 << 8) | (C1 << 16) | (D1 << 24); 
    }
}

该函数基本上比较两个 int 数组。对于每对匹配元素,该方法比较每个单独的字节值并取两者中较大的一个。然后为第一个数组中的元素分配一个新的 int 值,该值由 4 个最大的字节值构成(与源无关)。

认为我已经在 C# 中尽可能多地优化了这个方法(当然,我可能还没有——也欢迎就此提出建议)。我的问题是,将此方法移至非托管 C DLL 是否值得?考虑到编码托管 int 数组以便将它们传递给方法?

如果这样做能让我提高 10% 的速度,那么肯定不值得我花时间。如果它快 2 或 3 倍,那么我可能不得不这样做。

注意:请不要有“过早优化”的评论,在此先感谢。这简直就是“优化”。

更新:我意识到我的代码示例没有捕捉到我在此函数中尝试做的所有事情,所以这是一个更新版本:

private void DoSomethingElse(int[] dest, int[] src, double pos, 
    double srcMultiplier)
{
    int rdr;
    byte destA, destB, destC, destD;
    double rem = pos - Math.Floor(pos);
    double recipRem = 1.0 - rem;
    byte srcA1, srcA2, srcB1, srcB2, srcC1, srcC2, srcD1, srcD2;
    for (int i = 0; i < src.Length; i++)
    {
        // get destination values
        rdr = dest[(int)pos + i];
        destA = (byte)(rdr >> 0);
        destB = (byte)(rdr >> 8);
        destC = (byte)(rdr >> 16);
        destD = (byte)(rdr >> 24);
        // get bracketing source values
        rdr = src[i];
        srcA1 = (byte)(rdr >> 0);
        srcB1 = (byte)(rdr >> 8);
        srcC1 = (byte)(rdr >> 16);
        srcD1 = (byte)(rdr >> 24);
        rdr = src[i + 1];
        srcA2 = (byte)(rdr >> 0);
        srcB2 = (byte)(rdr >> 8);
        srcC2 = (byte)(rdr >> 16);
        srcD2 = (byte)(rdr >> 24);
        // interpolate (simple linear) and multiply
        srcA1 = (byte)(((double)srcA1 * recipRem) + 
            ((double)srcA2 * rem) * srcMultiplier);
        srcB1 = (byte)(((double)srcB1 * recipRem) +
            ((double)srcB2 * rem) * srcMultiplier);
        srcC1 = (byte)(((double)srcC1 * recipRem) +
            ((double)srcC2 * rem) * srcMultiplier);
        srcD1 = (byte)(((double)srcD1 * recipRem) +
            ((double)srcD2 * rem) * srcMultiplier);
        // bytewise best-of
        destA = srcA1 > destA ? srcA1 : destA;
        destB = srcB1 > destB ? srcB1 : destB;
        destC = srcC1 > destC ? srcC1 : destC;
        destD = srcD1 > destD ? srcD1 : destD;
        // convert bytes back to int
        dest[i] = (destA << 0) | (destB << 8) |
            (destC << 16) | (destD << 24);
    }
}

基本上这与第一种方法做同样的事情,除了在这个方法中第二个数组(src)总是小于第一个(dest),并且第二个数组相对于第一个数组的位置是小数点(这意味着它可以定位在 10.682791,而不是相对于 dest 的位置,比如说 10)。

为实现这一点,我必须在源中的两个括号值之间进行插值(例如,上例中的 10 和 11,对于第一个元素),然后将插值字节与目标字节进行比较。

我怀疑此函数中涉及的乘法比字节比较的成本要高得多,因此这部分可能是转移注意力(抱歉)。此外,即使比较相对于乘法仍然有些昂贵,我仍然有这个系统实际上可以是多维的问题,这意味着不是比较一维数组,数组可以是 2-、5- 或无论维度如何,因此最终计算内插值所花费的时间将使最终按字节比较 4 个字节所花费的时间相形见绌(我假设是这种情况)。

这里的乘法相对于移位有多昂贵,这种操作可以通过卸载到 C DLL(甚至是汇编 DLL,尽管我必须雇人)来加速为我创造)?

最佳答案

是的,_mm_max_epu8() 内在函数可以满足您的需求。一次咀嚼 16 个字节。痛点是数组。 SSE2 指令要求它们的参数在 16 字节地址处对齐。你不能从垃圾收集堆中得到它,它只保证 4 字节对齐。即使您通过计算数组中 16 字节对齐的偏移量来欺骗它,那么当垃圾收集器启动并移动数组时,您也会失败。

您必须使用 __declspec(align(#)) 声明符在 C/C++ 代码中声明数组。现在您需要将托管阵列复制到那些非托管阵列中。结果回来了。您是否仍然领先取决于您的问题中不容易看到的细节。

关于c# - 通过 C 和/或汇编帮助优化 C# 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2938762/

相关文章:

c# - 处理不同命名空间中重复类的最佳实践

c# - 不使用图形对象测量字符串?

c - IPC 使用 fork() 和 pipe()

user-interface - 我应该隐藏还是销毁 UI 元素?

mysql - 如何优化 MySQL ORDER BY + 巨大的 LIMIT Offsets

c# - 为什么我的用户控件绘制事件不只在一个控件上填充我的矩形?

c# - 什么时候使用 : Tuple vs Class in C# 7. 0

c - 为什么C比较3等于3.0?

Python:GIL 上下文——切换

c# - 递归处理文件夹中文件的快速(低级)方法