【问题标题】:Variable running time of a C programC程序的可变运行时间
【发布时间】:2011-06-18 02:44:45
【问题描述】:

我的 (simd) 实现需要不同的时间,尽管它是针对固定输入运行的。运行时间在 1 亿个时钟周期到 1.2 亿个时钟周期之间变化。该程序调用一个函数大约 600 次,而函数中最昂贵的部分是它的内存被访问了大约 2000 次。因此,我的程序中的整体内存参与度相当高。

运行时间的变化是由于内存访问模式/初始内存内容吗?

我使用 valgrind 来分析我的程序。它显示每次内存访问大约需要 8 条指令。这正常吗?

以下是被调用 600 次的代码(函数)。 Mulprev[32][20] 是访问次数最多的数组。

j = 15;  
u3v = _mm_set_epi64x (0xF, 0xF);
while (j + 1)  
{

    l = j << 2;  
    for (i = 0; i < 20; i++)
    {
        val1v   = _mm_load_si128 ((__m128i *) &elm1v[i]);       
        uv  = _mm_and_si128 (_mm_srli_epi64 (val1v, l), u3v);
        u1  = _mm_extract_epi16 (uv, 0);
        u2  = _mm_extract_epi16 (uv, 4) + 16;

        for (ival = i, ival1 = i + 1, k = 0; k < 20; k += 2, ival += 2, ival1 += 2)
        {
            temp11v = _mm_load_si128 ((__m128i *) &mulprev[u1][k]); 
            temp12v = _mm_load_si128 ((__m128i *) &mulprev[u2][k]);

            val1v   = _mm_load_si128 ((__m128i *) &res[ival]);
            val2v   = _mm_load_si128 ((__m128i *) &res[ival1]); 

            bv  = _mm_xor_si128 (val1v, _mm_unpacklo_epi64 (temp11v, temp12v));
            av  = _mm_xor_si128 (val2v, _mm_unpackhi_epi64 (temp11v, temp12v));

            _mm_store_si128 ((__m128i *) &res[ival], bv);                                   
            _mm_store_si128 ((__m128i *) &res[ival1], av); 
        }
    }

    if (j == 0)
        break;
    val0v = _mm_setzero_si128 ();

    for (i = 0; i < 40; i++)
    {
        testv   = _mm_load_si128 ((__m128i *)  &res[i]);
        val1v   = _mm_srli_epi64 (testv, 60);
        val2v   = _mm_xor_si128  (val0v, _mm_slli_epi64 (testv, 4));
        _mm_store_si128 (&res[i], val2v);
        val0v   = val1v;
    }
    j--;
}       

我想减少程序的计算时间。有什么建议吗?

【问题讨论】:

  • 如果您需要帮助优化它,您需要发布实际代码
  • 请看已编辑的问题..

标签: c optimization memory sse


【解决方案1】:

您在加载和存储之间几乎不执行任何计算,因此您的执行时间很可能主要取决于缓存/内存的 I/O 成本。更糟糕的是,您的数据集似乎相对较小。可能您可以进一步优化的唯一方法是改进内存访问模式(尽可能使访问顺序,并确保不浪费缓存行等)和/或将这些操作与在同一数据集上操作的其他代码组合在此例程之前/之后(以便在某种程度上摊销加载/存储的成本)。

编辑:请注意,当您对该例程的明显较早版本提出相同的问题时,我给出了非常相似的答案:How to make the following code faster - 您似乎错过了您的主要性能问题是内存访问这一点,不是计算。

【讨论】:

    【解决方案2】:

    计算机很复杂。很容易以某种方式干扰后台进程。如果没有其他信息,很难提出改进建议。通常,最好的优化是高级优化。选择更好的算法,尽量减少昂贵的操作。如果您认为那里没有太大的改进空间,请不要期望过高的收益。你说你的内存访问需要很多周期。我可以建议您尽可能使用受限指针,但很难就优化问题给出一般建议。您必须自己尝试一下。

    【讨论】:

    【解决方案3】:

    8 个周期的内存访问是相当长的时间。另一个进程可能对 CPU 缓存产生负面影响,导致您的程序出现大量缓存未命中,或者如果您的内存是动态分配的,您可能会看到未对齐的内存访问惩罚。

    可以是任何东西。

    【讨论】:

    • 你能给我一个假设命中的典型 L1 缓存读取时间吗?如果数据在缓存中,无论它在缓存中的位置如何,都可以保证获取时间相似。有参考吗?
    • 8 个周期对于内存访问来说是 NOT 很长的时间。如果缓存未命中一直到 DRAM,则在现代 CPU 上可能需要 100 个周期甚至更多。
    • @anup, @Axel:现代 CPU 上的 L1 延迟通常约为 4 个周期(有关详细信息,请参阅英特尔优化指南或特定于处理器的文档)。值得注意的是,anup 实际上说“[Valgrind] 显示每次内存访问大约需要 8 条指令”。指令不是循环;现代英特尔 CPU 每个周期可能会退出 4 条指令,因此“8 条指令”实际上可能只意味着几个周期,与 L1 命中一致。但是我对 Valgrind 并不熟悉,所以我不知道它的实际测量结果如何。
    • @Stephen:我说的是DRAM,而不是缓存。
    猜你喜欢
    • 2011-02-05
    • 2016-10-21
    • 1970-01-01
    • 2012-03-15
    • 1970-01-01
    • 1970-01-01
    • 2015-06-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多