单词大写的最快方法

Fastest way to capitalize words

本文关键字:方法 单词大      更新时间:2023-10-16

使用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;