读书人

千分求最快的Base64编码函数,该怎么处

发布时间: 2012-03-28 15:40:03 作者: rapoo

千分求最快的Base64编码函数
为了便于查看和讨论,请移步到 “ http://community.csdn.net/Expert/topic/5665/5665480.xml?temp=.9847528 ” 查看或发帖

我有上万的可用分,可以开新贴给分

Base64编码是很常用的一种把二进制数据转换为字符串的算法;
(它把3个8bit数据分成4组6bit数据,然后拿6bit数映射到64个可显示字符集合中的一个)

我开贴征集最快的Base64编码函数,要求如下:
1.使用200MB随机数据来测试编码函数 (数据量最少的话也要20MB上)
2.测试单位时间(秒)编码出的字符数据量 (假设200MB数据用了2秒钟编码时间,那速度就是:200MB/3*4/2s=133.3MB/s)
3.必须说明测试使用的CPU型号(运行频率)、最好说明内存型号(运行频率) 、语言和编译器类型
4.可以测试已有的Base64编码函数(比如各种库中的、开源的等等),注明来源(可以不提供源代码),也可以是自己写的(最好能给出源代码)
5.可以补充测试一下Base64解码函数的速度
(如果是自己写的 看函数速度 给分时酌情加分)


==========================================================================
//testBase64.cpp
//一个简单的base64_encode编码函数例子,和测试框架
#include <time.h>
#include <iostream>
#include <vector>
#include <string>

// 3x8bit to 4x6bit
// |----------------------|----------------------|----------------------|
// | a[0..7] | b[0..7] | c[0..7] |
// |----------------------|----------------------|----------------------|
//
// |----------------------|----------------------|----------------------|----------------------|
// | a[2..7] | b[4..7] + a[0..1] < <4 | c[6..7] + b[0..3] < <2 | c[0..5] |
// |----------------------|----------------------|----------------------|----------------------|

const unsigned char BASE64_PADDING= '= '; //输入数据不足3的倍数时 输出字符后面填充 '= '号


inline unsigned char to_base64char(const unsigned char code6bit)
{
if (code6bit <26) //[ 0..25] => [ 'A '.. 'Z ']
return code6bit+ 'A ';
else if (code6bit <52) //[26..51] => [ 'a '.. 'z ']
return code6bit+( 'a '-26);
else if (code6bit <62) //[52..61] => [ '0 '.. '9 ']
return code6bit+( '0 '-52);
else if (code6bit==62) //62 => '+ '


return '+ ';
else //if (code6bit==63) //63 => '/ '
return '/ ';
}

void base64_encode(const void* pdata,const unsigned long data_size,void* out_pcode)
{
if (data_size <=0) return;
const unsigned char* input=(const unsigned char*)pdata;
const unsigned char* input_end=&input[data_size];
unsigned char* output=(unsigned char*)out_pcode;

const unsigned char* input_end_sub_2=input_end-2;
for(;input <input_end_sub_2;input+=3,output+=4)
{
output[0]=to_base64char(input[0]/4);
output[1]=to_base64char(input[0]%4*16 + input[1]/16);
output[2]=to_base64char(input[1]%16*4 + input[2]/64);
output[3]=to_base64char(input[2]%64);
}

unsigned long bord_width=input_end-input;
if (bord_width==1)
{
output[0]=to_base64char(input[0]/4);
output[1]=to_base64char(input[0]%4*16);
output[2]=BASE64_PADDING;
output[3]=BASE64_PADDING;
}
else if (bord_width==2)
{
output[0]=to_base64char(input[0]/4);
output[1]=to_base64char(input[0]%4*16 + input[1]/16);
output[2]=to_base64char(input[1]%16*4);
output[3]=BASE64_PADDING;
}
}


typedef void (*Tbase64_encode_proc)(const void* pdata,const unsigned long data_size,void* out_pcode);

inline unsigned long base64_code_size(const unsigned long data_size)
{
return (data_size+2)/3*4;
}


void testSpeed(const char* proc_name_str,Tbase64_encode_proc base64_encode,const long DATA_SIZE)
{
std::cout < < "> > 编码函数: " < <proc_name_str < <std::endl;

const long DATA_SIZE_MAX=DATA_SIZE+12;

std::vector <unsigned char> data_buf(DATA_SIZE_MAX); //data_buf保存需要编码的数据
for (long r=0;r <DATA_SIZE_MAX;++r)
data_buf[r]=rand(); //data_buf填充随机数据 用以测试

const long code_size_MAX=base64_code_size(DATA_SIZE_MAX);
std::string code_str;//code_str用以储存编码后的字符串数据
code_str.resize(code_size_MAX, ' ');

long RunCount=0;
double SumSpeed=0;


for (long data_size=DATA_SIZE;data_size <DATA_SIZE_MAX;++data_size)
{

const long code_size=base64_code_size(data_size);
double start_time=(double)clock();

base64_encode(&data_buf[0],data_size,&code_str[0]);//编码测试

double run_time=((double)clock()-start_time)*(1.0/CLOCKS_PER_SEC);

double encode_speed=code_size*(1.0/1024/1024)/run_time;//编码速度(MB/秒)
++RunCount;
SumSpeed+=encode_speed;

std::cout < < " 编码前数据大小(MB): " < <data_size*(1.0/1024/1024) < < " 编码速度(MB/秒): " < <encode_speed < <std::endl;
//if (data_size <=1000) std::cout < <code_str < <std::endl; //
}
std::cout < <std::endl < < " 平均编码速度(MB/秒): " < <SumSpeed/RunCount < <std::endl;
std::cout < <std::endl;

}

int main()
{
std::cout < < " 请输入任意字符开始测试(可以把进程优先级设置为“实时”)> ";
getchar();
std::cout < <std::endl;

const long DATA_SIZE=80*1024*1024;

testSpeed( "base64_encode " ,base64_encode ,DATA_SIZE);
return 0;
}
===========================================================================


例子的结果说明:
1.测试数据集80MB
2.base64_encode编码函数速度(进程为 "实时 "优先级): 90.8MB/s
3.CPU: AMD64x2 3600+(2.01G); 内存: DDR2 667(334.9MHz); 语言:C++; 编译器:VC2005


为了便于查看和讨论,请移步到 “ http://community.csdn.net/Expert/topic/5665/5665480.xml?temp=.9847528 ” 查看或发帖

[解决办法]
试试看下面这个,也是用汇编写的,取自faststrings,效率没有测试过,不过faststings里面的部分字符串处理函数我测试过,效率确实高。相信这个也会不错。
unit Base64;

interface

const
Base64_Table: shortstring = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ ';

function Base64Encode(const Source: AnsiString): AnsiString;
function Base64Decode(const Source: string): string;

implementation

//Encode to Base64
function Base64Encode(const Source: AnsiString): AnsiString;
var
NewLength: Integer;
begin
NewLength := ((2 + Length(Source)) div 3) * 4;
SetLength(Result, NewLength);

asm
Push ESI
Push EDI
Push EBX
Lea EBX, Base64_Table
Inc EBX // Move past String Size (ShortString)
Mov EDI, Result
Mov EDI, [EDI]
Mov ESI, Source
Mov EDX, [ESI-4] //Length of Input String
@WriteFirst2:
CMP EDX, 0
JLE @Done
MOV AL, [ESI]
SHR AL, 2
{$IFDEF VER140} // Changes to BASM in D6
XLATB
{$ELSE}
XLAT
{$ENDIF}
MOV [EDI], AL
INC EDI
MOV AL, [ESI + 1]


MOV AH, [ESI]
SHR AX, 4
AND AL, 63
{$IFDEF VER140} // Changes to BASM in D6
XLATB
{$ELSE}
XLAT
{$ENDIF}
MOV [EDI], AL
INC EDI
CMP EDX, 1
JNE @Write3
MOV AL, 61 // Add ==
MOV [EDI], AL
INC EDI
MOV [EDI], AL
INC EDI
JMP @Done
@Write3:
MOV AL, [ESI + 2]
MOV AH, [ESI + 1]
SHR AX, 6
AND AL, 63
{$IFDEF VER140} // Changes to BASM in D6
XLATB
{$ELSE}
XLAT
{$ENDIF}
MOV [EDI], AL
INC EDI
CMP EDX, 2
JNE @Write4
MOV AL, 61 // Add =
MOV [EDI], AL
INC EDI
JMP @Done
@Write4:
MOV AL, [ESI + 2]
AND AL, 63
{$IFDEF VER140} // Changes to BASM in D6
XLATB
{$ELSE}
XLAT
{$ENDIF}
MOV [EDI], AL
INC EDI
ADD ESI, 3
SUB EDX, 3
JMP @WriteFirst2
@done:
Pop EBX
Pop EDI
Pop ESI
end;
end;

//Decode Base64
function Base64Decode(const Source: string): string;
var
NewLength: Integer;
begin
{
NB: On invalid input this routine will simply skip the bad data, a
better solution would probably report the error

ESI -> Source String
EDI -> Result String

ECX -> length of Source (number of DWords)
EAX -> 32 Bits from Source
EDX -> 24 Bits Decoded

BL -> Current number of bytes decoded
}

SetLength(Result, (Length(Source) div 4) * 3);
NewLength := 0;
asm
Push ESI
Push EDI
Push EBX

Mov ESI, Source

Mov EDI, Result //Result address
Mov EDI, [EDI]

Or ESI,ESI // Nil Strings
Jz @Done

Mov ECX, [ESI-4]
Shr ECX,2 // DWord Count

JeCxZ @Error // Empty String

Cld

jmp @Read4

@Next:
Dec ECX
Jz @Done

@Read4:
lodsd

Xor BL, BL
Xor EDX, EDX

Call @DecodeTo6Bits
Shl EDX, 6
Shr EAX,8
Call @DecodeTo6Bits
Shl EDX, 6
Shr EAX,8
Call @DecodeTo6Bits
Shl EDX, 6
Shr EAX,8
Call @DecodeTo6Bits


// Write Word

Or BL, BL
JZ @Next // No Data

Dec BL
Or BL, BL
JZ @Next // Minimum of 2 decode values to translate to 1 byte

Mov EAX, EDX

Cmp BL, 2
JL @WriteByte

Rol EAX, 8

BSWAP EAX

StoSW

Add NewLength, 2

@WriteByte:
Cmp BL, 2
JE @Next
SHR EAX, 16
StoSB

Inc NewLength
jmp @Next

@Error:
jmp @Done

@DecodeTo6Bits:

@TestLower:
Cmp AL, 'a '
Jl @TestCaps
Cmp AL, 'z '
Jg @Skip
Sub AL, 71
Jmp @Finish

@TestCaps:
Cmp AL, 'A '
Jl @TestEqual
Cmp AL, 'Z '
Jg @Skip
Sub AL, 65
Jmp @Finish

@TestEqual:
Cmp AL, '= '
Jne @TestNum
// Skip byte
ret

@TestNum:
Cmp AL, '9 '
Jg @Skip


Cmp AL, '0 '
JL @TestSlash
Add AL, 4
Jmp @Finish

@TestSlash:
Cmp AL, '/ '
Jne @TestPlus
Mov AL, 63
Jmp @Finish

@TestPlus:
Cmp AL, '+ '
Jne @Skip
Mov AL, 62

@Finish:
Or DL, AL
Inc BL

@Skip:
Ret

@Done:
Pop EBX
Pop EDI
Pop ESI

end;

SetLength(Result, NewLength); // Trim off the excess
end;

end.

[解决办法]
SafeF8(A++.NET) 的代码速度不错,
lz的代码我pc上只有29.7M/s
maozefa(阿发伯) 的有47M/s
我的pc配置太差了

读书人网 >.NET

热点推荐