当区域设置时,Windows C运行时toupper会变慢
我正在诊断跨平台(
Windows和
Linux)应用程序中的边缘情况,其中toupper在Windows上要慢得多.我假设这对于tolower来说也是一样的.
最初我用一个简单的C程序测试了它,每个没有语言环境信息集,甚至包括头文件,性能差异很小.测试是一个百万次迭代循环,将字符串的每个字符调用到toupper()函数. 包含头文件并包含下面的行之后,它的速度要慢很多,并调用了很多MS C运行时库语言环境特定的函数.这很好,但性能受到了很大影响.在Linux上,这似乎对性能没有任何影响. setlocale(LC_ALL,""); // system default locale 如果我设置以下它运行速度与linux一样快,但似乎跳过所有语言环境功能. setlocale(LC_ALL,NULL); // should be interpreted as the same as below? OR setlocale(LC_ALL,"C"); 注意: 尝试过荷兰设置设置和相同的结果,在Windows上没有速度差在Linux上. 我做错了什么或者是否存在Windows上的语言环境设置的错误,或者它是linux没有做它应该做的事情的另一种方式? 下面的代码用于测试(Linux): // C++ is only used for timing. The original program is in C. #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <chrono> #include <locale.h> using namespace std::chrono; void strToUpper(char *strVal); int main() { typedef high_resolution_clock Clock; high_resolution_clock::time_point t1 = Clock::now(); // set locale //setlocale(LC_ALL,"nl_NL"); setlocale(LC_ALL,"en_US"); // testing string char str[] = "the quick brown fox jumps over the lazy dog"; for (int i = 0; i < 1000000; i++) { strToUpper(str); } high_resolution_clock::time_point t2 = Clock::now(); duration<double> time_span = duration_cast<duration<double>>(t2 - t1); printf("chrono time %2.6f:n",time_span.count()); } void strToUpper(char *strVal) { unsigned char *t; t = (unsigned char *)strVal; while (*t) { *t = toupper(*t); *t++; } } 对于Windows,将本地信息更改为: // set locale //setlocale(LC_ALL,"nld_nld"); setlocale(LC_ALL,"english_us"); 您可以在完成的时间内看到分隔符的区域设置更改,完全停止与逗号. 编辑 – 分析数据 正如您在上面看到的,大部分时间都来自_toupper_l的子系统调用. 没有语言环境信息设置,toupper调用不会调用子_toupper_l,这使得它非常快. 解决方法
与LANG = C vs. LANG相同(并且相当好)的性能= Linux所使用的glibc实现所需的其他内容.
你的Linux结果是有道理的.你的测试方法可能还可以.使用分析器查看您的microbenchmark在Windows功能中花费的时间.如果Windows实现确实是问题所在,也许有一个Windows函数可以转换整个字符串,比如C 另请注意,升级ASCII字符串可以非常有效地进行SIMD矢量化.我使用C SSE内在函数为单个向量in another answer编写了一个case-flip函数;它可以适应大写而不是翻盖.如果你花费大量时间来更新超过16个字节的字符串,并且你知道是ASCII,那么这应该是一个巨大的加速. 实际上,Boost的to_upper_copy() appears to compile to extremely slow code,like 10x slower than 您当前的代码如何处理UTF-8?如果假设所有字符都是单个字节,则支持非ASCII语言环境没有多大好处. IIRC,Windows使用UTF-16进行大多数工作,这很不幸,因为事实证明世界需要超过2 ^ 16个代码点. UTF-16是Unicode的可变长度编码,如UTF-8,但没有读取ASCII的优点.固定宽度有很多优点,但不幸的是你甚至不能假设使用UTF-16. Java也犯了这个错误,并且坚持使用UTF-16. The glibc source是: #define __ctype_toupper ((int32_t *) _NL_CURRENT (LC_CTYPE,_NL_CTYPE_TOUPPER) + 128) int toupper (int c) { return c >= -128 && c < 256 ? __ctype_toupper[c] : c; } 来自x86-64 Ubuntu 15.10的/lib/x86_64-linux-gnu/libc.so.6的asm是: ## disassembly from objconv -fyasm -v2 /lib/x86_64-linux-gnu/libc.so.6 /dev/stdout 2>&1 toupper: lea edx,[rdi+80H] ; 0002E300 _ 8D. 97,00000080 movsxd rax,edi ; 0002E306 _ 48: 63. C7 cmp edx,383 ; 0002E309 _ 81. FA,0000017F ja ?_01766 ; 0002E30F _ 77,19 mov rdx,qword [rel ?_37923] ; 0002E311 _ 48: 8B. 15,00395AA8(rel) sub rax,-128 ; 0002E318 _ 48: 83. E8,80 mov rdx,qword [fs:rdx] ; 0002E31C _ 64 48: 8B. 12 mov rdx,qword [rdx] ; 0002E320 _ 48: 8B. 12 mov rdx,qword [rdx+48H] ; 0002E323 _ 48: 8B. 52,48 mov eax,dword [rdx+rax*4] ; 0002E327 _ 8B. 04 82 ## the final table lookup,indexing an array of 4B ints ?_01766: rep ret ; actual objconv output shows the prefix on a separate line 因此,如果arg不在0 – 0xFF范围内(因此这个分支应该完全不被预测),它需要提前输出,否则它会找到当前语言环境的表,其中涉及三个指针解引用:一个来自一个全局的,一个本地的线程,还有一个解引用.然后它实际上索引到256条目表. 这是整个库函数;反汇编中的toupper标签是您的代码所调用的. (好吧,由于动态链接,通过PLT间接层,但是在第一次调用后触发了懒惰符号查找,它只是你的代码和库中11个insn之间的一条额外的jmp指令.) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- IIS配置
- windows-server-2008-r2 – Windows Server 2008 R2 Core支
- 事件处理MVVM WinRT
- windows-7 – 如何在Windows 7中使用MaraDNS解析外部域名
- Windows上的python 2.7 vs 2.6速度
- Windows 10 + kali Linux 双系统安装教程(详细版)
- windows – 如何在vim中键入某些命令,如CTRL-] ^ Wc等在德语
- Windows代码页与标准C/C++文件名的交互?
- windows-firewall – 由Windows防火墙阻止的Teamviewer VPN
- windows-server-2008 – Windows Server 2008 R2,重新安装而