int与unsigned char

时间:2010-08-02 06:34:57

标签: c++ c optimization

假设我们有一个运行100次的循环。使用unsigned char代替int作为其计数器有何不同?还使用i += 1U代替i++?或者编译器会处理这个问题吗?

9 个答案:

答案 0 :(得分:7)

简单来说,int vs unsigned char为我提供相同的代码:

    for ( unsigned char i = 0; i < 100; ++i ) {
01081001  mov         esi,64h  
01081006  jmp         main+10h (1081010h)  
01081008  lea         esp,[esp]  
0108100F  nop  
        std::cout << 1 << std::endl;
01081010  mov         eax,dword ptr [__imp_std::endl (108204Ch)]  
01081015  mov         ecx,dword ptr [__imp_std::cout (1082050h)]  
0108101B  push        eax  
0108101C  push        1  
0108101E  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (1082044h)]  
01081024  mov         ecx,eax  
01081026  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (1082048h)]  
0108102C  dec         esi  
0108102D  jne         main+10h (1081010h)  
    }
    return 0; 

您应该对代码进行分析,然后优化慢速部分。 不要以premature optimization开头。

答案 1 :(得分:3)

我建议使用++ i而不是i + = 1或i ++

答案 2 :(得分:3)

使用i += 1U将没有任何区别。编译器会将其视为与i++完全相同。 intunsigned char的情况比较复杂。

最佳答案根本不用担心它,除非a)你的代码必须运行得更快并且b)你已经将循环识别为主要瓶颈。记住Knuth定律:过早优化是万恶之源。

下一个最佳答案是编译器可能会优化任何差异。即使它不会,100次重复是花生。循环索引操作完全无关紧要。

但是,为了“完全披露”,你的int / uchar问题的答案是,几乎可以肯定没有性能差异。几乎。许多相关因素未被C标准指定,因此理论上可能存在一个C环境,其中uchar会更快。

标准将char定义为最小的可寻址内存单元。这可能(几乎总是)将是8位的一个字节。 int将至少与char一样大(或者char显然不能是内存的最小可寻址单位,并且它必须能够保存-32,767到+32,767之间的任何值。(它不是不是需要保留-32,768,尽管几乎所有都这样做。)这意味着int必须至少为16位。实际上,int通常是一个机器字。这样做的好处是速度快。在你的机器上而我的,几乎肯定是32或64位。

现在,假设我们在一台8位字的机器上。假设16位整数没有特殊的硬件支持。 (这可能与也可能不对应于任何实际的机器。)char将是8位且int可能是16。对int的操作可能很昂贵,因此char可能更快。

假设我们有32位字和32位整数。假设您的代码包含四个(8位)char变量。进一步假设您的编译器在物理上尽可能多地打包局部变量,速度会被诅咒。如果其中一个字符是您的循环计数器,它可能与其他变量包装在一个单词中。对该字符的每个操作都可能是昂贵的,因为它必须重新包装。 (这假定编译器相当笨。)然而,int计数器无法与任何其他变量打包在一起。在这种情况下,int可能运行得稍快。

请记住,这两个例子都是人为的。我不知道任何真正的系统可以使用这些方式,但系统可以。 (注意:如果我提出的案例与规格不符,请告诉我,以便我能纠正。)

那么,在现实世界中,如何决定使用哪种类型?来自C Language FAQ

  

如果您可能需要较大的值(大于32,767或小于-32,767),请使用long。否则,如果空间非常重要(即,如果有大型阵列或许多结构),请使用short。否则,使用int。如果明确定义的溢出特性很重要而负值不重要,或者如果要在操作位或字节时避免出现符号扩展问题,请使用相应的无符号类型之一。 (但要注意在表达式中混合有符号和无符号值时;请参阅问题3.19。)

     

虽然字符类型(特别是unsigned char)可以用作“小”整数,但这样做有时会比它的价值更麻烦。编译器必须发出额外的代码以在char和int之间进行转换(使可执行文件更大),并且意外的符号扩展可能很麻烦。 (使用unsigned char可以提供帮助;有关相关问题,请参阅问题12.1。)

该页面的其余部分,以及整个常见问题解答,非常值得一读。

在这种情况下?使用int并且不用担心。

答案 3 :(得分:2)

这通常没有任何区别。如果你真的在乎,请尝试并测量,但首先要确保这是一个瓶颈。

答案 4 :(得分:2)

说真的,将这些微优化留给编译器。即使的差异,只有你每秒钟进行数万次才会变得明显。对于绝大多数情况来说,一点都不重要,因为大多数程序花费99.9%的时间等待一些事情发生。

如果您专注于算法选择等宏观内容,您也更有可能获得更高的投资回报。

答案 5 :(得分:1)

据我所知,性能数据从编译器变为编译器。除此之外,性能可能因操作系统而异。

更好的方法是编写一个小程序来衡量您感兴趣的操作的性能。

Programming Pearls 涵盖了有关原始操作性能的大量细节。它还展示了如何编写一些程序来衡量这些操作的性能。

答案 6 :(得分:1)

如果您关心像这样的微优化,您必须全心全意地了解您的编译器。尝试两种方式并比较生成的程序集。 一个好的经验法则是根据其语义选择变量类型(char用于ASCII字符,int用于整数,等等......)而不是其大小(除了对齐)你的结构)。

但有两件小事:

  • 在32位架构上,处理器注册表的大小为32位,因此使用访问char(8位)需要两次操作,而访问int只需要一次。

  • 如果您不打算在增量之前使用该值,请选择预增量到后增量。这在C ++中尤其重要,其中后增量可能会导致一些不需要的构造函数调用。

答案 7 :(得分:1)

由于差异(最多)是每个增量的一个时钟周期的一小部分,因此在开始接近人类感知的阈值之前,你需要在整个循环中运行1,000,000;假设运行循环1,000,000次,然后立即警告用户已完成循环。

假设循环任何其他;或者,单个通知是任何形式的IO(根据定义它),你必须运行(100万次迭代)外循环另外1000万次!在您的用户注意之前。

认真!你担心错误的事情。

答案 8 :(得分:0)

大多数编译器会识别出您正在将该变量用于循环,因此会将其保存在寄存器中。

在i386 CPU上,它可以使用8位寄存器,因为它们可以直接访问而不会降低性能(16位值只能通过“16位覆盖操作码”访问,导致代码段中再多一个字节)

然而,8位寄存器和32位寄存器一样快,所以使用8位寄存器的唯一好处是还有一个用于另一个8位变量(只有一个,因为更高的16位不能直接访问) )。

总结一下:让编译器进行优化 - 如果可能的话它会这样做,但在这种情况下它也几乎没有差别。

其他CPU(PowerPC,ARM等)还有其他行为。