我正在寻求一种更快的办法来做到这一点:
int is_empty(char * buf, int size)
{
int i;
for(i = 0; i < size; i++) {
if(buf[i] != 0) return 0;
}
return 1;
}
我认识到,除极端情况外,我没有必要寻找微量优化,但我知道存在一种更快的方法,我很想知道这种方法是什么。
我正在寻求一种更快的办法来做到这一点:
int is_empty(char * buf, int size)
{
int i;
for(i = 0; i < size; i++) {
if(buf[i] != 0) return 0;
}
return 1;
}
我认识到,除极端情况外,我没有必要寻找微量优化,但我知道存在一种更快的方法,我很想知道这种方法是什么。
在许多结构中,比较1个结构的时间相当于4个或8个,有时甚至16个。 通常有4个 by(或长期),8个 too(长期或长期)。 16或16岁以上可能需要在线组装,例如使用病媒单位。
而且,一个部门误判确实受到伤害,可能会有助于消除分支机构。 例如,如果缓冲几乎总是空洞的,而不是对每块零块进行检测,就把两者加在一起并测试最终结果。
表示这一点在便携式C中是困难的:将char*
投到long*
违反了严格的别条。 但幸运的是,你可以使用<代码>memcpy,以方便地表达一种不结盟的、可以包括任何东西的多束弹。 编审员将优化工作,满足你们的愿望。
例如,关于Godbolt.org/z/3hXQe7”的《工作进展》(,关于Gobolt汇编商的《工作进展》()显示,你可以连续装载两个连续的“密码>;tint_fast32_t vars (64-bit)和 要使目标高效地汇编成册,而没有高效的不配负荷,就必须在启动守则中进行一些手工调整,甚至连那时,在装货时,电算法可能不会在“<条码>上
https://stackoverflow.com/questions/14936/faster-means-of-eck-for-an-empty-buffer-in-c/1493963#1493963” 驳回的想法:
int is_empty(char *buf, size_t size)
{
static const char zero[999] = { 0 };
return !memcmp(zero, buf, size > 999 ? 999 : size);
}
请注意,你可以将这一解决办法用于任意规模。 你可以这样做:
int is_empty(char *buf, size_t size)
{
char *zero = calloc(size);
int i = memcmp(zero, buf, size);
free(zero);
return i;
}
但是,任何有活力的记忆分配都将比你少。 第一个解决办法的唯一原因是它能够使用<代码>mcmp(>,该编码将由图书馆作者以组装语言进行手提,其速度将大大快于您在C中能够制定的任何内容。
EDIT:根据先前关于缓冲状态“类似”的观察,没有人提到优化: 如果一个缓冲地带没有空,在开始时或结束时,它可能不会空洞吗? 如果更可能最终制造香料,你可以最终开始检查,并可能看到业绩几乎没有提高。
EDIT 2. 由于被告在评论中:
int is_empty(char *buf, size_t size)
{
return buf[0] == 0 && !memcmp(buf, buf + 1, size - 1);
}
这基本上将缓冲本身进行比较,初步检查看第一个要素是否为零。 这样,任何非零元素都将导致<代码>mcmp()失效。 我不知道如何比较使用另一种版本,但我确实知道,如果第一个要素是非零,那么它会很快失败(在我们甚至 lo之前)。 如果您更有可能在结尾处安装新产品,将更改<代码>buf[0]至buf[size]
,以取得相同效果。
上述基准() 并不准确。 这意味着,func3比其他选择要快得多。
然而,如果你改变测试顺序,使func3在func2之前出现,那么你就会看到func2的速度要快得多。
在单一执行中采用综合基准......副作用很大,特别是在使用相同变量时。 更好地操作孤立的试验!
例如,将其改为:
int main(){
MEASURE( func3 );
MEASURE( func3 );
MEASURE( func3 );
MEASURE( func3 );
MEASURE( func3 );
}
请允许我:
func3: zero 14243
func3: zero 1142
func3: zero 885
func3: zero 848
func3: zero 870
这确实使我感到困惑,因为我看不出3号 f子的表现会比2号 f快得多。
四个功能用于测试一个简单基准的缓冲的零度:
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include <inttypes.h>
#define SIZE (8*1024)
char zero[SIZE] __attribute__(( aligned(8) ));
#define RDTSC(var) __asm__ __volatile__ ( "rdtsc" : "=A" (var));
#define MEASURE( func ) {
uint64_t start, stop;
RDTSC( start );
int ret = func( zero, SIZE );
RDTSC( stop );
printf( #func ": %s %12"PRIu64"
", ret?"non zero": "zero", stop-start );
}
int func1( char *buff, size_t size ){
while(size--) if(*buff++) return 1;
return 0;
}
int func2( char *buff, size_t size ){
return *buff || memcmp(buff, buff+1, size-1);
}
int func3( char *buff, size_t size ){
return *(uint64_t*)buff || memcmp(buff, buff+sizeof(uint64_t), size-sizeof(uint64_t));
}
int func4( char *buff, size_t size ){
return *(wchar_t*)buff || wmemcmp((wchar_t*)buff, (wchar_t*)buff+1, size/sizeof(wchar_t)-1);
}
int main(){
MEASURE( func1 );
MEASURE( func2 );
MEASURE( func3 );
MEASURE( func4 );
}
依据我原来的《刑法》:
func1: zero 108668
func2: zero 38680
func3: zero 8504
func4: zero 24768
如果你的方案只有x86,或只有x64,你可以方便地优化使用在线萨姆布。 REPE SCASD指示将扫描一个缓冲地带,直到发现非欧安会的口号。
由于没有同等的标准图书馆功能,因此不可能使用这些指示(如《苏菲尼亚法典》所确认)。
从头来看,如果你的缓冲时间是4比特(MASM syntax)的,那么就这样做了:
_asm {
CLD ; search forward
XOR EAX, EAX ; search for non-zero
LEA EDI, [buf] ; search in buf
MOV ECX, [buflen] ; search buflen bytes
SHR ECX, 2 ; using dwords so len/=4
REPE SCASD ; perform scan
JCXZ bufferEmpty: ; completes? then buffer is 0
}
Tomas
EDIT:用Tony D 固定装置更新
简而言之,你需要看到汇编者正在产生什么法典。
$ gcc -S -O3 -o empty.s empty.c
The content of the Assembly:
.text
.align 4,0x90
.globl _is_empty
_is_empty:
pushl %ebp
movl %esp, %ebp
movl 12(%ebp), %edx ; edx = pointer to buffer
movl 8(%ebp), %ecx ; ecx = size
testl %edx, %edx
jle L3
xorl %eax, %eax
cmpb $0, (%ecx)
jne L5
.align 4,0x90
L6:
incl %eax ; real guts of the loop are in here
cmpl %eax, %edx
je L3
cmpb $0, (%ecx,%eax) ; compare byte-by-byte of buffer
je L6
L5:
leave
xorl %eax, %eax
ret
.align 4,0x90
L3:
leave
movl $1, %eax
ret
.subsections_via_symbols
这非常优化。 The loop do three matters:
可以通过逐字比较来略微优化,但随后,你需要担心一致性和这种协调。
当所有其他失败时,首先,不进行猜测。
如果可能的话,利用内部变量核对缓冲(应当加以调整)。
我的头顶部(不完善、未经测试的法典)几乎肯定至少有一ug。 这只是一个总的想法:
/* check the start of the buf byte by byte while it s unaligned */
while (size && !int_aligned( buf)) {
if (*buf != 0) {
return 0;
}
++buf;
--size;
}
/* check the bulk of the buf int by int while it s aligned */
size_t n_ints = size / sizeof( int);
size_t rem = size / sizeof( int);
int* pInts = (int*) buf;
while (n_ints) {
if (*pInt != 0) {
return 0;
}
++pInt;
--n_ints;
}
/* now wrap up the remaining unaligned part of the buf byte by byte */
buf = (char*) pInts;
while (rem) {
if (*buf != 0) {
return 0;
}
++buf;
--rem;
}
return 1;
如果有X86,你可以使用SSE测试16个 by子:
#include "smmintrin.h" // note: requires SSE 4.1
int is_empty(const char *buf, const size_t size)
{
size_t i;
for (i = 0; i + 16 <= size; i += 16)
{
__m128i v = _mm_loadu_si128((m128i *)&buf[i]);
if (!_mm_testz_si128(v, v))
return 0;
}
for ( ; i < size; ++i)
{
if (buf[i] != 0)
return 0;
}
return 1;
}
这可能随着停业而得到进一步改善。
在拥有AVX的现代S86 CPU上,你甚至可以使用256倍的SIMD,在某个时候测试32台。
Hackers Delight book/site are all about Superiord C/assembly. 该网站有很好的参考文献,而且目前也相当可靠(AMD64,NUMA技术)。
查阅 rel=“nofollow noreferer”>fast memcpy,可改编为mcmp(或针对不变值的超重)。
我看到许多人说,协调问题使你无法用大话,但这并不总是真实的。 如果你重新寻找可行的守则,这当然是一个问题,但x86实际上会容忍错误的准入。 如果在ESFAGS(EFLAGS)中转而进行调和检查,这只会在x86上失败(当然,制件实际上并不一致)。
int is_empty(char * buf, int size) {
int i;
for(i = 0; i < size; i+= 4) {
if(*(int *)(buf + i) != 0) {
return 0;
}
}
for(; i < size; i++) {
if(buf[i] != 0)
return 0;
}
return 1;
}
尽管汇编者CAN将你原先的休息时间变成了一种基于文字的比较,与额外的跳跃相适应,但是它不会在任何正常的优化水平这样做,因为它缺乏信息。 如果规模不大,那么,以这种方式使该守则放慢,汇编者希望保守。
解决这一问题的方法是利用指导性优化。 如果你让海合会了解有关“豁免功能”的信息,那么它就愿意将这种功能与“调和”核对结合起来。 你们也可以用——funroll-all-loops强迫这种行为。
是否有任何人提到 lo? 在上述任何一种循环中,循环间接费用和指数化将很大。
Also, what is the probability that the buffer will actually be empty? That s the only case where you have to check all of it. If there typically is some garbage in the buffer, the loop should stop very early, so it doesn t matter.
如果你计划将其清除为零,那么,用<条形码>(buf,0,大小(buf)条码>,无论该代码是否为零,这或许会更快。
大小到零的休息(检查):
int is_empty(char * buf, int size)
{
while(size --> 0) {
if(buf[size] != 0) return 0;
}
return 1;
}
必须指出,我们可能无法使汇编者超前工作,因此能够最积极地优化你的编辑,并假定你可能不会更快。
a. 或处理所有使用点的人(未经过测试但可能表现良好):
int is_empty(char* buf, int size)
{
char* org = buf;
if (buf[size-1] == 1)
return 0;
buf[size-1] = 1;
while(! *buf++);
buf--;
return buf == org[size-1];
}
你在你们的提问中说,你正在寻求一种最可能不必要的微观刺激。 在通常情况下,托马斯和其他人采取的阿盟办法应当给你最快的成果。
然而,这还是大局。 如果你的缓冲确实很庞大,那么从一开始就开始,进行线性搜查肯定不是这样做的最快途径。 假定你的粉刷替代物在寻找大量连续空闲区域方面非常好,但在阵列结束时却有一些非empt。 所有线性搜索都需要阅读整个阵列。 另一方面,快速激励算法可以寻找任何非零要素,并较快地压缩足够大的数据集。
因此,在做任何类型的微型刺激之前,我将仔细研究你的缓冲地带的数据,看看这是否给你带来任何模式。 对于一个单一的1条,在缓冲地带随机分配的线性搜索(处理校对/平行处理)将是最快的办法,在其他情况下不一定如此。
最初的C码的在线组装版本(如果uiSize
>为= 0
,和/或阵列不产生分配的例外情况)。 或许使用<代码>try{} Collection(>),因为这可能比在守则中增加许多检查更快。 或者与我一样,试图不把具有无效价值的职能(通常不可行)。 至少增加一次UNL点检查和一份size != 0
检查,这是非常容易的。
unsigned int IsEmpty(char* pchBuffer, unsigned int uiSize)
{
asm {
push esi
push ecx
mov esi, [pchBuffer]
mov ecx, [uiSize]
// add NULL ptr and size check here
mov eax, 0
next_char:
repe scasb // repeat string instruction as long as BYTE ptr ds:[ESI] == 0
// scasb does pointer arithmetic for BYTES (chars), ie it copies a byte to al and increments ESI by 1
cmp cx,0 // did the loop complete?
je all_chars_zero // yes, array is all 0
jmp char_not_zero // no, loop was interrupted due to BYTE PTR ds:[ESI] != 0
all_chars_zero:
mov eax, 1 // Set return value (works in MASM)
jmp end
char_not_zero:
mov eax, 0 // Still not sure if this works in inline asm
end:
pop ecx
pop esi
}
}
这是在传单上写成的,但认为更正足够正确,是值得欢迎的。 如果有人知道如何从网上确定收益价值,请说明。
int is_empty(char * buf, int size)
{
int i, content=0;
for(i = 0; !content && i < size; i++)
{
content=content | buf(i); // bitwise or
}
return (content==0);
}