M2的循环计数器是否支持Neon?

3
有没有关于苹果M1/M2上SIMD循环次数的资源?就像x86一样https://uops.info/table.html或者Agner Fog的那种资源?我希望我能提供更多的奖励,但这是我拥有的所有声望。
我从未在ARM机器上编程过。我看了一下sse2neon https://github.com/DLTcollab/sse2neon/blob/7bd15eac51e36bf7426052f8515358cb665d8c04/sse2neon.h
我查找的第一件事是setzero。我怀疑dup是否是正确的方法,所以我尝试了nanobench并发现xor更快,而sub本身则不同。
有没有什么东西可以让我大致了解一下?我的目标是M2。
#include <arm_neon.h>
#define ANKERL_NANOBENCH_IMPLEMENT
#include "nanobench.h"

int32x4_t setzeroA()
{
    return vdupq_n_s32(0);
}
int32x4_t setzeroB()
{
    int32x4_t v;
    return vsubq_u32(v, v);
}
uint8x16_t setzeroC()
{
    uint8x16_t v;
    return veorq_u8(v, v);
}

int main() {
    ankerl::nanobench::Bench().run("Set", [&] {
        auto v = setzeroA();
        ankerl::nanobench::doNotOptimizeAway(v);
    });
    ankerl::nanobench::Bench().run("sub", [&] {
        auto v = setzeroB();
        ankerl::nanobench::doNotOptimizeAway(v);
    });
    ankerl::nanobench::Bench().run("xor", [&] {
        auto v = setzeroC();
        ankerl::nanobench::doNotOptimizeAway(v);
    });
}

1
微基准测试很难,这些内在函数可能都编译成相同的汇编代码。(如果不是,那你的编译器做得不好。)如果你想测试零值习惯用法,你可能需要用汇编语言编写或确保你的doNotOptimizeAway也使编译器认为该值已被破坏,因此必须重新生成一个零值。尽管编译器仍然可以使用另一个寄存器中的“mov”来实现这一点,在循环之外把实际的清零提升出来如果更便宜就会这样做。 - Peter Cordes
1
https://godbolt.org/z/MzEhdzfca 显示clang15使用movi v0.2d,# 0000000000000000来进行setzeroAsetzeroC,但是对于setzeroB没有使用任何指令,显然它不喜欢未初始化的读取实例。 内置函数不是汇编语言; 就像您的编译器可能不会使用add指令来执行4 + 5一样,如果它理解内置函数的作用,它可能以不同的方式获得内置函数的结果。 (大多数内置函数都适用于clang。) - Peter Cordes
@PeterCordes 你说得对。我重复了测试,发现无论哪个在最后运行的都是最快的。我想知道它是否从高效核心 -> 更大的核心 -> 更高的时钟。你也说得对,B是一个无操作。我修复了我的代码,全部使用u8,似乎没有任何变化(它是相同的指令)。 - Stan
@PeterCordes 你之前用过nanobench吗?它建议使用py perf来调整系统,这在我的linux x64上给了我一致的结果。我完全忘记了陷阱,也不知道在mac上需要多少暖机时间才足够。我会尝试理解你所写的内容,比如需要多少CPU时间。 - Stan
不,我没有使用nanobench。我猜它的目的是作为单个程序的一部分运行许多不同的短基准测试,因此每个单独的测试可以假定CPU已经升温到最大频率。这至少可以解释结果。所以,在程序开始时运行一个虚拟循环,进行数亿次迭代,除非你的基准测试需要,否则不要触及太多或任何内存。也许可以使用一些NEON指令的循环,如果有任何单独的预热效果。 - Peter Cordes
显示剩余3条评论
1个回答

3

是的,在M1上,吞吐量和延迟的变化不如英特尔那么大 - 所以预计许多操作具有类似的延迟。是的,您要查找的指令是FCMGT(它适用于NEON simd寄存器 - 因此在文档中参数为“向量”,而不是通用整数寄存器)。从两个核心的吞吐量来看,似乎冰风暴有4x64位SIMD执行端口,而火风暴有4x128位。总体而言,Firestorm比Icestorm快约3倍。 - robthebloke
LAT和Retire有什么区别?延迟似乎总是更大或相同,对我来说它意味着从运行指令到可以使用它需要多长时间。我认为Retire的意思是相同的,但是特别指代值的退役(与不产生值的NOP或x86 mm_pause相反)。我不明白那个页面上的Retire是什么意思。 - Stan
退役指令是指微代码指令的数量。LD2与4S(4xfloat)是一个很好的例子。该指令加载4x vec2,并将它们转换为SOA格式:xxxx/yyyy。另一种方法是使用2x vldq_f32 + vzip1q_f32 + vzip2q_f32,你会得到同样的结果。LD2在内部具有4个微代码操作的退役计数,因此您可以非常确定它是使用我刚提到的那4个指令来实现的。LD2操作的唯一真正优势是您可以在exe中获得更高的代码密度(使用更少的L1指令缓存)。 - robthebloke
延迟只是CPU在指令结果可用之前所需的周期数。例如,FDIV执行所需的时间比FADD长得多... - robthebloke
我应该提一下,我对该页面上列出的加载/存储延迟有些怀疑。 - robthebloke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接