关于uint_fast*_t类型族,C标准还不太清楚.在gcc-4.4.4 linux x86_64系统上,类型uint_fast16_t和uint_fast32_t大小均为8个字节.但是,8字节数的乘法似乎比4字节数的乘法慢得多.以下代码演示了:
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int
main ()
{
uint_least16_t p, x;
int count;
p = 1;
for (count = 100000; count != 0; --count)
for (x = 1; x != 50000; ++x)
p*= x;
printf("%"PRIuLEAST16, p);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我得到了在程序上运行time命令
real 0m7.606s
user 0m7.557s
sys 0m0.019s
Run Code Online (Sandbox Code Playgroud)
如果我将类型更改为uint_fast16_t(和printf修饰符),则时间变为
real 0m12.609s
user 0m12.593s
sys 0m0.009s
Run Code Online (Sandbox Code Playgroud)
那么,如果stdint.h头文件uint_fast16_t(以及uint_fast32_t)定义为4字节类型,那会不会更好?
我正在为64位x86生成以下指令:
41 F3 0F 10 46 10 movss XMM0,014h[R14]
Run Code Online (Sandbox Code Playgroud)
不幸的是,它在该行上出现故障.gdb将其反汇编为:
0x0000000000402054 <+320>: rex.B
0x0000000000402055 <+321>: movss 0x14(%rsi),%xmm0
Run Code Online (Sandbox Code Playgroud)
请注意,无法识别rex.B覆盖,索引是RSI而不是R14.
指令无效吗?在AMD 64位指令参考中,我找不到任何迹象表明此编码无效.
objdump也无法将其识别为有效指令:
41 rex.B
f3 0f 10 46 10 movss 0x10(%rsi),%xmm0
Run Code Online (Sandbox Code Playgroud)
这里发生了什么?
我正在尝试学习x86_64程序集,我今天正在尝试标准输入输出并偶然发现这个帖子学习程序集 - echo程序名称如何从STDIN读取输入(使用SYSCALL指令)?特别是如果我知道输入将始终是一个整数,我想将其读入寄存器?
编辑: @Daniel Kozar在下面的回答帮助我理解了STDIN和STDOUT如何与Linux上的SYSCALL指令一起工作.我试图编写一个小程序,它从控制台输入中读取一个数字并打印与该数字对应的ascii字符.假如你输入65作为输入,你应该得到A作为输出.还有一个新的线条角色.如果有的话,它可以帮助任何其他人:-)
section .text
global _start
_start:
mov rdi, 0x0 ; file descriptor = stdin = 0
lea rsi, [rsp+8] ; buffer = address to store the bytes read
mov rdx, 0x2 ; number of bytes to read
mov rax, 0x0 ; SYSCALL number for reading from STDIN
syscall ; make the syscall
xor rax, rax ; clear off rax
mov rbx, [rsp+8] ; read the first byte read into rsp+8 by STDIN call …Run Code Online (Sandbox Code Playgroud) 我想检查在glibc中执行系统调用的代码.我发现了这样的事情.
ENTRY (syscall)
movq %rdi, %rax /* Syscall number -> rax. */
movq %rsi, %rdi /* shift arg1 - arg5. */
movq %rdx, %rsi
movq %rcx, %rdx
movq %r8, %r10
movq %r9, %r8
movq 8(%rsp),%r9 /* arg6 is on the stack. */
syscall /* Do the system call. */
cmpq $-4095, %rax /* Check %rax for error. */
jae SYSCALL_ERROR_LABEL /* Jump to error handler if error. */
L(pseudo_end):
ret /* Return to caller. */
Run Code Online (Sandbox Code Playgroud)
现在我的问题是系统调用(在cmpq指令之前)是否是指令?其次,如果它是一个指令,ENTRY(系统调用)的含义是什么?ENTRY的同名(我不知道ENTRY是什么)和指令?其次,L(pseudo_end)是什么?
在汇编指令级别分析代码时,如果现代CPU不按顺序或按顺序执行指令,那么指令指针的位置真正意味着什么呢?例如,假设以下x64汇编代码:
mov RAX, [RBX]; // Assume a cache miss here.
mov RSI, [RBX + RCX]; // Another cache miss.
xor R8, R8;
add RDX, RAX; // Dependent on the load into RAX.
add RDI, RSI; // Dependent on the load into RSI.
Run Code Online (Sandbox Code Playgroud)
指令指针大部分时间用在哪条指令上?我可以为所有人想出好的论点:
mov RAX, [RBX] 大概需要100个周期,因为这是一个缓存未命中.mov RSI, [RBX + RCX]也需要100个周期,但可能与前一个指令并行执行.它甚至意味着指令指针位于其中一个或另一个上?xor R8, R8 可能在内存加载完成之前执行乱序并完成,但指令指针可能会保留在此处,直到所有先前的指令也完成为止.add RDX, RAX生成管道停顿,因为它RAX是在缓慢的缓存未命中加载之后实际使用值的指令.add RDI, RSI也停滞,因为它依赖于负载RSI.64位Linux内核的地址空间是多少,也就是说,它所使用的代码,堆栈,堆和数据段的地址范围是多少.
我正在尝试编写一些合理快速的组件向量加法代码.我正在使用(签名,我相信)64位整数.
功能是
void addRq (int64_t* a, const int64_t* b, const int32_t dim, const int64_t q) {
for(int i = 0; i < dim; i++) {
a[i] = (a[i]+b[i])%q; // LINE1
}
}
Run Code Online (Sandbox Code Playgroud)
我在icc -std=gnu99 -O3IvyBridge(SSE4.2和AVX,但不是AVX2)上编译(icc以便我以后可以使用SVML).
我的基线是%q从LINE1中删除.100(迭代)函数调用dim=11221184需要1.6秒.ICC自动矢量化SSE代码; 大.
我真的想做模块化的补充.使用%q,ICC不会自动向量化代码,它在11.8秒(!)内运行.即使忽略了之前尝试的自动矢量化,这似乎仍然过分.
由于我没有AVX2,SSE的矢量化需要SVML,这也许就是为什么ICC没有自动矢量化的原因.无论如何,这是我尝试对内循环进行矢量化:
__m128i qs = _mm_set1_epi64x(q);
for(int i = 0; i < dim; i+=2) {
__m128i xs = _mm_load_si128((const __m128i*)(a+i));
__m128i ys = _mm_load_si128((const __m128i*)(b+i));
__m128i zs = _mm_add_epi64(xs,ys);
zs = _mm_rem_epi64(zs,qs);
_mm_store_si128((__m128i*)(a+i),zs);
}
Run Code Online (Sandbox Code Playgroud)
主循环的汇编是: …
在以下伪代码中:
if (rdtscp supported by hardware) {
Invoke "rdtscp" instruction
} else {
Invoke "rdtsc" instruction
}
Run Code Online (Sandbox Code Playgroud)
假设CPU不支持该rdtscp指令,因此我们回退到else语句.
如果CPU错误预测分支,指令管道是否可能尝试执行rdtscp并抛出Illgal Instruction错误?
当我objdump -D用来反汇编二进制时,典型的代码jmpq就像e9 7f fe ff ff是,用于表示负偏移.但是,x86-64的地址是64(48)位(据我所知),那么这个32位地址如何7f fe ff ff表示64位绝对地址的负偏移?
此外,还有像任何其他指令jmp和jmpq,但有64位地址位移?我怎样才能找到英特尔或AMD手册中的说明(我搜索jmpq但没有找到任何内容)?
当我搜索时,它似乎被称为RIP相对寻址.似乎并非所有说明都这样做.是否有64位相对寻址?如果是间接跳转,64位绝对地址将在寄存器或内存中,对吧?
我喜欢std::unique_ptr在 c++11 中看到它的那一刻,但我质疑它的有效性很长一段时间。(有关实时代码的链接,请参见下面的链接):
#include <memory>
std::unique_ptr<int> get();
extern std::unique_ptr<int> val;
void foo()
{
val = get();
}
Run Code Online (Sandbox Code Playgroud)
这给了我关于 last clang 的 16 条指令-O3。但更有趣的是,它生成了两个对 的调用delete,即使第二个永远不会被调用。
比我尝试这样做:
void foo()
{
auto ptr = get().release();
val.reset(ptr);
}
Run Code Online (Sandbox Code Playgroud)
突然间只有 11 条指令。然后我更深入地破解了 unique_ptr move ctor。最初它是作为reset(__u.release());. 我基本上只是按如下方式重新排序:
auto& ptr = _M_ptr();
if (ptr)
_M_deleter()(ptr);
ptr = __u.release();
Run Code Online (Sandbox Code Playgroud)
Aaand.... 手动管理版本中的 11 条指令。它略有不同,但似乎还可以。
我在这里保存了我的实验。
有人可以指出是我遗漏了什么还是实际上是有意为之?
x86-64 ×10
assembly ×6
c ×3
linux ×2
c++ ×1
gcc ×1
intrinsics ×1
linux-kernel ×1
low-level ×1
performance ×1
profiling ×1
sse ×1
stdin ×1
unique-ptr ×1
unsigned ×1