From what I've read, seems like there are 9 different flags. Is it possible to read/change them directly? I know I can know for example if the zero flag is set after doing a cmp/jmp instruction, but I'm asking if it's possible to do something like
mov eax, flags
Run Code Online (Sandbox Code Playgroud)
or something.
Also, for writing, is it possible to set them by hand?
我对它们之间的区别有点困惑
leal -4(%ebp), %eax
Run Code Online (Sandbox Code Playgroud)
和
movl -4(%ebp), %eax
Run Code Online (Sandbox Code Playgroud)
谁可以给我解释一下这个?
我很难理解当翻译旁视缓冲区的前两个级别导致未命中时会发生什么?
我不确定特殊硬件电路中是否出现"页面行走",或者页表是否存储在L2/L3高速缓存中,或者它们是否只存在于主存储器中.
考虑下面的C++ 11片段.对于GCC和clang,这会编译为两个(顺序一致的)foo.C++内存模型是否允许编译器将这两个加载合并到一个加载中并对x和y使用相同的值?
我认为它不能合并这些负载,因为这意味着轮询原子不再起作用,但我找不到内存模型文档中的相关部分.
#include <atomic>
#include <cstdio>
std::atomic<int> foo;
int main(int argc, char **argv)
{
int x = foo;
int y = foo;
printf("%d %d\n", x, y);
return 0;
}
Run Code Online (Sandbox Code Playgroud) c++ memory-model compiler-optimization language-lawyer stdatomic
Chandler Carruth在他的CppCon2015演讲中引入了两个函数,可以用来对优化器进行一些细粒度的抑制.它们可用于编写微基准测试,优化器不会简单地将其变为无意义.
void clobber() {
asm volatile("" : : : "memory");
}
void escape(void* p) {
asm volatile("" : : "g"(p) : "memory");
}
Run Code Online (Sandbox Code Playgroud)
这些使用内联汇编语句来更改优化程序的假设.
程序集语句声明clobber其中的汇编代码可以在内存中的任何位置读写.实际的汇编代码是空的,但优化器不会查看它,因为它是asm volatile.当我们告诉它代码可能在内存中的任何地方读写时,它都相信它.这有效地防止优化器在调用之前重新排序或丢弃内存写入clobber,并在调用clobber† 后强制执行内存读取.
所述一个中escape,另外使指针p可见的组装块.同样,因为优化器不会查看代码可能为空的实际内联汇编代码,并且优化器仍将假定该块使用指针指向的地址p.这有效地强制p存储器中的任何点而不是寄存器中的点,因为汇编块可能执行从该地址的读取.
(这很重要,因为clobber函数不会强制读取或写入编译器决定放入寄存器的任何内容,因为汇编语句中clobber并未声明组件中的任何内容都必须可见.)
所有这些都发生在没有任何额外代码由这些"障碍"直接生成的情况下.它们纯粹是编译时的工件.
但是,它们使用GCC和Clang支持的语言扩展.有没有办法在使用MSVC时有类似的行为?
†要理解为什么优化器必须这样思考,想象一下汇编块是否为内存中的每个字节添加1的循环.
我是这里的新手,刚刚开始学习汇编语言.所以,如果我错了,请纠正我,或者如果这篇文章没有任何意义我会删除.
我在讨论x86-64英特尔架构中的数据移动指令.我已经读过,常规movq指令只能有直接的源操作数,可以表示为32位二进制补码数,而movabsq指令可以有任意64位立即数作为其源操作数,并且只能有一个寄存器作为目标.
你能详细说明一下吗?这是否意味着我只能使用movabsq指令移动64位立即值?只有立即价值到登记册?我不知道如何将64位立即值移动到内存中.或者也许我错了一些重要的事情.
SSE2具有在单精度浮点数和32位整数之间转换向量的指令.
_mm_cvtps_epi32()_mm_cvtepi32_ps()但是没有双精度和64位整数的等价物.换句话说,他们失踪了:
_mm_cvtpd_epi64()_mm_cvtepi64_pd()似乎AVX也没有它们.
模拟这些内在函数的最有效方法是什么?
我试图准确理解什么是内存障碍.根据我目前所知,存储器屏障(例如:) mfence用于防止指令从存储器屏障之前到之后和之后重新排序.
这是使用中的内存屏障的示例:
instruction 1
instruction 2
instruction 3
mfence
instruction 4
instruction 5
instruction 6
Run Code Online (Sandbox Code Playgroud)
现在我的问题是:mfence指令只是一个标记,告诉CPU执行指令的顺序是什么?或者它是CPU实际执行的指令,就像它执行其他指令(例如:) mov.
我编写了这个简单的汇编代码,运行它并使用GDB查看内存位置:
.text
.global _main
_main:
pushq %rbp
movl $5, -4(%rbp)
addl $6, -4(%rbp)
popq %rbp
ret
Run Code Online (Sandbox Code Playgroud)
它直接在内存中添加5到6个,根据GDB它可以工作.所以这是直接在内存中执行数学运算而不是CPU寄存器.
现在在C中编写相同的东西并将其编译为汇编,结果如下:
... # clang output
xorl %eax, %eax
movl $0, -4(%rbp)
movl $5, -8(%rbp)
movl -8(%rbp), %ecx # load a
addl $6, %ecx # a += 6
movl %ecx, -8(%rbp) # store a
....
Run Code Online (Sandbox Code Playgroud)
在将它们添加到一起之前,它会将它们移动到寄存器中.
那么为什么我们不直接在内存中添加?
它慢了吗?如果是这样,为什么直接在内存中添加甚至允许,为什么汇编程序在开始时没有抱怨我的汇编代码?
编辑:这是第二个程序集块的C代码,我在编译时禁用了优化.
#include <iostream>
int main(){
int a = 5;
a+=6;
return 0;
}
Run Code Online (Sandbox Code Playgroud) 我有类似的东西:
if (f = acquire_load() == ) {
... use Foo
}
Run Code Online (Sandbox Code Playgroud)
和:
auto f = new Foo();
release_store(f)
Run Code Online (Sandbox Code Playgroud)
您可以很容易地想象使用atomic with load(memory_order_acquire)和store(memory_order_release)的acquire_load和release_store的实现.但是现在如果release_store是用_mm_stream_si64实现的,这是一个非临时写入,而不是针对x64上的其他商店进行排序的?如何获得相同的语义?
我认为以下是最低要求:
atomic<Foo*> gFoo;
Foo* acquire_load() {
return gFoo.load(memory_order_relaxed);
}
void release_store(Foo* f) {
_mm_stream_si64(*(Foo**)&gFoo, f);
}
Run Code Online (Sandbox Code Playgroud)
并使用它:
// thread 1
if (f = acquire_load() == ) {
_mm_lfence();
... use Foo
}
Run Code Online (Sandbox Code Playgroud)
和:
// thread 2
auto f = new Foo();
_mm_sfence(); // ensures Foo is constructed by the time f is published to gFoo
release_store(f)
Run Code Online (Sandbox Code Playgroud)
那是对的吗?我非常肯定这里绝对需要sfence.但是那个lfence怎么样?是否需要或者简单的编译器障碍对于x64是否足够?例如asm volatile("":::"memory").根据x86内存模型,负载不会与其他负载重新排序.所以根据我的理解,只要存在编译器障碍,acquire_load()必须在if语句中的任何加载之前发生.
assembly ×5
x86 ×5
c++ ×4
c ×2
stdatomic ×2
x86-64 ×2
att ×1
avx ×1
benchmarking ×1
clang ×1
cpu ×1
eflags ×1
gcc ×1
lock-free ×1
memory-model ×1
optimization ×1
performance ×1
simd ×1
sse ×1
tlb ×1
visual-c++ ×1