所以,我有这个代码:
constexpr unsigned N = 1000;
void f1(char* sum, char* a, char* b) {
for(int i = 0; i < N; ++i) {
sum[i] = a[i] + b[i];
}
}
void f2(char* sum, char* a, char* b) {
char* end = sum + N;
while(sum != end) {
*sum++ = *a++ + *b++;
}
}
Run Code Online (Sandbox Code Playgroud)
我想看看GCC 4.7.2会产生的代码.所以我跑了g++ -march=native -O3 -masm=intel -S a.c++ -std=c++11并获得了以下输出:
.file "a.c++"
.intel_syntax noprefix
.text
.p2align 4,,15
.globl _Z2f1PcS_S_
.type _Z2f1PcS_S_, @function
_Z2f1PcS_S_:
.LFB0:
.cfi_startproc …Run Code Online (Sandbox Code Playgroud) 我是C的新手,它是继Java之后的第二种高级编程语言.我已经掌握了大部分基础知识,但无论出于何种原因,我无法将单个字符写入屏幕内存.
该程序使用Turbo C for DOS编译,运行速度为120mhz的Am486-DX4-100.该显卡是使用Trio32芯片的非常标准的VLB Diamond Multimedia Stealth SE.
对于操作系统,我运行的PC-DOS 2000加载了ISO代码页.我正在使用标准的MDA/CGA/EGA/VGA 80列文本模式运行.
这是我编写的程序:
#include <stdio.h>
int main(void) {
unsigned short int *Video = (unsigned short int *)0xB8000;
*Video = 0x0402;
getchar();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
正如我所说,我对C很新,所以如果我的错误显而易见,我道歉,我无法找到一个可以理解的如何做到这一点的可靠来源.
据我所知,在x86平台上的实模式下,文本模式的屏幕内存从0xB8000开始.每个字符存储在两个字节中,一个用于字符,一个用于背景/前景.我的想法是将值0x0402(应该是一个红色的笑脸)写入0xB8000.这应该放在屏幕的左上角.
我已经考虑了屏幕可能滚动的可能性,因此在执行时会立即以两种方式删除我的角色.要解决此问题,我尝试过:
我可以读取并打印我写入内存的值,所以它显然仍然在内存中,但无论出于何种原因,我都没有在屏幕上显示任何内容.我显然做错了,但我不知道会出现什么问题.如果需要任何其他细节,请询问.感谢您提供任何可能的帮助.
我在我的C++代码的内部循环中使用了128位整数计数器.(不相关背景:实际应用是评估规则网格上的有限差分方程,其中涉及重复递增大整数,甚至64位也不够精确,因为小的舍入累积足以影响答案.)
我将整数表示为两个64位无符号长整数.我现在需要将这些值递增128位常数.这并不难,但你必须手动捕捉低字到高字的进位.
我有这样的工作代码:
inline void increment128(unsigned long &hiWord, unsigned long &loWord)
{
const unsigned long hiAdd=0x0000062DE49B5241;
const unsigned long loAdd=0x85DC198BCDD714BA;
loWord += loAdd;
if (loWord < loAdd) ++hiWord; // test_and_add_carry
hiWord += hiAdd;
}
Run Code Online (Sandbox Code Playgroud)
这是一个紧凑而简单的代码.有用.
不幸的是,这大约是我运行时的20%.这条杀手线就是低价测试.如果我删除它,我显然得到了错误的答案,但运行时开销从20%下降到4%!因此携带测试特别昂贵!
我的问题:C++是否公开了硬件进位标志,即使是作为GCC的扩展?如果实际编译的指令使用最后一个进位指令进行添加,似乎可以在没有上面的测试和添加进位线的情况下完成添加.有没有办法重写test-and-add-carry行以使编译器使用内部操作码?
据我所知道的,唯一的区别__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的范围
在尝试使用内在函数和汇编来回答嵌入式广播时,我试图做这样的事情:
__m512 mul_broad(__m512 a, float b) {
int scratch = 0;
asm(
"vbroadcastss %k[scalar], %q[scalar]\n\t" // want vbr.. %xmm0, %zmm0
"vmulps %q[scalar], %[vec], %[vec]\n\t"
// how it's done for integer registers
"movw symbol(%q[inttmp]), %w[inttmp]\n\t" // movw symbol(%rax), %ax
"movsbl %h[inttmp], %k[inttmp]\n\t" // movsx %ah, %eax
: [vec] "+x" (a), [scalar] "+x" (b), [inttmp] "=r" (scratch)
:
:
);
return a;
}
Run Code Online (Sandbox Code Playgroud)
的GNU C 86操作数修饰符文档仅指定到改性剂q(DI(DoubleInt)尺寸,64位).使用q一个向量寄存器总会带给它归结为xmm(从ymm或zmm). …
GCC具有128位整数.使用这些我可以让编译器使用mul(或imul只有一个操作数)指令.例如
uint64_t x,y;
unsigned __int128 z = (unsigned __int128)x*y;
Run Code Online (Sandbox Code Playgroud)
生产mul.我用它来创建一个128x128到256的函数(在更新之前,请参阅此问题的结尾,如果您感兴趣,请参阅此代码).
现在我想要进行256位加法,ADC除了使用汇编之外,我还没有找到让编译器使用的方法.我可以使用汇编程序,但我想要内联函数以提高效率.编译器已经生成了一个有效的128x128到256函数(因为我在这个问题的开头解释了)所以我不明白为什么我应该在汇编中重写它(或者编译器已经有效实现的任何其他函数) .
这是我提出的内联汇编函数:
#define ADD256(X1, X2, X3, X4, Y1, Y2, Y3, Y4) \
__asm__ __volatile__ ( \
"addq %[v1], %[u1] \n" \
"adcq %[v2], %[u2] \n" \
"adcq %[v3], %[u3] \n" \
"adcq %[v4], %[u4] \n" \
: [u1] "+&r" (X1), [u2] "+&r" (X2), [u3] "+&r" (X3), [u4] "+&r" (X4) \
: [v1] "r" (Y1), [v2] "r" (Y2), [v3] "r" (Y3), [v4] …Run Code Online (Sandbox Code Playgroud) 我正在尝试在Linux内核空间中禁用/启用缓存.
我使用的代码是
__asm__ __volatile__(
"pushw %eax\n\t" /*line 646*/
"movl %cr0,%eax\n\t"
"orl $0x40000000,%eax\n\t"
"movl %eax,%cr0\n\t"
"wbinvd\n\t"
"pop %eax");
Run Code Online (Sandbox Code Playgroud)
编译后,我得到如下错误信息:
memory.c: Assembler messages:
memory.c:645: Error: operand type mismatch for `push'
memory.c:646: Error: unsupported for `mov'
memory.c:648: Error: unsupported for `mov'
memory.c:650: Error: operand type mismatch for `pop'
make[4]: *** [memory.o] Error 1
Run Code Online (Sandbox Code Playgroud)
我的机器是Intel(R)Xeon(R)CPU E5-1650 v2 @ 3.50GHz.64位机器.
任何人都可以帮我指出哪个部分不正确以及我如何解决它?
我猜这是因为指令与寄存器的不匹配.但我对如何解决它感到困惑.:(
提前致谢!
我正在编写C++代码来查找内存中非0xFF的第一个字节.为了利用bitscanforward,我编写了一个我非常喜欢的内联汇编代码.但是对于"可读性"以及未来的校对(即SIMD矢量化),我想我会给g ++优化器一个机会.g ++没有矢量化,但它确实得到了我所做的几乎相同的非SIMD解决方案.但由于某种原因,它的版本运行速度慢得多,速度慢260000倍(即我必须循环我的版本260,000x才能达到相同的执行时间).我除了一些差异,但不是那么多!有人可以指出它为什么会这样吗?我只是想知道在未来的内联汇编代码中出错.
C++的起点如下,(就计数准确性而言,此代码中存在一个错误,但我已将其简化为此速度测试):
uint64_t count3 (const void *data, uint64_t const &nBytes) {
uint64_t count = 0;
uint64_t block;
do {
block = *(uint64_t*)(data+count);
if ( block != (uint64_t)-1 ) {
/* count += __builtin_ctz(~block); ignore this for speed test*/
goto done;
};
count += sizeof(block);
} while ( count < nBytes );
done:
return (count>nBytes ? nBytes : count);
}
Run Code Online (Sandbox Code Playgroud)
汇编代码g ++提出的是:
_Z6count3PKvRKm:
.LFB33:
.cfi_startproc
mov rdx, QWORD PTR [rsi]
xor eax, eax
jmp .L19
.p2align 4,,10
.p2align 3 …Run Code Online (Sandbox Code Playgroud) 我正在为x86-64系统开发setjmp/ longjmp自定义实现,它保存了CPU的整个上下文(即所有xmm,fpu堆栈等;不仅是callee-save寄存器).这是直接在汇编中编写的.
代码在最小的示例中工作正常(直接从汇编源调用它时).由于参数传递给自制程序setjmp/ longjmp函数的方式,在使用C代码时会出现问题.实际上,x64_64系统的SysV ABI规定参数应该通过寄存器传递(如果它们最多为6).我的职能签名是:
long long set_jmp(exec_context_t *env);
__attribute__ ((__noreturn__)) void long_jmp(exec_context_t *env, long long val);
当然,这不能正常工作.事实上,当我进入set_jmp,rdi并rsi已经被破坏,以保持指针env和val.这同样适用long_jmp于rdi.
有没有办法强制GCC,例如依靠某些属性,强制通过堆栈传递参数?这将比包装set_jmp和long_jmp使用某些定义更加优雅,这些定义手动将堆栈寄存器压入堆栈,以便稍后检索它们.
assembly ×6
c ×6
gcc ×6
c++ ×4
x86 ×3
performance ×2
visual-c++ ×2
algorithm ×1
avx512 ×1
bigint ×1
carryflag ×1
linux-kernel ×1
optimization ×1
real-mode ×1
sse ×1
turbo-c ×1
x86-16 ×1
x86-64 ×1