C++ 向量<;双倍>;比双倍还快*:为什么?

C++ 向量<;双倍>;比双倍还快*:为什么?,c++,arrays,optimization,vector,ode,C++,Arrays,Optimization,Vector,Ode,这里有一个循环,我用std::vector和普通的double*尝试过 对于1000万个元素,向量版本在double*版本所需时间的80%左右持续运行;对于几乎任何N的值,vector都明显更快 纵观GCC STL源代码,我看不出std::vector所做的事情本质上比double*惯用法所做的事情更有趣(即,使用普通的旧new[],操作符[]取消对偏移量的引用)。我也这么说 你知道为什么矢量版本更快吗 Compiler: GCC 4.6.1 Example compile line: g++

这里有一个循环,我用
std::vector
和普通的
double*
尝试过

对于1000万个元素,向量版本在
double*
版本所需时间的80%左右持续运行;对于几乎任何
N
的值,vector都明显更快

纵观GCC STL源代码,我看不出
std::vector
所做的事情本质上比
double*
惯用法所做的事情更有趣(即,使用普通的旧
new[]
操作符[]
取消对偏移量的引用)。我也这么说

你知道为什么矢量版本更快吗

Compiler: GCC 4.6.1
Example compile line: g++ -Ofast -march=native -DNDEBUG \
                      -ftree-vectorizer-verbose=2 -o vector.bin \
                      vector.cpp -lrt
OS: CentOS 5
CPU: Opteron 8431
RAM: 128 GB
如果我使用ICPC11.1或在Xeon上运行,结果在质量上是相同的。此外,矢量化器转储表示只有
std::vector
的构造函数中的填充操作被矢量化

矢量版本:

#include <vector>
#include <iostream>
#include <boost/lexical_cast.hpp>
#include "util.h"
#include "rkck_params.h"

using namespace std;

int main( int argc, char* argv[] )
{
    const size_t N = boost::lexical_cast<size_t>( argv[ 1 ] );

    vector<double> y_old( N );
    vector<double> y_new( N );
    vector<double> y_err( N );
    vector<double> k0( N );
    vector<double> k1( N );
    vector<double> k2( N );
    vector<double> k3( N );
    vector<double> k4( N );
    vector<double> k5( N );

    const double h = 0.5;

    const timespec start = clock_tick();
    for ( size_t i = 0 ; i < N ; ++i )
    {
        y_new[ i ] =   y_old[ i ]
                     + h
                      *(
                           rkck::c[ 0 ]*k0[ i ]
                         + rkck::c[ 2 ]*k2[ i ]
                         + rkck::c[ 3 ]*k3[ i ]
                         + rkck::c[ 5 ]*k5[ i ]
                       );
        y_err[ i ] =  h
                     *(
                          rkck::cdiff[ 0 ]*k0[ i ]
                        + rkck::cdiff[ 2 ]*k2[ i ]
                        + rkck::cdiff[ 3 ]*k3[ i ]
                        + rkck::cdiff[ 4 ]*k4[ i ]
                        + rkck::cdiff[ 5 ]*k5[ i ]
                      );
    }
    const timespec stop = clock_tick();
    const double total_time = seconds( start, stop );

    // Output
    cout << "vector\t" << N << "\t" << total_time << endl;

    return 0;
}
rkck_参数h

#ifndef RKCK_PARAMS_H
#define RKCK_PARAMS_H

namespace rkck
{

// C.f. $c_i$ in Ch. 16.2 of NR in C++, 2nd ed.
const double c[ 6 ]
    = { 37.0/378.0,
        0.0,
        250.0/621.0,
        125.0/594,
        0.0,
        512.0/1771.0 };

// C.f. $( c_i - c_i^* )$ in Ch. 16.2 of NR in C++, 2nd ed.
const double cdiff[ 6 ]
    = { c[ 0 ] - 2825.0/27648.0,
        c[ 1 ] - 0.0,
        c[ 2 ] - 18575.0/48384.0,
        c[ 3 ] - 13525.0/55296.0,
        c[ 4 ] - 277.0/14336.0,
        c[ 5 ] - 1.0/4.0 };

}

#endif
#ifndef UTIL_H
#define UTIL_H

#include <time.h>
#include <utility>

inline timespec clock_tick()
{
    timespec tick;
    clock_gettime( CLOCK_REALTIME, &tick );
    return tick;
}

// \cite{www.guyrutenberg.com/2007/09/22/profiling-code-using-clock_gettime}
inline double seconds( const timespec& earlier, const timespec& later )
{
    double seconds_diff = -1.0;
    double nano_diff = -1.0;

    if ( later.tv_nsec < earlier.tv_nsec )
    {
        seconds_diff = later.tv_sec - earlier.tv_sec - 1;
        nano_diff = ( 1.0e9 + later.tv_nsec - earlier.tv_nsec )*1.0e-9;
    }
    else
    {
        seconds_diff = later.tv_sec - earlier.tv_sec;
        nano_diff = ( later.tv_nsec - earlier.tv_nsec )*1.0e-9;
    }

    return seconds_diff + nano_diff;
}

#endif
util.h

#ifndef RKCK_PARAMS_H
#define RKCK_PARAMS_H

namespace rkck
{

// C.f. $c_i$ in Ch. 16.2 of NR in C++, 2nd ed.
const double c[ 6 ]
    = { 37.0/378.0,
        0.0,
        250.0/621.0,
        125.0/594,
        0.0,
        512.0/1771.0 };

// C.f. $( c_i - c_i^* )$ in Ch. 16.2 of NR in C++, 2nd ed.
const double cdiff[ 6 ]
    = { c[ 0 ] - 2825.0/27648.0,
        c[ 1 ] - 0.0,
        c[ 2 ] - 18575.0/48384.0,
        c[ 3 ] - 13525.0/55296.0,
        c[ 4 ] - 277.0/14336.0,
        c[ 5 ] - 1.0/4.0 };

}

#endif
#ifndef UTIL_H
#define UTIL_H

#include <time.h>
#include <utility>

inline timespec clock_tick()
{
    timespec tick;
    clock_gettime( CLOCK_REALTIME, &tick );
    return tick;
}

// \cite{www.guyrutenberg.com/2007/09/22/profiling-code-using-clock_gettime}
inline double seconds( const timespec& earlier, const timespec& later )
{
    double seconds_diff = -1.0;
    double nano_diff = -1.0;

    if ( later.tv_nsec < earlier.tv_nsec )
    {
        seconds_diff = later.tv_sec - earlier.tv_sec - 1;
        nano_diff = ( 1.0e9 + later.tv_nsec - earlier.tv_nsec )*1.0e-9;
    }
    else
    {
        seconds_diff = later.tv_sec - earlier.tv_sec;
        nano_diff = ( later.tv_nsec - earlier.tv_nsec )*1.0e-9;
    }

    return seconds_diff + nano_diff;
}

#endif
\ifndef UTIL\u H
#定义UTIL_H
#包括
#包括
内联timespec时钟_tick()
{
timespec滴答声;
时钟获取时间(时钟实时和滴答声);
返回勾号;
}
//\cite{www.guyrutenberg.com/2007/09/22/profiling-code-using-clock_-gettime}
内联双秒(常数时间间隔和更早、常数时间间隔和更晚)
{
双秒_diff=-1.0;
双纳米差异=-1.0;
如果(later.tv\u nsec
在矢量版本中,数据被初始化为零。在
new
版本中,它未初始化,因此可能会执行不同的工作


你跑过多次了吗,以不同的顺序?

@Martinho:我非常同意,如果一个人的结果有任何意义的希望,真实的评测测试用例是至关重要的。可能重复@Martin:Is'这不是相反的问题吗?@MSalters:我假设人们足够聪明,能够颠倒问题以得到相同的答案。听起来不错:我在我的系统上复制OPs结果(vector快了50%),并将所有
new[]
s更改为
new[]()
s将差异降低到1-5%。乘法指令定时数据是否依赖?我认为随着时间的推移,它变得越来越快,也越来越一致了。@Mark Ransom不可能用零乘以随机数会比用随机数快得多。@Hector,你从来没有说过你用什么值来表示
N
,但如果它足够小,可以放入缓存,“启动”通过初始化缓存肯定会使它更快。这比我对乘法指令的推测更有意义。@Nemo,我想你已经了解了一些事情-如果内存是分配的,但没有物理映射,映射它将需要一些时间。我开始后悔,每次我给了“测量它”的建议,而测量似乎很难!