gpt4 book ai didi

arrays - 为什么数组声明的顺序对性能影响如此之大?

转载 作者:行者123 更新时间:2023-12-02 22:33:01 24 4
gpt4 key购买 nike

首先,在使用 Accelerate 框架调整频率分析函数时,绝对系统时间始终为每次迭代 225 毫秒。然后昨晚我改变了其中两个数组的声明顺序,突然下降到 202 毫秒。仅通过更改申报顺序增加 10% 似乎很疯狂。有人可以向我解释为什么编译器(设置为优化)尚未找到此解决方案吗?

附加信息:在循环之前,循环中使用的数组有一些设置,包括将它们从整数数组转换为 float 组(用于加速),然后获取时间数组的 sin 和 cos(长 16 行)。所有 float 组(8 个数组 x 1000 个元素)首先在函数中声明(在对参数进行完整性检查之后)。它们总是被声明为相同的大小(通过一个常量),因为否则性能会因占用空间的微小收缩而受到影响。我测试了使它们成为全局变量,但我认为编译器已经知道了,因为没有性能变化。循环有 25 行长。

---添加---

是的,“-Os”是标志。 (无论如何 Xcode 中的默认值:最快、最小)

(以下来自内存 - 不要尝试编译它,因为我没有输入步幅(即 1)等内容。但是,所有 Accelerate 调用都在那里)

传递的参数:inttimearray、intamparray、length、scale1、scale2、amp

float trigarray1[maxsize];
float trigarray2[maxsize];
float trigarray3[maxsize];
float trigarray4[maxsize];
float trigarray5[maxsize];
float temparray[maxsize];
float amparray[maxsize]; //these two make the most change
float timearray[maxsize]; //these two make the most change

vDSP_vfltu32(inttimearray,timearray,length); //convert to float array
vDSP_vflt16(intamparray,amparray,length); //convert to float array

vDSP_vsmul(timearray,scale1,temparray,length); //scale time and store in temp
vvcosf(temparray,trigarray3,length); //cos of temparray
vvsinf(temparray,trigarray4,length); //sin of temparray
vDSP_vneg(trigarray4,trigarray5,length); //negative of trigarray4

vDSP_vsmul(timearray,scale2,temparray,length); //scale time and store in temp
vvcosf(temparray,trigarray1,length); //cos of temparray
vvsinf(temprray,trigarray2,length); //sin of temparray

float ysum;
vDSP_sve(amparray,ysum,length); //sum of amparray

float csum, ssum, ccsum, sssum, cssum, ycsum, yssum;

for (i = 0; i<max; i++) {

vDSP_sve(trigarray1,csum,length); //sum of trigarray1
vDSP_sve(trigarray2,ssum,length); //sum of trigarray2

vDSP_svesq(trigarray1,ccsum,length); //sum of trigarray1^2
vDSP_svesq(trigarray2,sssum,length); //sum of trigarray2^2

vDSP_vmul(trigarray1,trigarray2,temparray,length); //temp = trig1*trig2
vDSP_sve(temparray,cssum,length); //sum of temp array
// 2 more sets of the above 2 lines, for the 2 remaining sums

amp[i] = (arithmetic of sums);

//trig identity to increase the sin/cos by a delta frequency
//vmma is a*b+c*d=result
vDSP_vmma (trigarray1,trigarray3,trigarray2,trigarray4,temparray,length);
vDSP_vmma (trigarray2,trigarray3,trigarray1,trigarray5,trigarray2,length);
memcpy(trigarray1,temparray,length*sizeof(float));
}

---当前解决方案---

我做了如下修改:

数组都声明为对齐的,并且归零(我将在接下来解释)并且 maxsize 现在是 16 的倍数

__attribute__ ((align (16))) float timearray[maxsize] = {0};

我已将所有数组置零,因为现在,当长度小于 maxsize 时,我将长度四舍五入到最接近的 16 的倍数,以便所有循环函数都在可被 16 整除的宽度上运行,不影响总和。

好处是:

  • 轻微的性能提升
  • 无论数组声明的顺序如何,速度几乎是恒定的(现在在需要它们之前就完成了,而不是全部放在一个大块中)
  • 对于任何 16 宽的长度(即 241 到 256,或 225 到 240...),速度也几乎恒定,而之前,如果长度从 256 到 255,函数将占用 3+%性能下降。

在未来(可能使用这段代码,因为分析需求仍在不断变化),我意识到我需要更多地考虑堆栈的使用,以及向量的对齐/ block 。不幸的是,对于这段代码,我无法将这些数组设为静态或全局数组,因为该函数一次可以被多个对象调用。

最佳答案

我首先怀疑的是对齐。您可能想尝试:

__attribute__ ((align (16))) float ...[maxsize];

或者确保 maxsize 是 16 的倍数。如果在一种配置中您对齐而在另一种配置中您没有对齐,那肯定会导致 10% 的命中。向量运算可能对此极为敏感。

您可能遇到的下一个主要问题是巨大的堆栈(假设 maxsize 相当大)。 ARM 处理小于 4k 的数字比处理大于 4k 的数字效率高得多(因为它只能处理 12 位立即值)。因此,根据编译器优化它的方式,将 amparray 向下推到堆栈上可能会导致更复杂的数学运算来访问它。

当微小的事情导致性能发生巨大变化时,我总是建议拉起程序集(产品>生成输出>程序集)并查看编译器输出中的变化。我也强烈推荐Whirlwind Tour of ARM Assembly让你开始理解你在看什么。 (确保将输出设置为“用于存档”,以便看到优化结果。)

你还应该做一些更多的事情:

  • 尝试将此例程重写为简单的 C,而不是使用 Accelerate。是的,我知道 Accelerate 总是更快,但事实并非如此。所有这些函数调用都非常昂贵,而且根据我的经验,编译器通常可以更好地矢量化 Accelerate 可以做到的简单乘法和加法。如果您的步幅为 1,您的向量不是很大,并且您使用的是 1-2 核心设备(如 iPad),则尤其如此。一旦你有了处理步幅的代码(如果你不需要步幅),它就会比你手写的代码更复杂(更慢)。根据我的经验,Accelerate 似乎确实非常擅长斜坡和超越数(例如大表的余弦),但在简单的向量和矩阵数学方面却不太擅长。

  • 如果这段代码真的对您很重要,我发现手写程序集绝对可以超过编译器。我什至不太擅长 ARM 汇编程序,而且我已经能够在简单的矩阵数学上以 2 倍的速度击败编译器(并且编译器粉碎了 Accelerate)。我在这里特别谈论你的循环,它似乎只是在做加法和乘法。手写汇编当然很痛苦,然后您必须为汇编器维护一个 C 版本,但在真正重要的时候它真的很快。

关于arrays - 为什么数组声明的顺序对性能影响如此之大?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11902468/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com