isdigit() 在 C++ 中不应该更快吗?

Shouldn't isdigit() be faster in c++?

我在 C++ 中使用 isdigit() 函数,但我发现它很慢,所以我实现了自己的 is_digit(),请参阅下面的代码:

#include<iostream>
#include<cctype>
#include<ctime>
using namespace std;
static inline bool is_digit(char c)
{
    return c>='0'&&c<='9';
}
int main()
{
    char c='8';
    time_t t1=clock(),t2,t3;
    for(int i=0;i<1e9;i++)
        is_digit(c);
    t2=clock();
    for(int i=0;i<1e9;i++)
        isdigit(c);
    t3=clock();
    cout<<"is_digit:"<<(t2-t1)/CLOCKS_PER_SEC<<"\nisdigit:"<<(t3-t2)/CLOCKS_PER_SEC<<endl;

    return 0;
}

经过运行,is_digit()只用了1秒(1161ms),而isdigit()却用了4秒(3674ms),我知道isdigit是位实现的操作,isdigit()不应该比is_digit()快吗?


更新1

我使用带有默认选项的 MS VS2010,发布版本,如何使 isdigit() 比 VS 中的 is_digit() 更快?

update2

谢谢大家。 在 VS 中处于发布模式时,项目将针对速度默认值 (-O2).

进行优化

全部处于发布模式。

VS2010: is_digit:1182(毫秒) isdigit:3724(毫秒)

VS2013: is_digit:0(毫秒) isdigit:3806(毫秒)

带有 g++(4.7.1) 和 -O3 的代码块: is_digit:1275(毫秒) isdigit:1331(毫秒)

所以这是结论:

is_digit() 在 VS 中比 isdigit() 快,但在 g++ 中比 isdigit() 慢。

并且 g++ 中的 isdigit() 比 VS 中的 isdigit() 快。

所以 "VS sucks" 表现?

使用 -O3 查看此代码(适用于 g++)

#include<iostream>
#include<cctype>
#include<ctime>
#include <time.h>
#include <sys/time.h>
using namespace std;
static inline bool is_digit(char c)
{
    return c>='0'&&c<='9';
}
int main()
{
    char c='8';
    struct timeval tvSt, tvEn;
    time_t t1=clock(),t2,t3;
    gettimeofday(&tvSt, 0);
    for(int i=0;i<1e9;i++)
        is_digit(c);
    gettimeofday(&tvEn, 0);
    cout << "is_digit:" << (tvEn.tv_sec - tvSt.tv_sec)*1000000 + (tvEn.tv_usec - tvSt.tv_usec) << " us"<< endl;
    gettimeofday(&tvSt, 0);
    for(int i=0;i<1e9;i++)
        isdigit(c);
    gettimeofday(&tvEn, 0);
    cout << "isdigit:" << (tvEn.tv_sec - tvSt.tv_sec)*1000000 + (tvEn.tv_usec - tvSt.tv_usec) << " us"<< endl;

    return 0;
}

结果:

is_digit:1610771 us
isdigit:1055976 us

所以,C++ 实现胜过你。
通常,当您衡量性能时,以秒为单位并不是一个好主意。至少考虑微秒级别。

我不确定 VS。请找出微秒级时钟并进行测量。

PS。请参考 https://msdn.microsoft.com/en-us/library/19z1t1wy.aspx VS 优化

在 clang/llvm [我选择的编译器] 中,isdigitis_digit 将变成完全相同的代码,因为它针对特定的库调用进行了优化以翻译它进入 ((unsigned)(c-48) < 10u)

return c>='0' && c <='9'; 也通过优化转换为 c-48 > 10(作为编译器执行的通用 if x >= N && x <= M -> x-N > (M-N) 转换)。

因此,理论上,这两个循环应该变成相同的代码(至少对于具有针对 isdigit 的此类优化的编译器 - 无论 MSVC 是否这样做,我都不能说,因为源代码对一般人来说是不可用的 public)。我知道gcc有类似的优化库调用的代码,但是目前我的机器上没有gcc源码,懒得去查[以我的经验,会难一点无论如何,比 llvm 代码更容易阅读。

llvm 中的代码:

Value *LibCallSimplifier::optimizeIsDigit(CallInst *CI, IRBuilder<> &B) {
  Function *Callee = CI->getCalledFunction();
  FunctionType *FT = Callee->getFunctionType();
  // We require integer(i32)
  if (FT->getNumParams() != 1 || !FT->getReturnType()->isIntegerTy() ||
      !FT->getParamType(0)->isIntegerTy(32))
    return nullptr;

  // isdigit(c) -> (c-'0') <u 10
  Value *Op = CI->getArgOperand(0);
  Op = B.CreateSub(Op, B.getInt32('0'), "isdigittmp");
  Op = B.CreateICmpULT(Op, B.getInt32(10), "isdigit");
  return B.CreateZExt(Op, CI->getType());
}

对于那些不熟悉 LLVM 代码的人:它首先检查函数调用是否具有正确的参数数量和参数类型。如果失败,它 returns NULL 表示 "I can't optimise this"。否则,它会构建操作链来执行 if (c - '0' > 10) 使用无符号比较来处理 "negative" 值 [无符号是巨大的值]。

这样做会出错:

bool isdigit(int x)
{
   return image_contains_finger(imagefiles[x]); 
}

[但是用你自己的版本替换库函数通常会产生有趣的效果!]

您的函数 is_digit 可以通过以下方式更快地实现:

#define ISDIGIT(X) (((uint32_t)X - '0') < 10u)

保存一个比较的地方。我认为,这是 gcc 中的正常方法,但在 Microsoft Visual Studio 中我猜你有 isdigit() 的本地化版本(因此检查语言环境需要很长时间)。