试图减少几乎是整数但不是整数的类的速度开销 我实现了一个C++类,它的行为与标准 int >代码>非常类似。不同之处在于它有一个额外的“ε”概念,它表示一些非常小的值,远小于1,但大于0。一种方法是将其视为一个非常宽的不动点数,其中包含32个MSB(整数部分)、32个LSB(ε部分)和大量的零。(注:此类数字与普通固定点数字之间的一大区别是有两个符号,而不是一个:“值”和“ε”可以相互独立地为负,而对于固定点数字,整个数字有一个符号。)

试图减少几乎是整数但不是整数的类的速度开销 我实现了一个C++类,它的行为与标准 int >代码>非常类似。不同之处在于它有一个额外的“ε”概念,它表示一些非常小的值,远小于1,但大于0。一种方法是将其视为一个非常宽的不动点数,其中包含32个MSB(整数部分)、32个LSB(ε部分)和大量的零。(注:此类数字与普通固定点数字之间的一大区别是有两个符号,而不是一个:“值”和“ε”可以相互独立地为负,而对于固定点数字,整个数字有一个符号。),c++,optimization,numbers,operator-overloading,C++,Optimization,Numbers,Operator Overloading,以下课程有效,但在整个课程中引入了~2倍的速度惩罚。(该程序包含与该类无关的代码,因此该类的实际速度损失可能远大于2x。)我无法粘贴使用该类的代码,但我可以说: +、-、+=、和=是唯一大量使用的运算符。使用setEpsilon()和getInt()是极其罕见的 *也很少见,甚至根本不需要考虑ε值。 下面是课堂: #include <limits> struct int32Uepsilon { typedef int32Uepsilon Self; int32Uepsilon (

以下课程有效,但在整个课程中引入了~2倍的速度惩罚。(该程序包含与该类无关的代码,因此该类的实际速度损失可能远大于2x。)我无法粘贴使用该类的代码,但我可以说:

+、-、+=、
=
是唯一大量使用的运算符。使用
setEpsilon()
getInt()
是极其罕见的<代码> *<代码>也很少见,甚至根本不需要考虑ε值。

下面是课堂:

#include <limits>

struct int32Uepsilon {
typedef int32Uepsilon Self;

int32Uepsilon () { _value = 0;
                   _eps   = 0; }
int32Uepsilon (const int &i) { _value = i;
                               _eps   = 0; }
void setEpsilon() { _eps = 1; }
Self operator+(const Self &rhs) const { Self result = *this;
                                      result._value += rhs._value;
                                      result._eps   += rhs._eps;
                                      return result; }
Self operator-(const Self &rhs) const { Self result = *this;
                                      result._value -= rhs._value;
                                      result._eps   -= rhs._eps;
                                      return result; }
Self operator-(               ) const { Self result = *this;
                                      result._value = -result._value;
                                      result._eps   = -result._eps;
                                      return result; }
Self operator*(const Self &rhs) const { return this->getInt() * rhs.getInt(); } // XXX: discards epsilon

bool operator<(const Self &rhs) const { return (_value < rhs._value) ||
                                             (_value == rhs._value && _eps < rhs._eps); }
bool operator>(const Self &rhs) const { return (_value > rhs._value) ||
                                             (_value == rhs._value && _eps > rhs._eps); }
bool operator>=(const Self &rhs) const { return (_value >= rhs._value) ||
                                             (_value == rhs._value && _eps >= rhs._eps); }

Self &operator+=(const Self &rhs) { this->_value += rhs._value;
                                  this->_eps   += rhs._eps;
                                  return *this; }
Self &operator-=(const Self &rhs) { this->_value -= rhs._value;
                                  this->_eps   -= rhs._eps;
                                  return *this; }
int getInt() const { return(_value); }

private:
  int _value;
  int _eps;
};

namespace std {
template<>
struct numeric_limits<int32Uepsilon> {
  static const bool is_signed  = true;
  static int max() { return 2147483647; }
}
};
#包括
结构int32Uepsilon{
typedef int32Uepsilon Self;
int32Uepsilon(){u值=0;
_eps=0;}
int32Uepsilon(const int&i){u值=i;
_eps=0;}
void setEpsilon(){u eps=1;}
Self运算符+(const Self&rhs)const{Self result=*this;
结果。_值+=rhs。_值;
结果。_eps+=rhs。_eps;
返回结果;}
自运算符-(const Self&rhs)const{Self result=*this;
结果。_值-=rhs。_值;
结果。_eps-=rhs。_eps;
返回结果;}
自运算符-()const{Self result=*this;
结果。_值=-结果。_值;
结果。_eps=-结果。_eps;
返回结果;}
Self操作符*(const Self&rhs)const{返回this->getInt()*rhs.getInt();}//XXX:discards epsilon
布尔运算符(const Self&rhs)const{return(_值>rhs._值)||
(_值==rhs._值&&u eps>rhs._eps);}
布尔运算符>=(常量Self&rhs)常量{return(_值>=rhs._值)||
(_值==rhs._值&&u eps>=rhs._eps);)
Self&operator+=(const Self&rhs){this->_值+=rhs._值;
此->_eps+=rhs._eps;
返回*this;}
Self&operator-=(const Self&rhs){this->_值-=rhs._值;
此->_eps-=右侧。_eps;
返回*this;}
int getInt()常量{return(_value);}
私人:
int_值;
int_eps;
};
名称空间标准{
模板
结构数值极限{
静态常数布尔值为_signed=true;
静态int max(){return 2147483647;}
}
};
上面的代码可以工作,但速度相当慢。有人对如何提高绩效有什么想法吗?我可以给出一些可能有用的提示/细节:

  • 32位绝对不足以同时保存_值和_eps。在实践中,高达24~28位的 _使用值,最多使用20位的_eps
  • 我无法衡量使用
    int32\u t
    int64\u t
    之间的显著性能差异, 因此,内存开销本身可能不是问题所在
  • 在eps上饱和加法/减法会很酷,但实际上并不必要
  • 注意_value和_eps的符号不一定相同!这打破了我的第一次尝试 加快这个班的速度
  • 内联汇编是没有问题的,只要它在运行Linux的核心i7系统上与GCC一起工作
2x速度惩罚似乎并不合理,因为所有操作都要进行两次

您可以使用MMX/SSE2指令将value和epsilon打包到两个寄存器中,并仅并行执行一次这两个操作。或者,在64位体系结构上,您可以将两个值打包到一个int64中,如:
[32位值][12零][20位eps]
。比较将通过一次操作自动进行,加法和减法需要将eps的结转掩盖为填充零。使用MMX进行加法和减法(然后自动屏蔽)以及使用普通整数比较进行比较没有任何障碍


顺便说一句,您的操作员-=似乎有问题:在
this->\u eps-=rhs.\u eps
中,
this->eps
可能会变为负数。难道你不应该同时调整每股收益并降低其值吗?eps的溢出行为是什么?它是否会转化为价值?

一件值得尝试的事情是,根据
操作符+=
定义例如
操作符+

Self operator+(const Self &rhs) const { return Self(*this) += rhs; }
这有助于实现,从而消除了按值返回所需的复制构造函数

而且,它减少了代码维护

我的(部分)解决方案使用的是one整数运算,而不是Oli Charlesworth在评论中指出的解决方案中的two

您可以这样做:使用
int64\u t
存储
\u eps
\u值
。在我下面的示例中,
\u值
位0到位31
表示,
\u eps
位32到位63
表示

struct int32Uepsilon 
{

   typedef int32Uepsilon Self;

   int64_t value;

   int32Uepsilon () { value = 0 }
   int32Uepsilon (const int i) {  value = i; }
   void setEpsilon() 
   {  
      //equivent to _eps = 1
      value = ((int64_t)1 << 32) + (value & 0xFFFFFFFF); 
   }
   Self operator+(const Self &rhs) const 
   { 
     Self result = *this;
    //this adds lower 32 bits to lower 32 bits, upper 32 bits to upper 32 bits!
     result.value += rhs.value; 
     return result; 
   }
   //....
   int getValue() { return value & 0xFFFFFFFF; }
   int getEpsilon() { return value >> 32; }
};
输出:

2, 4    
65, 7897   
67, 7901
正如所料

Ideone演示:


您可以不使用
x+y
,而使用
2, 4    
65, 7897   
67, 7901