C++ C++;STD::Vector使用模板化函数运行时间异常长

C++ C++;STD::Vector使用模板化函数运行时间异常长,c++,templates,stl,performance,C++,Templates,Stl,Performance,以下函数使用两个T值进行操作。一个是4字节(与DWORD相同)。另一个是64字节。缓冲区用于将对象存储在其缓存中,并在以后检索它们 使用64字节结构时,函数中的总时间急剧增加。向量中的寻道时间、memcpy甚至函数“自我”部分的时间都显著增加 store函数与下面的代码几乎相反,并且似乎没有相同的不对称计时 你知道为什么吗 template <class T> void Buffer::retrieve ( T& Value ) { int nTypeSize

以下函数使用两个T值进行操作。一个是4字节(与DWORD相同)。另一个是64字节。缓冲区用于将对象存储在其缓存中,并在以后检索它们

使用64字节结构时,函数中的总时间急剧增加。向量中的寻道时间、memcpy甚至函数“自我”部分的时间都显著增加

store函数与下面的代码几乎相反,并且似乎没有相同的不对称计时

你知道为什么吗

template <class T>
void Buffer::retrieve ( T& Value )
   {
   int nTypeSize  = sizeof ( T );
   int nDWORDSize = sizeof ( DWORD );

   /*
    * Number of DWORDs needed to store this value.
    */

   int nDWORDCount = ( nTypeSize + 3 ) / 4;

   if ( m_nReadPosition + nDWORDCount >= m_nSize )
      return;

   memcpy ( &Value, &m_Cache[m_nReadPosition], nTypeSize );  //m_Cache is a DWORD vector.
   m_nReadPosition += nDWORDCount;
   }
模板
无效缓冲区::检索(T和值)
{
int nTypeSize=sizeof(T);
int nDWORDSize=sizeof(DWORD);
/*
*存储此值所需的DWORD数。
*/
int nDWORDCount=(nTypeSize+3)/4;
如果(m_nReposition+nDWORDCount>=m_nSize)
返回;
memcpy(&Value,&m_Cache[m_nredposition],nTypeSize);//m_Cache是一个DWORD向量。
m_nredposition+=nDWORDCount;
}

memcpy时间的增加可能只是因为复制了更多的字节,因为memcpy时间会随着被复制的内存量而(天真地)扩展。这不一定是线性扩展,因为memcpy的一些实现会通过一次复制32或64位来优化

std::vector中的查找不应随对象大小缩放,因为m_nReposition或m_缓存都不依赖于t

尽管4字节结构可以存储在32位处理器上的寄存器中,但对于任何操作T的代码,速度都会有所减慢,而对于编译器来说,任何更大的操作都会更加复杂。这可能会增加一些开销


总时间增加了多少?如果它是16的倍数,我会把它完全归结为T大小的变化。

我会怀疑分析器将时间归因于从这里调用的函数:
std::vector
的访问器是内联的,
memcpy()
可能是编译器的内在特性,这意味着在优化的发布版本中(您正在为发布构建计时,对吗?)他们的大部分工作都将归功于调用函数

因此,考虑到我会运行一些受控实验来确定速度的降低。例如,这里CPU时间过长的最可能罪魁祸首是
memcpy()
,因此请尝试暂时将其从等式中去掉:

volatile DWORD g_dummy;
void Buffer::retrieve ( T& Value )
   {
   /* ... */
   // memcpy ( &Value, &m_Cache[m_nReadPosition], nTypeSize );  
   g_dummy += m_Cache[m_nReadPosition]; // force the compiler to perform the vector lookup
   m_nReadPosition += nDWORDCount;
   }

如果你有一个
std::vector
,为什么要使用
memcpy
?我建议使用
std::copy
std::vector
自己的方法。这在很大程度上是一种风格上的改变,但它确实保证了构造函数被调用

至于事情进展缓慢的原因,我不确定:

  • 向量中的寻道时间
这很奇怪,因为向量指向连续内存,而内存的查找时间是恒定的。也就是说,如果我指向向量中的第一项,则从第二项移动到最后一项所需的时间与从第二项移动到最后一项所需的时间相同(通过
+
+=
std::advance

如果您使用的是
std::list
,则会显示查找时间。但它不应该出现在向量中。而且它肯定不应该出现在对向量的原始内存进行操作的
memcpy中

  • 记忆
您复制的数据是原来的16倍(4字节,而不是64字节)

  • 甚至在功能的“自我”部分,时间也会急剧增加
这也很奇怪,因为

int nTypeSize  = sizeof ( T );
int nDWORDSize = sizeof ( DWORD );
int nDWORDCount = ( nTypeSize + 3 ) / 4;
都是编译时常量,并且应该由编译器为每个类型T预先计算

if ( m_nReadPosition + nDWORDCount >= m_nSize )

Buffer::retrieve
中唯一在运行时实际执行的行(除了
memcpy
)。除非此增加仅仅是由于重复计算
memcpy
(一次在标题“
memcpy
”下,一次在标题“
Buffer::retrieve
”下)



std::vector中需要注意的是零碎的内存分配和不必要的拷贝。但是,在示例代码中您没有做任何操作。

首先,您使用的是什么系统?其次,您的意思是什么?我希望64字节比4字节花费更长的时间。第三,您的意思是什么“在向量中寻找时间”和函数的“自我”部分?第四,您如何确定时间?除了其他人要求的一些进一步解释外,您应该注意,发布的解决方案是脆弱的,它不适用于依赖于用户提供的副本构造函数(资源处理对象)的T类型什么,是不是慢了16倍?给我们一个数字!如果比这稍微多一些,我不会感到惊讶,因为在memcpy()的情况下执行的一些编译器优化在复制64字节时可能不会执行。David-1)32位Vista 2)长度超过16倍,但向量中的数量级寻道时间由[]中的时间表示运算符。除了memcpy行之外,Self函数就是一切。4)一个外部分析器正在确定时间,不知道是哪一个,也不知道那个同事今天出去了。Dribeas-很好的观点。当前的使用都不需要这样的构造函数,但最好记住。Andreas-是的,比16x慢一点。我想它不会出现然而,将memcpy复制为16个4字节副本。而且大部分额外时间都在memcpy之外……通过一次复制32或64位进行优化。或者更好的是,在某些情况下,实现将使用处理器中的向量指令。仅当数据正确对齐时。通常是在汇编中看到的在memcpy()的调用点上,4个字节的r代码是一条mov指令。当调用被调用时,编译器会对其进行优化
m_nReadPosition += nDWORDCount;