我正在尝试一个词法分析器,我发现在程序的一个部分中从while循环切换到if语句和do-while循环导致代码变得快20%,这看起来很疯狂.我将编译器生成的代码的差异隔离到这些汇编代码段.有谁知道为什么快速代码更快?
在程序集中,'edi'是当前文本位置,'ebx'是文本的结尾,'isAlpha'是一个查找表,如果字符是字母,则为1,否则为0.
慢代码:
slow_loop:
00401897 cmp edi,ebx
00401899 je slow_done (4018AAh)
0040189B movzx eax,byte ptr [edi]
0040189E cmp byte ptr isAlpha (4533E0h)[eax],0
004018A5 je slow_done (4018AAh)
004018A7 inc edi
004018A8 jmp slow_loop (401897h)
slow_done:
Run Code Online (Sandbox Code Playgroud)
快速代码:
fast_loop:
0040193D inc edi
0040193E cmp edi,ebx
00401940 je fast_done (40194Eh)
00401942 movzx eax,byte ptr [edi]
00401945 cmp byte ptr isAlpha (4533E0h)[eax],0
0040194C jne fast_loop (40193Dh)
fast_done:
Run Code Online (Sandbox Code Playgroud)
如果我只针对仅包含字母"a"的兆字节文本运行这些汇编代码段,则快速代码的速度提高30%.我的猜测是慢速代码由于分支错误预测而变慢,但我认为在循环中这是一次性成本.
这是我用来测试两个片段的程序:
#include <Windows.h>
#include <string>
#include <iostream>
int main( int argc, char* argv[] )
{
static char …Run Code Online (Sandbox Code Playgroud) 指令如何rep stosb比这段代码执行得更快?
Clear: mov byte [edi],AL ; Write the value in AL to memory
inc edi ; Bump EDI to next byte in the buffer
dec ecx ; Decrement ECX by one position
jnz Clear ; And loop again until ECX is 0
Run Code Online (Sandbox Code Playgroud)
在所有现代CPU上都能保证这一点吗?我是否应该总是喜欢使用rep stosb而不是手动编写循环?
使用以下代码是否存在任何执行速度差异:
cmp al, 0
je done
Run Code Online (Sandbox Code Playgroud)
以下内容:
or al, al
jz done
Run Code Online (Sandbox Code Playgroud)
我知道JE和JZ指令是相同的,并且使用OR可以提供一个字节的大小改进.但是,我也关心代码速度.逻辑运算符似乎比SUB或CMP更快,但我只是想确定.这可能是规模和速度之间的权衡,或双赢(当然代码将更加不透明).
很难以相信构造p[u+1]发生在代码的最内层循环中的几个地方,我保持这样,在正确运行数天的操作中,对其进行微观优化会产生数小时的差异.
通常*((p+u)+1)是最有效的.有时候*(p+(u+1))效率最高.很少*((p+1)+u)是最好的.(但通常优化器可以转换*((p+1)+u)为*((p+u)+1)后者更好,并且不能*(p+(u+1))与其他任何一个转换).
p是一个指针,u是一个无符号的.在实际代码中,它们中的至少一个(更可能两者)在表达式被评估的点处已经在寄存器中.这些事实对我的问题至关重要.
在32位(在我的项目放弃支持之前)中,所有三个都具有完全相同的语义,并且任何一半不错的编译器只选择三者中最好的并且程序员永远不需要关心.
在这些64位用法中,程序员知道这三者具有相同的语义,但编译器不知道.就编译器所知,何时u从32位扩展到64位的决定会影响结果.
什么是最简洁的方法告诉编译器这三个语义是相同的,编译器应该选择最快的?
在一个Linux 64位编译器中,我几乎在那里p[u+1L]使编译器在通常最好*((p+u)+1)和有时更好之间智能地选择*(p+( (long)(u) + 1) ).在极少数情况下*(p+(u+1))仍然比第二种更好,有点丢失.
显然,这在64位Windows中没有用.既然我们已经放弃了32位支持,那么可能p[u+1LL]足够便携且足够好.但我能做得更好吗?
请注意,使用std::size_t而不是unsignedfor u将消除整个问题,但会在附近产生更大的性能问题.铸造u到std::size_t那里几乎是足够好,也许我能做到的最好.但对于一个不完美的解决方案来说,这是非常冗长的.
简单编码(p+1)[u]使选择更可能是最佳选择p[u+1].如果代码的模板化程度较低且更稳定,我可以将它们全部设置为(p+1)[u]然后进行配置文件,然后再将其切换回p[u+1].但是模板化往往会破坏这种方法(在配置文件的许多地方出现单一的源代码行,导致严重的时间,但不是单独的严重时间).
对此应该有效的编译器是GCC,ICC和MSVC.
我正在研究一个6502 cpu的汇编语言程序,我发现我需要一个快速尽可能快的七分程序,特别是一个可以获得16位分红的程序.
我熟悉这里发现的例程,但是对七分法例程进行了概括,发现它非常复杂,粗略地检查了一般算法(使用整数除法)
x/7~ =(x + x/8 + x/64 ...)/ 8
表示要处理16位范围,由于6502的单个累加器寄存器和6502上各个存储器位移的相对慢速,可能需要100多个周期才能完成.
我认为查找表可能会有所帮助,但在6502上,我当然只限于256字节或更少的查找表.为此,可以假设存在两个256字节的查找表xdiv7和xmod7,当使用无符号的单字节值作为表的索引时,可以快速获得字节除以7或模数的结果分别为7.但是,我不确定如何利用它们来查找完整16位范围的值.
与此同时,我还需要一个模7算法,尽管理想情况下,可以通过除法得到的解决方案也会产生mod7结果.如果需要额外的预计算表,只要所有表的总内存需求不超过约3k,我就可以添加这些表.
虽然我最终需要一个带符号的除法算法,但是一个无符号算法就足够了,因为我可以根据需要将它推广到一个有符号的例程.
任何帮助将不胜感激.
当我有私有方法或字段的内部类时,编译器必须创建合成的包受保护的访问器方法,以允许外部类访问这些私有元素(反之亦然).
为了避免这种情况,我通常会将所有字段和方法以及构造函数保护为包而不是私有.
但是班级本身的知名度如何呢?有没有开销
private static class A {
A(){}
}
Run Code Online (Sandbox Code Playgroud)
与
static class A {
A(){}
}
Run Code Online (Sandbox Code Playgroud)
请注意,构造函数在两种情况下都是受包保护的,或者是否使类私有更改?
只是想知道这样的事情的最佳做法是什么:
一个返回多个变量的函数 - 如何返回这些变量?
像这样(全球化):
function myfun(){
global $var1,$var2,$var3;
$var1="foo";
$var2="foo";
$var3="foo";
}//end of function
Run Code Online (Sandbox Code Playgroud)
或者像这样(返回一个数组):
function myfun(){
$var1="foo";
$var2="foo";
$var3="foo";
$ret_var=array("var1"=>$var1,"var2"=>$var2,"var3"=>$var3);
return $ret_var;
}//end of function
Run Code Online (Sandbox Code Playgroud)
我做了性能测试,看起来使用数组更快(刷新几次后):
array took: 5.9999999999505E-6
global took: 2.0999999999938E-5
Run Code Online (Sandbox Code Playgroud)
但我很想知道哪种方法最适合这种简单的情况?
int eq3(int a, int b, int c, int d, int e, int f){
return a == d || a == e || a == f
|| b == d || b == e || b == f
|| c == d || c == e || c == f;
}
Run Code Online (Sandbox Code Playgroud)
如果3个第一个int中的任何一个等于3个最后一个int中的任何一个,则此函数接收6个int并返回true.是否有任何类似的方式使其更快?
如果可能,我如何改进以下快速排序(性能明智).有什么建议?
void main()
{
quick(a,0,n-1);
}
void quick(int a[],int lower,int upper)
{
int loc;
if(lower<upper)
{
loc=partition(a,lower,upper);
quick(a,lower,loc-1);
quick(a,loc+1,upper);
}
}
/* Return type: int
Parameters passed: Unsorted array and its lower and upper bounds */
int partition(int a[],int lower,int upper)
{
int pivot,i,j,temp;
pivot=a[lower];
i=lower+1;
j=upper;
while(i<j)
{
while((i<upper)&&(a[i]<=pivot))
i++;
while((a[j]>pivot))
j--;
if(i<j)
{
temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}//end while
if(pivot>a[j])
{
temp=a[j];
a[j]=a[lower];
a[lower]=temp;
}
return(j);
}//end partition
Run Code Online (Sandbox Code Playgroud) 我的主要问题是,执行时间int和int8_t之间有什么区别吗?
在我正在研究的框架中,我经常阅读代码,其中一些参数被设置为int8_t函数,因为"该特定参数不能超出-126,125范围".
在很多地方,int8_t用于通信协议,或将数据包分成多个字段__attribute((packed)) struct.
但是在某些时候,它主要是因为有人认为使用与数据大小更匹配的类型会更好,可能会先考虑编译器.
鉴于代码是在Linux上运行,使用glibc使用gcc编译,并且内存或可移植性不是问题,我想知道它是否真的是一个好主意,性能方面.
我的第一印象来自于"试图比编译器更聪明的规则总是一个坏主意"(除非您知道需要优化的位置和方式).
但是,我不知道使用int8_t是否实际上是性能成本(更多测试和计算以匹配int8_t大小,需要更多操作来确保变量不会超出范围等),或者它确实提高了性能某种方式.
我不擅长阅读简单的asm,所以我没有将测试代码编译成asm以试图知道哪个更好.
我试图找到一个相关的问题,但所有讨论中,我发现int<size>_t与int约可移植性,而不是性能.
感谢您的输入.我们将非常感谢组装样品的解释或有关此问题的来源.