- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
这可能是一个愚蠢的问题,但是每次我想“优化”大量参数(例如结构)传递给只读取它们的函数时,都会使我有些slightly跷。我在传递指针之间犹豫不决:
struct Foo
{
int x;
int y;
int z;
} Foo;
int sum(struct Foo *foo_struct)
{
return foo_struct->x + foo_struct->y + foo_struct->z;
}
struct Foo
{
int x;
int y;
int z;
} Foo;
int sum(const struct Foo foo_struct)
{
return foo_struct.x + foo_struct.y + foo_struct.z;
}
最佳答案
结构与数组很像,是数据的容器。每次使用容器时,都会将其数据布置在连续的内存块中。容器本身由其起始地址标识,并且每次使用容器进行操作时,您的程序都需要通过专用指令进行低级指针算术运算,以便应用偏移量从起始地址到达所需字段(或数组中的元素)。编译器唯一需要了解的用于结构的东西是(大致):
const
限定符对于理解将结构作为指针或按值传递之间的区别没有用。它只是告诉编译器,在函数内部,参数本身的值不会被修改。作为值或作为指针传递之间的性能差异通常不受
const
影响。
const
关键字仅对其他类型的优化有用,而对这一优化则无效。
void first(const struct mystruct x);
void second(struct mystruct *x);
#include <stdio.h>
struct mystruct {
unsigned a, b, c, d, e, f, g, h, i, j, k;
};
unsigned long __attribute__ ((noinline)) first(const struct mystruct x) {
unsigned long total = x.a;
total += x.b;
total += x.c;
total += x.d;
total += x.e;
total += x.f;
total += x.g;
total += x.h;
total += x.i;
total += x.j;
total += x.k;
return total;
}
unsigned long __attribute__ ((noinline)) second(struct mystruct *x) {
unsigned long total = x->a;
total += x->b;
total += x->c;
total += x->d;
total += x->e;
total += x->f;
total += x->g;
total += x->h;
total += x->i;
total += x->j;
total += x->k;
return total;
}
int main (void) {
struct mystruct x = {0};
scanf("%u", &x.a);
unsigned long v = first(x);
printf("%lu\n", v);
v = second(&x);
printf("%lu\n", v);
return 0;
}
__attribute__ ((noinline))
只是为了避免自动内联函数,出于测试目的,该函数非常简单,因此很可能会使用
-O3
内联。
objdump
编译和反汇编结果。
main()
调用first()
的方式: 86a: 48 89 e0 mov rax,rsp
86d: 48 8b 55 c0 mov rdx,QWORD PTR [rbp-0x40]
871: 48 89 10 mov QWORD PTR [rax],rdx
874: 48 8b 55 c8 mov rdx,QWORD PTR [rbp-0x38]
878: 48 89 50 08 mov QWORD PTR [rax+0x8],rdx
87c: 48 8b 55 d0 mov rdx,QWORD PTR [rbp-0x30]
880: 48 89 50 10 mov QWORD PTR [rax+0x10],rdx
884: 48 8b 55 d8 mov rdx,QWORD PTR [rbp-0x28]
888: 48 89 50 18 mov QWORD PTR [rax+0x18],rdx
88c: 48 8b 55 e0 mov rdx,QWORD PTR [rbp-0x20]
890: 48 89 50 20 mov QWORD PTR [rax+0x20],rdx
894: 8b 55 e8 mov edx,DWORD PTR [rbp-0x18]
897: 89 50 28 mov DWORD PTR [rax+0x28],edx
89a: e8 81 fe ff ff call 720 <first>
0000000000000720 <first>:
720: 55 push rbp
721: 48 89 e5 mov rbp,rsp
724: 8b 45 10 mov eax,DWORD PTR [rbp+0x10]
727: 89 c0 mov eax,eax
729: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
72d: 8b 45 14 mov eax,DWORD PTR [rbp+0x14]
730: 89 c0 mov eax,eax
732: 48 01 45 f8 add QWORD PTR [rbp-0x8],rax
736: 8b 45 18 mov eax,DWORD PTR [rbp+0x18]
739: 89 c0 mov eax,eax
... same stuff happening over and over ...
783: 48 01 45 f8 add QWORD PTR [rbp-0x8],rax
787: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
78b: 5d pop rbp
78c: c3 ret
DWORD PTR [rbp + offset]
)。 main()
调用second()
的方式: 8bf: 48 8d 45 c0 lea rax,[rbp-0x40]
8c3: 48 89 c7 mov rdi,rax
8c6: e8 c2 fe ff ff call 78d <second>
000000000000078d <second>:
78d: 55 push rbp
78e: 48 89 e5 mov rbp,rsp
791: 48 89 7d e8 mov QWORD PTR [rbp-0x18],rdi
795: 48 8b 45 e8 mov rax,QWORD PTR [rbp-0x18]
799: 8b 00 mov eax,DWORD PTR [rax]
79b: 89 c0 mov eax,eax
79d: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
7a1: 48 8b 45 e8 mov rax,QWORD PTR [rbp-0x18]
7a5: 8b 40 04 mov eax,DWORD PTR [rax+0x4]
7a8: 89 c0 mov eax,eax
... same stuff happening over and over ...
81f: 48 01 45 f8 add QWORD PTR [rbp-0x8],rax
823: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
827: 5d pop rbp
828: c3 ret
lea
+ mov
)。但是,由于现在该函数必须使用->
运算符与指针一起使用,因此我们看到,每次需要访问结构中的值时,都需要将内存解除引用两次,而不是一次(第一个将指针指向)。堆栈中的结构,然后获取结构中指定偏移量处的值)。 struct
时,我们并不真正在意它在内存中的位置(堆栈,堆,数据段等)。只要我们知道它从哪里开始,就可以归结为应用相同的简单指针算法来访问字段。无论结构位于何处或是否已动态分配,都始终需要这样做。-O3
优化上面的代码,我们现在将看到以下内容:main()
调用first()
的方式: 61a: 48 83 ec 30 sub rsp,0x30
61e: 48 8b 44 24 30 mov rax,QWORD PTR [rsp+0x30]
623: 48 89 04 24 mov QWORD PTR [rsp],rax
627: 48 8b 44 24 38 mov rax,QWORD PTR [rsp+0x38]
62c: 48 89 44 24 08 mov QWORD PTR [rsp+0x8],rax
631: 48 8b 44 24 40 mov rax,QWORD PTR [rsp+0x40]
636: 48 89 44 24 10 mov QWORD PTR [rsp+0x10],rax
63b: 48 8b 44 24 48 mov rax,QWORD PTR [rsp+0x48]
640: 48 89 44 24 18 mov QWORD PTR [rsp+0x18],rax
645: 48 8b 44 24 50 mov rax,QWORD PTR [rsp+0x50]
64a: 48 89 44 24 20 mov QWORD PTR [rsp+0x20],rax
64f: 8b 44 24 58 mov eax,DWORD PTR [rsp+0x58]
653: 89 44 24 28 mov DWORD PTR [rsp+0x28],eax
657: e8 74 01 00 00 call 7d0 <first>
00000000000007d0 <first>:
7d0: 8b 44 24 0c mov eax,DWORD PTR [rsp+0xc]
7d4: 8b 54 24 08 mov edx,DWORD PTR [rsp+0x8]
7d8: 48 01 c2 add rdx,rax
7db: 8b 44 24 10 mov eax,DWORD PTR [rsp+0x10]
7df: 48 01 d0 add rax,rdx
7e2: 8b 54 24 14 mov edx,DWORD PTR [rsp+0x14]
7e6: 48 01 d0 add rax,rdx
7e9: 8b 54 24 18 mov edx,DWORD PTR [rsp+0x18]
7ed: 48 01 c2 add rdx,rax
7f0: 8b 44 24 1c mov eax,DWORD PTR [rsp+0x1c]
7f4: 48 01 c2 add rdx,rax
7f7: 8b 44 24 20 mov eax,DWORD PTR [rsp+0x20]
7fb: 48 01 d0 add rax,rdx
7fe: 8b 54 24 24 mov edx,DWORD PTR [rsp+0x24]
802: 48 01 d0 add rax,rdx
805: 8b 54 24 28 mov edx,DWORD PTR [rsp+0x28]
809: 48 01 c2 add rdx,rax
80c: 8b 44 24 2c mov eax,DWORD PTR [rsp+0x2c]
810: 48 01 c2 add rdx,rax
813: 8b 44 24 30 mov eax,DWORD PTR [rsp+0x30]
817: 48 01 d0 add rax,rdx
81a: c3 ret
main()
调用second()
的方式: 671: 48 89 df mov rdi,rbx
674: e8 a7 01 00 00 call 820 <second>
0000000000000820 <second>:
820: 8b 47 04 mov eax,DWORD PTR [rdi+0x4]
823: 8b 17 mov edx,DWORD PTR [rdi]
825: 48 01 c2 add rdx,rax
828: 8b 47 08 mov eax,DWORD PTR [rdi+0x8]
82b: 48 01 d0 add rax,rdx
82e: 8b 57 0c mov edx,DWORD PTR [rdi+0xc]
831: 48 01 d0 add rax,rdx
834: 8b 57 10 mov edx,DWORD PTR [rdi+0x10]
837: 48 01 c2 add rdx,rax
83a: 8b 47 14 mov eax,DWORD PTR [rdi+0x14]
83d: 48 01 c2 add rdx,rax
840: 8b 47 18 mov eax,DWORD PTR [rdi+0x18]
843: 48 01 d0 add rax,rdx
846: 8b 57 1c mov edx,DWORD PTR [rdi+0x1c]
849: 48 01 d0 add rax,rdx
84c: 8b 57 20 mov edx,DWORD PTR [rdi+0x20]
84f: 48 01 c2 add rdx,rax
852: 8b 47 24 mov eax,DWORD PTR [rdi+0x24]
855: 48 01 c2 add rdx,rax
858: 8b 47 28 mov eax,DWORD PTR [rdi+0x28]
85b: 48 01 d0 add rax,rdx
85e: c3 ret
first()
情况下,我们看到所有字段都通过[rsp + offset]
访问,这意味着堆栈本身上的某个地址(rsp
)用于计算字段的位置,而在second()
情况下,我们看到[rdi + offset]
,这意味着地址而是使用作为参数传递(在rdi
中)。尽管偏移量仍然相同。first()
函数仍需要按值传递结构,因此即使启用了优化,整个结构仍需要复制到堆栈上,因此我们可以看到 first()
函数较重并添加了调用方中有很多代码。const
函数的first()
限定符可能会给编译器敲响钟声,并使其理解,实际上并不需要复制堆栈上的数据,并且调用者只需传递一个指针即可。但是,编译器应严格遵守ABI针对给定签名所规定的调用约定,而不是尽力优化代码。毕竟,在这种情况下,并不是真正的编译器错误,而是程序员的错误。struct
本身。
关于c - 结构和 union :从性能的角度来看哪个更好?通过值或指针传递参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59976295/
对于游戏,我正在尝试计算我正在看的位置与场景中另一个对象的位置之间的角度。我通过使用以下代码获得了角度: Vec3 out_sub; Math.Subtract(pEnt->vOrigin, pLoc
我还是 Firebase 的新手。有什么方法可以使用 Firebase 性能监控从控制台自动逐屏监控我们的 Web 应用程序?我检查了文档,它说我们需要在代码中添加一些跟踪来跟踪(如果我弄错了,请纠正
我正在使用 angular-material2 的复选框。目前复选框的默认颜色为紫色。 看起来他们已将复选框的默认颜色从“主要”更改为重音。 有没有办法在不覆盖 css 的情况下获得“主要”(绿色)颜
Angular-Material 中是否有任何分页指令可与 md-list 一起使用? 这些确实有效,但不是基于 Material 设计的。 https://github.com/brantwills
所以我有一个configmap config.json { "apiUrl": "http://application.cloudapp.net/", "test": "1232" } 称为“连续部署
我可以成功对图像进行阈值处理并找到图像的边缘。我正在努力尝试的是准确地提取黑色边缘的 Angular 。 我目前正在使用黑色边缘的极端点,并使用atan2函数计算 Angular ,但是由于混叠,根据
我需要一些帮助来计算点的角度: 我需要计算从点 (0,0) 到从图像中提取的点的角度。 1 将是 0*,2 大约是 40-44* 等。 我的问题是 atan2 显示的值不正确。atan2 的当前输出是
好的,所以我有一个运动场 512x512环绕,-32变成 512对于x和 y . 现在我需要计算两个实体之间的角度,我有以下代码作为一种解决方法,它在大多数时间都有效,但有时仍然会失败: Shoote
我有一个组件,它有一个子组件。子组件有一个按钮,可以打开一个 Material 对话框。 在对话框中,我们有表单、用户名和密码以及提交按钮。当我提交时,我正在调用后端 REST api。 这在子组件中
我一直在试图找到2之间的差异,但是要减去这个就没有运气了 The primary diff erence between the two representations is that a quate
我在 Angular Material Expansion 面板中遇到了这个问题。部分分页下拉菜单被切断。如何使下拉菜单与扩展面板的末端重叠?我尝试了 z-index 但没有成功。 Material
我正在创建一个PapperSoccer项目,但是在寻找运动/线条的方向时遇到了问题。我正在使用HoughLinesP来检测行,并且效果尽可能好。 我使用ROI,在其中寻找一行,然后相应地移动它。 到目
我正在寻找修改构建函数输出的方法 ng build --prod 想添加一些引导CSS文件到index.html的head部分,更改名称index.html => index.php等 怎么做? 最佳
如何获得两个单位向量之间的 x、y 和 z 旋转值?我不能使用点积,因为它只给我一个值。我想使用旋转矩阵在每个轴上旋转,对于那些我需要每个轴上的角度差。我尝试了仅两个组件的点积,但我有点困惑。有没有一
我必须计算图像中每条可检测线的斜率(或角度)。如果可能的话,甚至可以检测直线斜率的变化。我已经执行了 2D 傅立叶并且我知道每个区域的邻域平均角度(64x64px 的集合)。我什至尝试了 Hough
我正在使用Tiled map 编辑器创建简单的平铺 map 。在我的 map 中,我有几个矩形,如果我创建一个宽度为 50、高度为 10 的矩形并将其精确旋转 90°,则保存 map 并将其加载到我的
我计算了一个三角形的角度,但我不明白为什么我得到一些锐角的负角。例如: var sin = Math.Sin(4.45); var radians = Math.Atan(sin); var
我正在开发一个机器学习项目,其中使用 TensorFlow(和 DNNRegressor)。我想预测范围在 -pi 和 pi 之间的模算术值(角度)。当我尝试“正常方式”执行此操作时,模型不是很好,因
我有一个包含 40 个旋转图像的图像。 图像索引实际上从 0. 0-39 开始。 这是将 0-39 转换为度数的代码 int image_direction = 0; //Can be 0-39 in
在 PostGIS/PostgreSQL 中,是否有一个函数可以给出给定点所在的线串的线段的角度? 最佳答案 在 PostGIS 版本 1.5.3 上 ST_Azimuth()需要两点作为输入——据我
我是一名优秀的程序员,十分优秀!