我记得在优化x86速度时通常要避免使用读 - 修改 - 写指令.也就是说,你应该避免类似的东西add [rsi], 10,这会增加存储在其中的内存位置rsi.建议通常是将其拆分为读取 - 修改指令,然后是商店,如下所示:
mov rax, 10
add rax, [rsp]
mov [rsp], rax
Run Code Online (Sandbox Code Playgroud)
或者,您可以使用显式加载和存储以及reg-reg添加操作:
mov rax, [esp]
add rax, 10
mov [rsp], rax
Run Code Online (Sandbox Code Playgroud)
对于现代x86来说,这仍然是合理的建议(并且它曾经是吗?)?1
当然,在内存中的值被多次使用的情况下,RMW是不合适的,因为您将产生冗余的加载和存储.我对只使用一次值的情况感兴趣.
基于对Godbolt的探索,所有icc,clang和gcc都喜欢使用单个RMW指令来编译类似于:
void Foo::f() {
x += 10;
}
Run Code Online (Sandbox Code Playgroud)
成:
Foo::f():
add QWORD PTR [rdi], 10
ret
Run Code Online (Sandbox Code Playgroud)
因此,至少大多数编译器似乎认为RMW很好,当值仅使用一次.
有趣的是,当增量值是全局值而不是成员时,各种编译器不同意,例如:
int global;
void g() {
global += 10;
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,gcc并且clang仍然是单个RMW指令,而icc更倾向于 一个REG-REG具有明确载入和存储地址:
g():
mov eax, DWORD PTR global[rip] …Run Code Online (Sandbox Code Playgroud) 使用时组装对象时nasm,我发现所有标签都包含在结果.o文件中的符号中,以及最终的二进制文件中.
这对于我已经声明的函数入口点GLOBAL和段开始部分(例如,对于该.text部分)是有意义的,但是标签简单地用作循环入口点并且所有这些都接近出现在输出文件中似乎很奇怪.除了泄漏内部实现细节之外,它还浪费了符号表中的空间.
例如,鉴于这个简短的装配程序:
GLOBAL _start
_start:
xor eax, eax
normal_label:
xor eax, eax
.local_label:
xor eax, eax
xor edi, edi
mov eax, 231 ; exit(0)
syscall
Run Code Online (Sandbox Code Playgroud)
...建造使用:
nasm -f elf64 label-test.s
ld label-test.o -o label-test
Run Code Online (Sandbox Code Playgroud)
在l对象文件和链接的可执行文件中生成(即本地)符号:
objdump --syms label-test.o
label-test.o: file format elf64-x86-64
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 label-test.s
0000000000000000 l d .text 0000000000000000 .text
0000000000000002 l .text 0000000000000000 normal_label
0000000000000004 l .text 0000000000000000 normal_label.local_label
0000000000000000 g .text …Run Code Online (Sandbox Code Playgroud) Pattern p1 = Pattern.compile(".................");
Pattern p2 = Pattern.compile("xxxxxxxxxxxxxxxxxxx");
Run Code Online (Sandbox Code Playgroud)
由于p1和p2都很长,并且很难编写单个模式来覆盖p1和p2中的所有情况.是否可以编写另一个基于p1和p2构建的模式p3,这样我只能运行一个匹配器:
Matcher m = p3.matcher(str);
Run Code Online (Sandbox Code Playgroud) 从最新消息来看,新的苹果处理器A11 Bionic在Geekbench基准测试中比移动英特尔酷睿i7得分更高。
据我了解,该基准测试中有很多不同的测试。这些测试模拟了不同的负载,包括日常使用中可能发生的负载。
有人指出,这些结果无法与x86结果进行比较。他们说x86能够执行“更复杂的任务”。例如,他们领导Photoshop,视频转换,科学计算。我同意ARM的软件通常只是台式机软件的“轻量级”版本。但是在我看来,这种限制是由移动操作系统的格式(移动中完成工作,没有鼠标等)引起的,而不是由ARM的性能引起的。
作为相反的示例,让我们看一下Safari。浏览器是一个复杂的程序。Safari在iPad上的工作原理与Mac一样。此外,如果我们采用Sunspider(JS基准测试)的结果,事实证明iPad上的Safari得分更高。
我认为在日常任务(Web,Office,音乐/电影)中,ARM(A10X,A11)和x86(双核移动英特尔i7)的性能是可比的和相等的。
ARM真的有远远落后于x86的任何类型的任务吗?如果是这样,原因是什么?是什么阻止了Apple在ARM上发布笔记本电脑?从POWER迁移到x86,他们已经做了同样的事情。这是技术限制,还是营销?
我相信这是一个常见问题,但有些谷歌搜索不会返回匹配项,因此请在这里询问。
所以我有以下课程:
class A {
public:
A(const A &rhs) { m_a = rhs.m_a; }
private:
int m_a;
};
Run Code Online (Sandbox Code Playgroud)
一切都很酷,直到一段时间后,可能是一年后,我添加了一个新属性m_bto class A,但我忘记更新复制构造函数。
需要进行痛苦的调试才能找到不同步的位置。
有没有办法避免这样的问题,最好是在构建时?
是的,我可以编写单元测试来覆盖该复制构造函数,但是当我忘记更新复制构造函数时,很可能我也忘记了该单元测试。
我正在编写C,C++和汇编的混合编程,我希望从代码的任何部分获得可靠的回溯.
这主要工作正常的C和C++代码,因为我可以生成调试与信息-g,这对于现代的x86编译器和平台生成DWARF调试信息,这意味着最终的二进制包括CFI(调用帧信息).此信息允许通过当前调用堆栈中的函数向后移动.它可以支持复杂的场景,例如没有基指针的函数以及rsp动态方式的变化.对于C和C++代码,我不必关心:即使对于优化的代码,编译器也会生成正确的CFI.
对于我在nasm中编写的x86汇编代码,CFI是个问题.nasm和语法兼容的竞争对手yasm对生成DWARF信息有一些有限的支持,但它主要涉及指向行映射的指令指针,并且不包括任何CFI 1(实际上,即使它集合也无法真正生成它因为程序集太低而无法明确表达调用/返回语义,所以需要.
我想为程序集生成的例程添加CFI,但我不需要CFI的完全灵活性来根据指令细化更改识别CFA rsp等等:我非常乐意设置标准框架-pointer in rbpfor each assembly function并保持整个函数的完整性.DWARF3是否通过一小段CFI信息支持这种情况,希望我能用汇编器宏生成一个?
1 yasm 的实验性"nextgen" 版本确实支持CFI指令,但该项目尚未在5年内更新.有一个未解决的问题是将此支持移回yasm的主线版本.
GCC 支持-march允许您指定目标架构的开关 - 允许它调整该平台的指令序列以及使用该平台上可能可用但在“默认”或基本版本上不可用的指令架构。
例如,-march=skylake将告诉编译器以 Skylake CPU 为目标,包括使用 Skylake 上可用的指令集,例如 AVX2。
如何判断-march本地版本gcc支持的值?当传递无效参数时,较新版本有助于列出有效参数,但较旧版本不会。
当不存在这样的重载时,是否允许重载运算符,例如operator+标准库类型和内置类型的组合?
例如,在默认命名空间或用户定义的命名空间中实现以下运算符是否合法:
std::string operator+(const std::string& s, int right) { ... }
Run Code Online (Sandbox Code Playgroud)
我知道在命名空间中实现事物有各种限制std::,但我不清楚是否有任何规则违反上述规定(当然这是否是一个好主意是完全不同的事情!)。
我试图通过使用物理时钟来测量c ++中某些命令的执行时间,但是我遇到了一个问题,即从计算机上的物理时钟读取测量值的过程可能需要很长时间.这是代码:
#include <string>
#include <cstdlib>
#include <iostream>
#include <math.h>
#include <time.h>
int main()
{
int64_t mtime, mtime2, m_TSsum, m_TSssum, m_TSnum, m_TSmax;
struct timespec t0;
struct timespec t1;
int i,j;
for(j=0;j<10;j++){
m_TSnum=0;m_TSsum=0; m_TSssum=0; m_TSmax=0;
for( i=0; i<10000000; i++) {
clock_gettime(CLOCK_REALTIME,&t0);
clock_gettime(CLOCK_REALTIME,&t1);
mtime = (t0.tv_sec * 1000000000LL + t0.tv_nsec);
mtime2= (t1.tv_sec * 1000000000LL + t1.tv_nsec);
m_TSsum += (mtime2-mtime);
m_TSssum += (mtime2-mtime)*(mtime2-mtime);
if( (mtime2-mtime)> m_TSmax ) { m_TSmax = (mtime2-mtime);}
m_TSnum++;
}
std::cout << "Average "<< (double)(m_TSsum)/m_TSnum
<< " +/- " << …Run Code Online (Sandbox Code Playgroud)