我正在阅读Agner Fog的材料,我有一些疑问:
预解码器和解码器每个时钟周期可处理 16 字节或 4 条指令
作者谈到了宏指令的缓存。我不知道为什么它有用,毕竟我们有缓存指令。什么是环回缓冲区?
什么是微操作融合和宏操作融合?
我在 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)
这段代码被调用得太多,以至于这个循环实际上是代码中的速度瓶颈。我想知道几件事:
由于 switchs是函数中的常量,好的编译器会优化循环,以便分支不会一直减慢速度吗?
如果没有,优化这段代码的好方法是什么?
====
编辑:这是一个包含更完整示例的更新:
int func(int s,
int start, int stop, …Run Code Online (Sandbox Code Playgroud) c optimization loops micro-optimization compiler-optimization
我相信 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)
什么编译器可以生成第二种代码?他们通常会产生第一个的一些变体。
我正在考虑如何否定 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 会更慢吗?最后,有没有其他方法可以做到这一点?
对于那些已经测量过或对此类注意事项有深入了解的人,假设您必须执行以下操作(仅选择任何示例)浮点运算符:
float calc(float y, float z)
{ return sqrt(y * y + z * z) / 100; }
Run Code Online (Sandbox Code Playgroud)
哪里y和z可能是非正规数,让我们假设两种可能的情况,其中只有 y,只有 z,或者两者,以完全随机的方式,可以是非正规数
现在假设我想避免处理非正规数的性能损失,我只想将它们视为 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) 我有一个特别的问题。我将尝试尽可能准确地描述这一点。
我正在做一个非常重要的“微优化”。一次运行数天的循环。所以如果我能减少这个循环时间,它需要一半的时间。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) 用于标准库不可用的情况。假设月份是一个无符号整数。
我很想看到给出正确答案的最短算术表达式,允许或禁止按位运算符和掩码,但不允许查找表。部分表达式可以保存到变量中以提高可读性以展示所使用的想法。
我的教科书(计算机系统:程序员的观点)指出,当一系列操作必须严格按顺序执行时,就会遇到延迟界限,而吞吐量界限则表征处理器功能单元的原始计算能力。
课本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)
假设循环在具有以下执行单元的微体系结构上执行:
为这个问题给出的浮点乘法和加法的延迟界限分别是 5.0 和 3.0。根据答案键,第一个循环的总循环延迟是每个元素 5.0 个周期,第二个是每个元素 8.0 个周期。我不明白为什么第一个循环不是 8.0。
似乎 a[i] …
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 等效项,但外观更好。
我有一个值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