最快捷的词汇方式

时间:2012-05-21 16:18:54

标签: c++ boost stdstring

使用C ++将单词(std :: string)大写的最快方法是什么?

在使用带有-O3标志的g ++ 4.6.3的Debian Linux上,使用boost::to_lower的此函数将在AMD Phenom(tm)II X6 1090T处理器的单个执行线程中大约24秒内占用81,450,625个字(3200 MHz)。

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 ++中使用boost工作,我想尝试一下。

感谢。

6 个答案:

答案 0 :(得分:4)

很少有办法让它更快:
1.不要使用to_lower,这很慢。不要使用if,创建一个包含256个条目的表,这些条目从字符映射到小写版本,另一个表格用于大写。
2.不要使用transform,指向第一个字符并循环直到空终止符 3.如果内存不是问题,请使用映射2个字符序列的表。在这种情况下,您将需要另一个处理终止的表 如果你能在装配中做到这一点,它会快得多。

答案 1 :(得分:3)

在处理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个字节但是使用上面的代码,大写对我们来说几乎是瞬时的。对于小写:do:

        *it |= 0x20;

答案 2 :(得分:1)

如果大写是真正瓶颈,那么请使用手写循环和内联toupper / tolower函数编写您自己的大写实现。 如果有必要,请使用ASM。

答案 3 :(得分:1)

我有一个实现,我发现它比std :: transform更快,编译在g ++ -03 Fedora 18中。

performance time in seconds :
transform took         : 11 s
my implementation took : 2 s
Test data size = 26*15*9999999 chars
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;
}

我很高兴知道性能是否可以进一步提高。

答案 4 :(得分: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中。

答案 5 :(得分:-1)

必需的伪代码是:  

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;