这是一段看似非常特殊的C++代码.出于某种奇怪的原因,奇迹般地对数据进行排序使得代码几乎快了六倍.
#include <algorithm>
#include <ctime>
#include <iostream>
int main()
{
// Generate data
const unsigned arraySize = 32768;
int data[arraySize];
for (unsigned c = 0; c < arraySize; ++c)
data[c] = std::rand() % 256;
// !!! With this, the next loop runs faster.
std::sort(data, data + arraySize);
// Test
clock_t start = clock();
long long sum = 0;
for (unsigned i = 0; i < 100000; ++i)
{
// Primary loop
for (unsigned c = 0; c < arraySize; ++c) …Run Code Online (Sandbox Code Playgroud) 在x86-64 Tour of Intel Manuals中,我读到了
也许最令人惊讶的事实是,诸如
MOV EAX, EBX自动将指令的高32位归零的指令RAX.
同一来源引用的英特尔文档(3.4.1.1 64位手动基本架构中的通用寄存器)告诉我们:
- 64位操作数在目标通用寄存器中生成64位结果.
- 32位操作数生成32位结果,在目标通用寄存器中零扩展为64位结果.
- 8位和16位操作数生成8位或16位结果.目标通用寄存器的高56位或48位(分别)不会被操作修改.如果8位或16位操作的结果用于64位地址计算,则将寄存器显式符号扩展为完整的64位.
在x86-32和x86-64汇编中,16位指令如
mov ax, bx
Run Code Online (Sandbox Code Playgroud)
不要表现出这种"奇怪"的行为,即eax的上层词被归零.
因此:引入这种行为的原因是什么?乍一看似乎不合逻辑(但原因可能是我习惯了x86-32汇编的怪癖).
我有一个带内循环的简单函数 - 它缩放输入值,在查找表中查找输出值,并将其复制到目标.(ftol_ambient是我从网上复制的一种技巧,用于将float快速转换为int).
for (i = 0; i < iCount; ++i)
{
iScaled = ftol_ambient(*pSource * PRECISION3);
if (iScaled <= 0)
*pDestination = 0;
else if (iScaled >= PRECISION3)
*pDestination = 255;
else
{
iSRGB = FloatToSRGBTable3[iScaled];
*pDestination = iSRGB;
}
pSource++;
pDestination++;
}
Run Code Online (Sandbox Code Playgroud)
现在我的查找表是有限的,并且浮点数是无限的,因此有可能出现一个一个错误.我用一些代码创建了一个函数副本来处理这种情况.请注意,唯一的区别是添加了2行代码 - 请忽略丑陋的指针转换.
for (i = 0; i < iCount; ++i)
{
iScaled = ftol_ambient(*pSource * PRECISION3);
if (iScaled <= 0)
*pDestination = 0;
else if (iScaled >= PRECISION3)
*pDestination = 255;
else
{
iSRGB = …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) 每次我做一个objdump -d我总是看到asm代码与批量的nop指令(指令什么都不做)
例如,采取相同的程序:
#include <stdio.h>
#include <math.h>
int main()
{
printf("Hello World!\n");
printf("cos: %f\n", cos(1));
return 1;
}
Run Code Online (Sandbox Code Playgroud)
示例的objdump在入口点的末尾有2个nops
0000000000400450 <_start>:
400450: 31 ed xor %ebp,%ebp
400452: 49 89 d1 mov %rdx,%r9
400455: 5e pop %rsi
400456: 48 89 e2 mov %rsp,%rdx
400459: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
40045d: 50 push %rax
40045e: 54 push %rsp
40045f: 49 c7 c0 00 06 40 00 mov $0x400600,%r8
400466: 48 c7 c1 70 05 40 00 mov $0x400570,%rcx
40046d: 48 c7 …Run Code Online (Sandbox Code Playgroud) 在对一些代码进行基准测试时,我发现即使是最无害的代码更改,它的执行时间也会有所不同.
我试图将下面的代码归结为最小的测试用例,但它仍然相当冗长(为此我道歉).几乎任何改变都会影响基准测试结果.
#include <string>
#include <vector>
#include <iostream>
#include <random>
#include <chrono>
#include <functional>
constexpr double usec_to_sec = 1000000.0;
// Simple convenience timer
class Timer
{
std::chrono::high_resolution_clock::time_point start_time;
public:
Timer() : start_time(std::chrono::high_resolution_clock::now()) { }
int64_t operator()() const {
return static_cast<int64_t>(
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now()-start_time).count()
);
}
};
// Convenience random number generator
template <typename T>
class RandGen
{
mutable std::default_random_engine generator;
std::uniform_int_distribution<T> distribution;
constexpr unsigned make_seed() const {
return static_cast<unsigned>(std::chrono::system_clock::now().time_since_epoch().count());
}
public:
RandGen(T min, T max) : generator(make_seed()), distribution(min, max) { } …Run Code Online (Sandbox Code Playgroud) 我想问一些与ILP有关的问题.
超标量处理器是标量和矢量处理器的混合体.那么我可以说矢量处理器的架构遵循超标量吗?
同时处理多个指令并不能使架构超标量化,因为流水线,多处理器或多核架构也实现了这一点.这意味着什么?
我读过'超标量CPU架构在单个处理器中实现了一种称为指令级并行的并行形式',超标量不能使用多个处理器?任何人都可以提供我使用超标量的例子吗?
VLIW,我已经完成了本文第9页的图4.它显示了一个通用的VLIW实现,没有复杂的重排序缓冲区和解码和调度逻辑.没有解码的术语令我感到困惑.
此致,anas anjaria
这是复制(转换)未签名寄存器的指令:http : //www.felixcloutier.com/x86/MOVZX.html
基本上,该指令具有8-> 16、8-> 32、8-> 64、16-> 32和16-> 64。
32-> 64转换在哪里?我需要为此使用签名版本吗?
如果是这样,如何将全64位用于无符号整数?
assembly ×3
c++ ×3
performance ×3
x86 ×3
optimization ×2
visual-c++ ×2
x86-64 ×2
benchmarking ×1
c ×1
intel ×1
java ×1
nop ×1
objdump ×1
visual-c++-6 ×1
vliw ×1