标签: micro-optimization

预解码器和解码器。不同之处

我正在阅读Agner Fog的材料,我有一些疑问:

预解码器和解码器每个时钟周期可处理 16 字节或 4 条指令

  1. 解码器上下文中的预解码器是什么?
  2. 作者谈到了宏指令的缓存。我不知道为什么它有用,毕竟我们有缓存指令。什么是环回缓冲区?

  3. 什么是微操作融合和宏操作融合?

x86 assembly cpu-architecture micro-optimization

1
推荐指数
1
解决办法
855
查看次数

循环内 if(循环不变)if 语句的编译器优化

我在 C 中有一个这样的函数(在伪代码中,删除不重要的部分):

int func(int s, int x, int *a, int *r) {
    int i;

    // do some stuff

    for (i = 0; i < a_really_big_int; ++i) {
        if (s)
            r[i] = x ^ i;
        else
            r[i] = x ^ a[i];
        // and maybe a couple other ways of computing r
        // that are equally fast individually
    }

    // do some other stuff

}
Run Code Online (Sandbox Code Playgroud)

这段代码被调用得太多,以至于这个循环实际上是代码中的速度瓶颈。我想知道几件事:

  1. 由于 switchs是函数中的常量,好的编译器会优化循环,以便分支不会一直减慢速度吗?

  2. 如果没有,优化这段代码的好方法是什么?

====

编辑:这是一个包含更完整示例的更新:

int func(int s,
         int start, int stop, …
Run Code Online (Sandbox Code Playgroud)

c optimization loops micro-optimization compiler-optimization

1
推荐指数
1
解决办法
3504
查看次数

什么 C/C++ 编译器可以使用 push pop 指令来创建局部变量,而不是仅仅增加一次 esp?

我相信 push/pop 指令会产生更紧凑的代码,甚至可能会运行得稍微快一点。不过,这也需要禁用堆栈帧。

为了检查这一点,我需要手动重写一个足够大的汇编程序(比较它们),或者安装和研究一些其他编译器(看看他们是否有这个选项,并比较结果) .

这是有关此问题和类似问题的论坛主题

简而言之,我想了解哪个代码更好。像这样的代码:

sub esp, c
mov [esp+8],eax
mov [esp+4],ecx
mov [esp],edx
...
add esp, c
Run Code Online (Sandbox Code Playgroud)

或这样的代码:

push eax
push ecx
push edx
...
add esp, c
Run Code Online (Sandbox Code Playgroud)

什么编译器可以生成第二种代码?他们通常会产生第一个的一些变体。

c++ x86 assembly micro-optimization compiler-optimization

1
推荐指数
1
解决办法
1905
查看次数

关于否定 mips 中的符号整数?

我正在考虑如何否定 mips32 中的有符号整数。我的直觉是使用 2 的补码的定义,例如:(假设$s0是要否定的数字)

nor $t0, $s0, $s0   ; 1's complement
addiu $t0, $t0, 1   ; 2's = 1's + 1
Run Code Online (Sandbox Code Playgroud)

然后我意识到可以这样做:

sub $t0, $zero, $s0
Run Code Online (Sandbox Code Playgroud)

所以……有什么区别?哪个更快?IIRC sub 会尝试检测溢出,但是这个 make 会更慢吗?最后,有没有其他方法可以做到这一点?

assembly mips cpu-architecture micro-optimization mips32

1
推荐指数
1
解决办法
3143
查看次数

性能损失:非规范化数字与分支错误预测

对于那些已经测量过或对此类注意事项有深入了解的人,假设您必须执行以下操作(仅选择任何示例)浮点运算符:

float calc(float y, float z)
{ return sqrt(y * y + z * z) / 100; }
Run Code Online (Sandbox Code Playgroud)

哪里yz可能是非正规数,让我们假设两种可能的情况,其中只有 y,只有 z,或者两者,以完全随机的方式,可以是非正规数

  • 50%的时间
  • <1% 的时间

现在假设我想避免处理非正规数的性能损失,我只想将它们视为 0,然后通过以下方式更改该段代码:

float calc(float y, float z)
{
   bool yzero = y < 1e-37;
   bool zzero = z < 1e-37;
   bool all_zero = yzero and zzero;
   bool some_zero = yzero != zzero;

   if (all_zero)
      return 0f;

   float ret;

   if (!some_zero) ret = sqrt(y * y + z * z);
   else if (yzero) …
Run Code Online (Sandbox Code Playgroud)

c++ floating-point x86 micro-optimization branch-prediction

1
推荐指数
1
解决办法
455
查看次数

大型数组或列表的 4 桶直方图的微观优化

我有一个特别的问题。我将尝试尽可能准确地描述这一点。

我正在做一个非常重要的“微优化”。一次运行数天的循环。所以如果我能减少这个循环时间,它需要一半的时间。10 天将减少到只有 5 天等。

我现在拥有的循环是函数:“testbenchmark1”。

我有 4 个索引需要在这样的循环中增加。但是当从列表中访问索引时,实际上需要一些额外的时间,正如我所注意到的。这就是我想知道是否有其他解决方案。

indexes[n]++; //increase correct index

“testbenchmark1”的完整代码需要 122 毫秒:

void testbenchmark00()
{
    Random random = new Random();
    List<int> indexers = new List<int>();
    for (int i = 0; i < 9256408; i++)
    {
        indexers.Add(random.Next(0, 4));
    }
    int[] valueLIST = indexers.ToArray();


    Stopwatch stopWatch = new Stopwatch();
    stopWatch.Start();

    int[] indexes = { 0, 0, 0, 0 };
    foreach (int n in valueLIST) //Takes 122 ms
    {
        indexes[n]++; //increase correct index
    }

    stopWatch.Stop();
    MessageBox.Show("stopWatch: " + stopWatch.ElapsedMilliseconds.ToString() …
Run Code Online (Sandbox Code Playgroud)

c# optimization simd histogram micro-optimization

1
推荐指数
1
解决办法
385
查看次数

对于给定月份的天数,是否有一个简单的(可能是混淆的)数学表达式?

用于标准库不可用的情况。假设月份是一个无符号整数。

我很想看到给出正确答案的最短算术表达式,允许或禁止按位运算符和掩码,但不允许查找表。部分表达式可以保存到变量中以提高可读性以展示所使用的想法。

assembly date micro-optimization

1
推荐指数
1
解决办法
120
查看次数

必须按顺序发生的操作的处理器的延迟界限和吞吐量界限

我的教科书(计算机系统:程序员的观点)指出,当一系列操作必须严格按顺序执行时,就会遇到延迟界限,而吞吐量界限则表征​​处理器功能单元的原始计算能力。

课本5.5、5.6题介绍了这两种可能的多项式计算循环结构

double result = a[0];
double xpwr = x;
for (int i = 1; i <= degree; i++) {
    result += a[i] * xpwr;
    xpwr = x * xpwr;
}
Run Code Online (Sandbox Code Playgroud)

double result = a[degree];
double xpwr = x;
for (int i = degree - 1; i >= 0; i--) {
    result = a[i] + x * result;
}
Run Code Online (Sandbox Code Playgroud)

假设循环在具有以下执行单元的微体系结构上执行:

  • 一个浮点加法器。它的延迟为 3 个周期,并且是完全流水线化的。
  • 两个浮点乘法器。每个的延迟是 5 个周期,并且都是完全流水线化的。
  • 四个整数 ALU,每个都有一个周期的延迟。

为这个问题给出的浮点乘法和加法的延迟界限分别是 5.0 和 3.0。根据答案键,第一个循环的总循环延迟是每个元素 5.0 个周期,第二个是每个元素 8.0 个周期。我不明白为什么第一个循环不是 8.0。

似乎 a[i] …

performance cpu-architecture micro-optimization

1
推荐指数
1
解决办法
194
查看次数

有没有更好的方法来检测 16 字节标志数组中设置的位?

    ALIGNTO(16) uint8_t noise_frame_flags[16] = { 0 };

    // Code detects noise and sets noise_frame_flags omitted

    __m128i xmm0            = _mm_load_si128((__m128i*)noise_frame_flags);
    bool    isNoiseToCancel = _mm_extract_epi64(xmm0, 0) | _mm_extract_epi64(xmm0, 1);

    if (isNoiseToCancel)
        cancelNoises(audiobuffer, nAudioChannels, audio_samples, noise_frame_flags);
Run Code Online (Sandbox Code Playgroud)

这是我在 Linux 上的 AV Capture 工具的代码片段。这里的noise_frame_flags是16通道音频的标志数组。对于每个通道,相应的字节可以是 0 或 1。1 表示该通道有一些噪声需要消除。例如,如果noise_frame_flags[0] == 1,则意味着设置了第一个通道噪声标志(通过省略的代码)。

即使设置了一个“标志”,我也需要调用cancelNoises. 这段代码在这方面似乎工作得很好。正如您所看到的,我曾经_mm_load_si128加载正确对齐的整个标志数组,然后加载两个_mm_extract_epi64来提取“标志”。我的问题是有更好的方法来做到这一点(也许使用流行计数)?

注意:ALIGNTO(16)是一个宏扩展以纠正 GCC 等效项,但外观更好。

c++ sse x86-64 simd micro-optimization

1
推荐指数
1
解决办法
192
查看次数

如何在 C 中执行无分支条件算术运算?

我有一个值int x,我想有条件地添加(例如)一个值int y,具体取决于bool c. 我可以写这样的代码:

bool c;    // C23, or stdbool.h macro for _Bool.  Converts to integer 0 or 1
int x, y;  // x could be a global or something

...

if (c)
    x += y;
Run Code Online (Sandbox Code Playgroud)

如果没有分支我怎么能写这个?

ifx是一个没有其他线程可以引用的局部变量,如果编译器认为这样更有效,则可以将 if 转换为无分支。(特别是在自动向量化的情况下,但也适用于标量。)但这对于全局变量来说不是线程安全的,或者如果x实际上是*x带有int *. 编译器无法发明类似于*x += 0抽象机不读取或写入的可能共享对象的写入,这可能会引入数据竞争并影响其他线程存储的值。

c bit-manipulation micro-optimization twos-complement branchless

1
推荐指数
1
解决办法
205
查看次数