使用C++将单词(std::string(大写的最快方法是什么?
在使用带有 -O3 标志的 g++ 4.6.3 的 Debian Linux 上,这个使用 boost::to_lower
的函数将在大约 24 秒内在 AMD Phenom(tm( II X6 1090T 处理器 (3200 MHz( 上的单个执行线程中大写 81,450,625 个单词。
void Capitalize( std::string& word )
{
boost::to_lower( word );
word[0] = toupper( word[0] );
}
这个使用 std::transform
的函数在大约 10 秒内完成同样的事情。我在测试之间清除了VM,因此我认为这种差异不是侥幸:
sync && echo 3 > /proc/sys/vm/drop_caches
void Capitalize( std::string& word )
{
std::transform(word.begin(), word.end(), word.begin(), ::tolower);
word[0] = toupper( word[0] );
}
有没有更快的方法?我不想为了速度而失去便携性,但如果有更快的方法可以做到这一点,可以在 std C++ 或 std C++ with boost 中工作,我想尝试一下。
谢谢。
在处理 DNA 序列时,输入不能保证是大写的,并且boost::to_upper
代码中的瓶颈是瓶颈时,有这个确切的问题。更改为:
template<typename T_it>
void SequenceToUpperCase( T_it begin, T_it end )
{
// Convert to upper: clear the '32' bit, 0x20 in hex. And with the
// inverted bit string (~).
for ( auto it = begin; it != end; ++it )
*it &= ~0x20;
}
导致速度大幅提高。我确信可以通过例如一次翻转 8 个字节来进一步优化,但使用上面的代码,大写对我们来说几乎是即时的。对于小写:执行:
*it |= 0x20;
几种使其更快的方法:
1.不要用to_lower
,速度慢。也不要使用if
,创建一个包含 256 个条目的表,该表从字符映射到其小写版本,另一个表映射到大写版本。
2.不要使用transform
,取一个指向第一个字符的指针并循环直到空终止符。
3. 如果内存不是问题,请使用映射 2 个字符序列的表。在这种情况下,您将需要另一个处理终止的表。
4.如果可以在组装中做到这一点,它会快得多。
如果大写是一个真正的瓶颈,那么使用手写循环和内联到上/下函数编写你自己的大写实现。如有必要,请使用 ASM。
我有一个实现,我发现它比std::transform更快,用g++ -03 Fedora 18编译。
表演时间(秒(:变换时间 : 11 s我的实现花了:2 秒测试数据大小 = 26*15*9999999 个字符
inline void tolowerPtr(char *p) ;
inline void tolowerStr(std::string& s)
{char* c=const_cast<char*>(s.c_str());
size_t l = s.size();
for(char* c2=c;c2<c+l;c2++)tolowerPtr(c2);
};
inline void tolowerPtr(char *p)
{
switch(*p)
{
case 'A':*p='a'; return;
case 'B':*p='b'; return;
case 'C':*p='c'; return;
case 'D':*p='d'; return;
case 'E':*p='e'; return;
case 'F':*p='f'; return;
case 'G':*p='g'; return;
case 'H':*p='h'; return;
case 'I':*p='i'; return;
case 'J':*p='j'; return;
case 'K':*p='k'; return;
case 'L':*p='l'; return;
case 'M':*p='m'; return;
case 'N':*p='n'; return;
case 'O':*p='o'; return;
case 'P':*p='p'; return;
case 'Q':*p='q'; return;
case 'R':*p='r'; return;
case 'S':*p='s'; return;
case 'T':*p='t'; return;
case 'U':*p='u'; return;
case 'V':*p='v'; return;
case 'W':*p='w'; return;
case 'X':*p='x'; return;
case 'Y':*p='y'; return;
case 'Z':*p='z'; return;
};
return ;
}
void testtransform( std::string& word )
{
std::string word2=word;
time_t t;
time_t t2;
time(&t);
std::cout << "testtransform: start " << "n";
int i=0;
for(;i<9999999;i++)
{ word2=word;
std::transform(word2.begin(), word2.end(), word2.begin(), ::tolower);
}
time(&t2);
std::cout << word2 << "n";
std::cout << "testtransform: end " << i << ":"<< t2-t << "n";
}
void testmytolower( std::string& word )
{
std::string word2=word;
time_t t;
time_t t2;
time(&t);
std::cout << "testmytolower: start " << "n";
int i=0;
for(;i<9999999;i++)
{ word2=word;
cstralgo::tolowerStr(word2);
}
time(&t2);
std::cout << word2 << "n";
std::cout << "testmytolower: end " << i << ":"<< t2-t << "n";
}
int main(int argc, char* argv[])
{
std::string word ="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
word =word+word+word+word+word+word+word+word+word+word+word+word+word+word+word;
testtransform( word);
testmytolower( word);
return 0;
}
我很高兴知道性能是否可以进一步提高。
我会使用 for 循环逐个字符遍历整个字符串,将它们解析为要转换为大写的函数。为了加快速度,我将在汇编中编写大写函数。应用程序的C++组件如下所示:
#include <iostream>
#include <string>
extern "C" char asm_capt(char x);
using namespace std;
int main(void)
{
string str;
cin >> str;
string tmp = str;
for(int i=0; i<str.length(); i++){
tmp.at(i) = asm_capt(str.at(i));
}
}
现在是组装部分;我假设您使用的是Windows和MASM编译器。代码应保存在 .asm 源文件中,并使用 MASM 生成设置包含在项目中:
.model flat
.code
_asm_capt proc
mov rax, rcx
cmp rax, 61h
jl already_capt
sub rax, 20h
ret
already_capt:
ret
_asm_capt endp
end
本质上,它会检查字符(十六进制(是否小于 0x61,这意味着,如果您只使用字母,它已经大写。 否则,该值将减少 0x20,这会将其移动到小写等效值。(请参阅 ASCII 表。
注意: 默认情况下,返回参数存储在 RAX 寄存器中;C++传递给程序集的第一个参数存储在 RCX 中。
所需的伪代码是:
for(int i=0 to given_str.length)
{ upper[i]=(char*)given_str[i]-32; // in ascii tbl, any lower case char - upper case char=32
}
return upper;