liangbch
发表于 2014-12-18 08:19:46
本楼是讨论汇编优化的,不喜欢汇编的人请忽略本楼。
在28#的代码中,“if ( g_used [ i ] ) continue” 这句是用来查找一个值为0的元素,这使我立即想起c运行时刻函数strlen,这个函数也是用来查找值为0的元素,不过其数组是char型而不是整型。如果将这段代码改为调用strlen函数,效果如何呢?说干就干 ,于是我又写了一版调用函数strlen的版本。另外,我有加了2个strlen函数的汇编的版本。源代码是来自于丹麦的Anger Fog。下面是调用strlen函数的版本。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
/* 说明:
我们定义尺子编号为i,(i>=0), 尺子i总是有i+1段。
我们首先搜索编号最大的尺子,然后搜索编号次小的尺子,最后搜索编号为0的尺子,在本程序尺子编号用rule_idx来表示
*/
#define MAX_RULES_COUNT 8 //最多可搜索8把尺子
#define MAX_SEG_COUNT120 //最多可覆盖120种不同的长度
char g_used; //used=1表示长度i可被尺子测量
int g_maxSegArr[]={1,4,10,20,35,56,84,120}; //maxSegArr表示尺子i可测量的不同长度的总数
int g_result; // result表示尺子i第j+1段的长度
int g_count;
// The asm code is from Anger Fog "optimization_manuals"
_declspec(naked)
int anger_strlen( char *s )
{
__asm
{
push ebx
mov ecx, ; get pointer to string
mov eax, ecx ; copy pointer
and ecx, 3 ; lower 2 bits of address, check alignment
jz L2 ; string is aligned by 4. Go to loop
and eax, -4 ; align pointer by 4
mov ebx, ; read from nearest preceding boundary
shl ecx, 3 ; mul by 8 = displacement in bits
mov edx, -1
shl edx, cl ; make byte mask
not edx ; mask = 0FFH for false bytes
or ebx, edx ; mask out false bytes
; check first four bytes for zero
lea ecx, ; subtract 1 from each byte
not ebx ; invert all bytes
and ecx, ebx ; and these two
and ecx, 80808080H ; test all sign bits
jnz L3 ; zero-byte found
; Main loop, read 4 bytes aligned
L1: add eax, 4 ; increment pointer by 4
L2: mov ebx, ; read 4 bytes of string
lea ecx, ; subtract 1 from each byte
not ebx ; invert all bytes
and ecx, ebx ; and these two
and ecx, 80808080H ; test all sign bits
jz L1 ; no zero bytes, continue loop
L3: bsf ecx, ecx ; find right-most 1-bit
shr ecx, 3 ; divide by 8 = byte index
sub eax, ; subtract start address
add eax, ecx ; add index to byte
pop ebx
ret
}
}
// The asm code is from Anger Fog "optimization_manuals"
_declspec(naked)
int anger_strlen_sse2( char *s )
{
__asm
{
mov eax, ; get pointer to string
mov ecx,eax ; copy pointer
pxor xmm0, xmm0 ; set to zero
and ecx,15 ; lower 4 bits indicate misalignment
and eax,-16 ; align pointer by 16
movdqa xmm1, ; read from nearest preceding boundary
pcmpeqbxmm1, xmm0 ; compare 16 bytes with zero
pmovmskb edx,xmm1 ; get one bit for each byte result
shr edx,cl ; shift out false bits
shl edx,cl ; shift back again
bsf edx,edx ; find first 1-bit
jnz L2 ; found
; Main loop, search 16 bytes at a time
L1: add eax,16 ; increment pointer by 16
movdqa xmm1, ; read 16 bytes aligned
pcmpeqbxmm1, xmm0 ; compare 16 bytes with zero
pmovmskb edx,xmm1 ; get one bit for each byte result
bsf edx,edx ; find first 1-bit
jz L1 ; loop if not found
L2: ; Zero-byte found. Compute string length
sub eax, ; subtract start address
add eax,edx ; add byte index
ret
}
}
void output_result( int rule_count)
{
int i,j;
for(i=0;i<rule_count;i++)
{
printf("(");
for(j=0;j<=i;j++)
printf("%d,",g_result);
printf(")\n");
}
printf("\n");
g_count++;
}
int check(int rule_idx, int deep, int *pNewSegCount, int newSegs[] )
{
int i,c,sum;
for (c=0,sum=0,i=deep-1;i>=0;i--)
{
sum += g_result;
if ( g_used )
return 0;
g_used=32;
newSegs=sum;
*pNewSegCount=c;
}
return 1;
}
//deep: 表示将要确定尺子第deep+1段的长度
void new_mark(int rule_count,int seg_count,int rule_idx, int deep)
{
int i,j,mid,flag,sum,new_used_count;
int new_used;
mid=rule_idx/2; //第rule_idx+1把尺子中间段的编号
for (sum=0,i=0;i<deep;i++)
sum += g_result;
for (i=1;i<=seg_count;i++)
{
//i+= strlen(g_used+i);
//i+= anger_strlen(g_used+i);
i+= anger_strlen_sse2(g_used+i);
if ( i + sum > seg_count)
break;
if ( deep==mid+1 && i< g_result )
continue;//消除重复的结果
g_result=i;
flag=check(rule_idx,deep+1,&new_used_count,new_used);
if (flag )
{
if ( rule_idx==0)
output_result(rule_count);
if ( deep==rule_idx)
new_mark(rule_count,seg_count,rule_idx-1,0);
else
new_mark(rule_count,seg_count,rule_idx,deep+1);
}
for ( j=0;j<new_used_count;j++)
g_used]=0;
}
}
void search(int n)
{
DWORD t;
printf("\nIs searching the soultion when rule count is %d\n",n);
t=::GetTickCount();
g_count=0;memset(g_result,0,sizeof(g_result));
new_mark(n,g_maxSegArr,n-1,0);
t=::GetTickCount()-t;
printf("It take %d ms and find %d solution\n",t,g_count);
}
//int main(int argc, char* argv[])
//{
// int i;
// for (i=1;i<=6;i++ ) search(i);
// return 0;
//}
liangbch
发表于 2014-12-18 08:30:42
效果?
效果不是很明显,调用3种strlen函数的版本的速度区别不大,在忽略误差的情况下,与原始版本差不多,可能的原因有
1. 这段代码不是热点。
2. 值为0的元素相对比较多,函数strlen查找的长度较小;
3. g_used[ i ]的地址在绝大部分情况下不是4字节对齐。汇编版本难以施展其威力。
我们不应怀疑汇编函数的意义。在字符串长度相对较长的情况下,在字符串地址是4字节对齐的情况下。汇编版本的strlen应该具有很大的优势。
zeroieme
发表于 2014-12-18 18:14:05
渣机器渣代码跑了半小时……
于是,我想能否转成跳舞链的01矩阵
毒酒滴冻鸭
发表于 2014-12-19 11:02:32
上次忘了,还有一张把全部56个长度的量法都列出的图。。。
毒酒滴冻鸭
发表于 2014-12-28 23:35:59
非常感谢gxqcn大大的赐精!:loveliness:
有空再多发两道困扰我多时的题目。。。:)
王守恩
发表于 2019-12-18 11:21:12
我们好像可以约定每把尺子的长度。
1 把尺子=1
2 把尺子=4, 3
3 把尺子=10, 9, 7
4 把尺子=20, 19, 17, 14
5 把尺子=35, 34, 32, 29,25
6 把尺子=56, 55, 53, 50, 46,41
7 把尺子=84, 83, 81, 78, 74, 69, 63