据我所知道的,唯一的区别__asm { ... };,并__asm__("...");是第一个使用mov eax, var第二个使用movl %0, %%eax与:"=r" (var)结尾.还有什么其他差异?那又怎么样asm?
给定std::bitset<64> bits任意数量的位和位位置X(0-63)
在X位或更低位计数位的最有效方法是什么,如果未设置X位,则返回0
注意:如果设置该位,则返回始终至少为1
蛮力方式很慢:
int countupto(std::bitset<64> bits, int X)
{
if (!bits[X]) return 0;
int total=1;
for (int i=0; i < X; ++i)
{
total+=bits[i];
}
return total;
}
Run Code Online (Sandbox Code Playgroud)
这个count()方法bitset将为您popcount提供所有位,但bitset不支持范围
注意:这不是如何计算32位整数中的设置位数?因为它询问所有位而不是0到X的范围
来自Ira Baxter回答,为什么INC和DEC指令不会影响进位标志(CF)?
大多数情况下,我远离
INC而DEC现在,因为他们做的部分条件代码更新,这样就可以在管道中引起滑稽的摊位,和ADD/SUB没有.因此,无关紧要(大多数地方),我使用ADD/SUB避免失速.我使用INC/DEC仅在保持代码较小的情况下,例如,适合高速缓存行,其中一个或两个指令的大小产生足够的差异.这可能是毫无意义的纳米[字面意思!] - 优化,但我在编码习惯上相当老派.
我想问一下为什么它会导致管道中的停顿,而添加不会?毕竟,无论是ADD和INC更新标志寄存器.唯一的区别是INC不更新CF.但为什么重要呢?
当试图理解汇编(启用编译器优化)时,我看到这种行为:
这样一个非常基本的循环
outside_loop;
while (condition) {
statements;
}
Run Code Online (Sandbox Code Playgroud)
经常被编译成(伪代码)
; outside_loop
jmp loop_condition ; unconditional
loop_start:
loop_statements
loop_condition:
condition_check
jmp_if_true loop_start
; outside_loop
Run Code Online (Sandbox Code Playgroud)
但是,如果未打开优化,则会编译为通常可理解的代码:
loop_condition:
condition_check
jmp_if_false loop_end
loop_statements
jmp loop_condition ; unconditional
loop_end:
Run Code Online (Sandbox Code Playgroud)
根据我的理解,编译后的代码更像是这样的:
goto condition;
do {
statements;
condition:
}
while (condition_check);
Run Code Online (Sandbox Code Playgroud)
我看不到巨大的性能提升或代码可读性提升,为什么经常出现这种情况呢?是否有此循环样式的名称,例如"尾随条件检查"?
我被告知并且从英特尔的手册中读到可以将指令写入内存,但是指令预取队列已经获取了陈旧的指令并将执行那些旧的指令.我没有成功观察到这种行为.我的方法如下.
英特尔软件开发手册从第11.6节开始说明
对当前在处理器中高速缓存的代码段中的存储器位置的写入导致相关联的高速缓存行(或多个行)无效.此检查基于指令的物理地址.此外,P6系列和奔腾处理器检查对代码段的写入是否可以修改已经预取执行的指令.如果写入影响预取指令,则预取队列无效.后一种检查基于指令的线性地址.
所以,看起来如果我希望执行陈旧的指令,我需要有两个不同的线性地址引用相同的物理页面.所以,我将内存映射到两个不同的地址.
int fd = open("code_area", O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO);
assert(fd>=0);
write(fd, zeros, 0x1000);
uint8_t *a1 = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FILE | MAP_SHARED, fd, 0);
uint8_t *a2 = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FILE | MAP_SHARED, fd, 0);
assert(a1 != a2);
Run Code Online (Sandbox Code Playgroud)
我有一个汇编函数,它接受一个参数,一个指向我想要更改的指令的指针.
fun:
push %rbp
mov %rsp, %rbp
xorq %rax, %rax # Return value 0
# A far jump simulated with a far return
# Push the …Run Code Online (Sandbox Code Playgroud) 我一直看到人们声称MOV指令可以在x86中免费,因为寄存器重命名.
对于我的生活,我无法在一个测试用例中验证这一点.每个测试用例我尝试揭穿它.
例如,这是我用Visual C++编译的代码:
#include <limits.h>
#include <stdio.h>
#include <time.h>
int main(void)
{
unsigned int k, l, j;
clock_t tstart = clock();
for (k = 0, j = 0, l = 0; j < UINT_MAX; ++j)
{
++k;
k = j; // <-- comment out this line to remove the MOV instruction
l += j;
}
fprintf(stderr, "%d ms\n", (int)((clock() - tstart) * 1000 / CLOCKS_PER_SEC));
fflush(stderr);
return (int)(k + j + l);
}
Run Code Online (Sandbox Code Playgroud)
这为循环生成以下汇编代码(随意生成这个你想要的;你显然不需要Visual C++):
LOOP:
add edi,esi
mov …Run Code Online (Sandbox Code Playgroud) 这与此问题有关,但不一样:x86-64汇编的性能优化 - 对齐和分支预测与我之前的问题略有关系:无符号64位到双倍转换:为什么这个算法来自g ++
以下是一个不真实的测试用例.这种素性测试算法是不明智的.我怀疑任何真实世界的算法都不会执行如此多的小内循环(num大概是2**50的大小).在C++ 11中:
using nt = unsigned long long;
bool is_prime_float(nt num)
{
for (nt n=2; n<=sqrt(num); ++n) {
if ( (num%n)==0 ) { return false; }
}
return true;
}
Run Code Online (Sandbox Code Playgroud)
然后g++ -std=c++11 -O3 -S生成以下内容,包含RCX n和包含XMM6 sqrt(num).请参阅我之前发布的剩余代码(在此示例中从未执行过,因为RCX永远不会变得足够大,不能被视为带符号的否定).
jmp .L20
.p2align 4,,10
.L37:
pxor %xmm0, %xmm0
cvtsi2sdq %rcx, %xmm0
ucomisd %xmm0, %xmm6
jb .L36 // Exit the loop
.L20:
xorl %edx, %edx
movq %rbx, %rax …Run Code Online (Sandbox Code Playgroud) 在MSVC 2013中编译以下代码,64位版本构建,/O2优化:
while (*s == ' ' || *s == ',' || *s == '\r' || *s == '\n') {
++s;
}
Run Code Online (Sandbox Code Playgroud)
我得到了以下代码 - 使用64位寄存器作为带有bt(位测试)指令的查找表,它具有非常酷的优化.
mov rcx, 17596481020928 ; 0000100100002400H
npad 5
$LL82@myFunc:
movzx eax, BYTE PTR [rsi]
cmp al, 44 ; 0000002cH
ja SHORT $LN81@myFunc
movsx rax, al
bt rcx, rax
jae SHORT $LN81@myFunc
inc rsi
jmp SHORT $LL82@myFunc
$LN81@myFunc:
; code after loop...
Run Code Online (Sandbox Code Playgroud)
但我的问题是:movsx rax, al第一个分支后的目的是什么?
首先,我们从字符串中加载一个字节rax并对其进行零扩展:
movzx eax, BYTE …Run Code Online (Sandbox Code Playgroud) 在阅读Joel的文章Back to Basics并在SO上看到许多类似的问题之后,我开始想知道什么是具体的例子,知道像C这样的东西可以让你成为更好的高级程序员.
我想知道的是,有很多例子.很多时候,这个问题的答案就像" 了解C让你更好地了解幕后发生的事情 "或" 你需要为你的计划奠定坚实的基础 ",这些答案没有多大意义.我想了解您从了解低级概念中获益的不同具体方式,
Joel给出了几个例子:二进制数据库与XML和字符串.但是有两个例子并不能证明学习C和/或汇编是正确的.所以我的问题是:知道C让你成为更好的高级程序员的具体例子是什么?
我理解计算机如何在基本原理上工作,例如,程序可以用C#,C等"高级"语言编写,然后将其分解为目标代码,然后分解为处理器才能理解.但是,我真的想学习装配,以及它如何在现代应用中使用.
我知道处理器在基本x86指令集之上有不同的指令集.所有汇编语言都支持所有指令集吗?
有多少汇编语言?有多少与其他语言一起使用?
如何在程序集中编写例程,然后将其编译为对象/二进制代码?
那么有人会如何用C或C++这样的语言引用汇编代码中的函数/例程?
我们怎么知道我们在汇编中编写的代码可能是最快的?
是否有关于汇编语言的推荐书籍/将它们与现代程序一起使用?
对于问题的数量感到抱歉,我希望它们足够通用,对其他人有用,并且足够简单,让其他人回答!
assembly ×7
c ×5
x86 ×5
performance ×4
c++ ×3
algorithm ×1
caching ×1
gcc ×1
high-level ×1
increment ×1
intel ×1
loops ×1
low-level ×1
optimization ×1
visual-c++ ×1
x86-64 ×1