почему так валяра?

17

Прошу прощения за мой вопрос о валарме. Я пытаюсь использовать его, поскольку он очень похож на матрицу при работе с вектором & amp; матрицы. Сначала я проверил некоторые проверки производительности и обнаружил, что valarray не может достичь производительности, объявленной как в языке программирования на языке программирования c ++, посредством strustrup.

В тестовой программе фактически было умножено 5M удвоений. Я думал, что c = a * b по крайней мере будет сопоставим с умножением элемента двойного типа for цикла, но я совершенно неправ. Пробовал несколько компьютеров и vc6.0 и vs2008.

Кстати, я тестировал на Matlab, используя следующий код:

len=5*1024*1024;
a=rand(len,1);b=rand(len,1);c=zeros(len,1);
tic;c=a.*b;toc;

, а результат - 46 мс. Это время не является высокой точностью, работает только как ссылка.

Код:

#include <iostream>
#include <valarray>
#include <iostream>
#include "windows.h"

using namespace std ;
SYSTEMTIME stime;
LARGE_INTEGER sys_freq;

double gettime_hp();

int main()
{
    enum { N = 5*1024*1024 };
    valarray<double> a(N), b(N), c(N) ;
    QueryPerformanceFrequency(&sys_freq);   
    int i,j;
    for(  j=0 ; j<8 ; ++j )
    {
        for(  i=0 ; i<N ; ++i ) 
        {
            a[i]=rand();
            b[i]=rand();
        }

        double* a1 = &a[0], *b1 = &b[0], *c1 = &c[0] ;
        double dtime=gettime_hp();
        for(  i=0 ; i<N ; ++i ) c1[i] = a1[i] * b1[i] ;
        dtime=gettime_hp()-dtime;
        cout << "double operator* " << dtime << " ms\n" ;

        dtime=gettime_hp();
        c = a*b ;
        dtime=gettime_hp()-dtime;
        cout << "valarray operator* " << dtime << " ms\n" ;

        dtime=gettime_hp();
        for(  i=0 ; i<N ; ++i ) c[i] = a[i] * b[i] ;
        dtime=gettime_hp()-dtime;
        cout << "valarray[i] operator* " << dtime<< " ms\n" ;

        cout << "------------------------------------------------------\n" ;
    }
}

double gettime_hp()
{
    LARGE_INTEGER tick;
    extern LARGE_INTEGER sys_freq;
    QueryPerformanceCounter(&tick);
    return (double)tick.QuadPart*1000.0/sys_freq.QuadPart;
}

Результаты работы: (режим выпуска с максимальной оптимизацией скорости)

double operator* 52.3019 ms
valarray operator* 128.338 ms
valarray[i] operator* 43.1801 ms
------------------------------------------------------
double operator* 43.4036 ms
valarray operator* 145.533 ms
valarray[i] operator* 44.9121 ms
------------------------------------------------------
double operator* 43.2619 ms
valarray operator* 158.681 ms
valarray[i] operator* 43.4871 ms
------------------------------------------------------
double operator* 42.7317 ms
valarray operator* 173.164 ms
valarray[i] operator* 80.1004 ms
------------------------------------------------------
double operator* 43.2236 ms
valarray operator* 158.004 ms
valarray[i] operator* 44.3813 ms
------------------------------------------------------

режим отладки с той же оптимизацией:

double operator* 41.8123 ms
valarray operator* 201.484 ms
valarray[i] operator* 41.5452 ms
------------------------------------------------------
double operator* 40.2238 ms
valarray operator* 215.351 ms
valarray[i] operator* 40.2076 ms
------------------------------------------------------
double operator* 40.5859 ms
valarray operator* 232.007 ms
valarray[i] operator* 40.8803 ms
------------------------------------------------------
double operator* 40.9734 ms
valarray operator* 234.325 ms
valarray[i] operator* 40.9711 ms
------------------------------------------------------
double operator* 41.1977 ms
valarray operator* 234.409 ms
valarray[i] operator* 41.1429 ms
------------------------------------------------------
double operator* 39.7754 ms
valarray operator* 234.26 ms
valarray[i] operator* 39.6338 ms
------------------------------------------------------
    
задан shangping 27.07.2011 в 22:27
источник

7 ответов

11

Я подозреваю, что причина c = a*b намного медленнее, чем выполнение операций по элементу за раз, что

template<class T> valarray<T> operator*
    (const valarray<T>&, const valarray<T>&);
Оператор

должен выделить память для ввода результата, затем возвращает это значение.

Даже если для выполнения копии используется «swaptimization», эта функция все еще имеет накладные расходы

  • выделение нового блока для итогового valarray
  • инициализация нового valarray (возможно, это может быть оптимизировано)
  • вывод результатов в новый valarray
  • пейджинг в памяти для нового valarray , поскольку он инициализирован или задан с помощью значений результата
  • освобождение старого valarray , которое заменяется результатом
ответ дан Michael Burr 27.07.2011 в 22:46
источник
22

Я просто попробовал его в системе x86-64 для Linux (процессор Sandy Bridge):

gcc 4.5.0:

double operator* 9.64185 ms
valarray operator* 9.36987 ms
valarray[i] operator* 9.35815 ms

Intel ICC 12.0.2:

double operator* 7.76757 ms
valarray operator* 9.60208 ms
valarray[i] operator* 7.51409 ms

В обоих случаях я просто использовал -O3 и никаких других связанных с оптимизацией флагов.

Похоже, что компилятор MS C ++ и / или реализация valarray сосут.

Вот код OP, измененный для Linux:

#include <iostream>
#include <valarray>
#include <iostream>
#include <ctime>

using namespace std ;

double gettime_hp();

int main()
{
    enum { N = 5*1024*1024 };
    valarray<double> a(N), b(N), c(N) ;
    int i,j;
    for(  j=0 ; j<8 ; ++j )
    {
        for(  i=0 ; i<N ; ++i )
        {
            a[i]=rand();
            b[i]=rand();
        }

        double* a1 = &a[0], *b1 = &b[0], *c1 = &c[0] ;
        double dtime=gettime_hp();
        for(  i=0 ; i<N ; ++i ) c1[i] = a1[i] * b1[i] ;
        dtime=gettime_hp()-dtime;
        cout << "double operator* " << dtime << " ms\n" ;

        dtime=gettime_hp();
        c = a*b ;
        dtime=gettime_hp()-dtime;
        cout << "valarray operator* " << dtime << " ms\n" ;

        dtime=gettime_hp();
        for(  i=0 ; i<N ; ++i ) c[i] = a[i] * b[i] ;
        dtime=gettime_hp()-dtime;
        cout << "valarray[i] operator* " << dtime<< " ms\n" ;

        cout << "------------------------------------------------------\n" ;
    }
}

double gettime_hp()
{
    struct timespec timestamp;

    clock_gettime(CLOCK_REALTIME, &timestamp);
    return timestamp.tv_sec * 1000.0 + timestamp.tv_nsec * 1.0e-6;
}
    
ответ дан Paul R 27.07.2011 в 23:24
источник
3

Я, наконец, получил это, используя задержку оценки. Код может быть уродливым, так как я только начинаю изучать эти расширенные концепции c ++. Поправьте меня, если у вас есть идея лучше. Большое спасибо за вашу помощь. Вот код:

#include <iostream>
#include <valarray>
#include <iostream>
#include "windows.h"

using namespace std ;
SYSTEMTIME stime;
LARGE_INTEGER sys_freq;

double gettime_hp();
//to improve the c=a*b (it will generate a temp first, assigned to c and delete the temp
//which causes the program really slow
//the solution is the expression template and let the compiler to decide when all the expression is known
//delayed evaluation
//typedef valarray<double> Vector;
class Vector;
class VecMul
{
public:
    const Vector& va;
    const Vector& vb;
    //Vector& vc;
    VecMul(const Vector& v1,const Vector& v2):va(v1),vb(v2){}
    operator Vector();
};

class Vector:public valarray<double>
{
    valarray<double> *p;
public:
    explicit Vector(int n)
    {
        p=new valarray<double>(n);
    }
    Vector& operator=(const VecMul &m)
    {
        for(int i=0;i<m.va.size();i++) (*p)[i]=(m.va)[i]*(m.vb)[i];//ambiguous
        return *this;
    }
    double& operator[](int i) const {return (*p)[i];}  //const vector_type[i]
    int size()const {return (*p).size();}
};



inline VecMul operator*(const Vector& v1,const Vector& v2)
{
    return VecMul(v1,v2);
}


int main()
{
    enum { N = 5*1024*1024 };
    Vector a(N), b(N), c(N) ;
    QueryPerformanceFrequency(&sys_freq);   
    int i,j;
    for(  j=0 ; j<8 ; ++j )
    {
        for(  i=0 ; i<N ; ++i ) 
        {
            a[i]=rand();
            b[i]=rand();
        }

        double* a1 = &a[0], *b1 = &b[0], *c1 = &c[0] ;
        double dtime=gettime_hp();
        for(  i=0 ; i<N ; ++i ) c1[i] = a1[i] * b1[i] ;
        dtime=gettime_hp()-dtime;
        cout << "double operator* " << dtime << " ms\n" ;

        dtime=gettime_hp();
        c = a*b ;
        dtime=gettime_hp()-dtime;
        cout << "valarray operator* " << dtime << " ms\n" ;

        dtime=gettime_hp();
        for(  i=0 ; i<N ; ++i ) c[i] = a[i] * b[i] ;
        dtime=gettime_hp()-dtime;
        cout << "valarray[i] operator* " << dtime<< " ms\n" ;

        cout << "------------------------------------------------------\n" ;
    }
}

double gettime_hp()
{
    LARGE_INTEGER tick;
    extern LARGE_INTEGER sys_freq;
    QueryPerformanceCounter(&tick);
    return (double)tick.QuadPart*1000.0/sys_freq.QuadPart;
}

Результат работы в Visual Studio:

double operator* 41.2031 ms
valarray operator* 43.8407 ms
valarray[i] operator* 42.49 ms
    
ответ дан shangping 28.07.2011 в 08:18
источник
3

Весь смысл valarray - быть быстрым на векторных машинах, которые x86-машины просто не являются. Хорошая реализация на машине без использования компонентов должна соответствовать производительности, которую вы получаете с чем-то вроде for (i=0; i < N; ++i) c1[i] = a1[i] * b1[i];

и плохой, конечно, не будет. Если в аппаратном обеспечении не будет чего-то ускорить параллельную обработку, это будет очень близко к лучшему, что вы можете сделать.

    
ответ дан Leo Bellantoni 06.09.2011 в 23:12
источник
1

Я компилирую в выпуске x64, VS 2010. Я немного изменил код:

    double* a1 = &a[0], *b1 = &b[0], *c1 = &c[0] ;
    double dtime=gettime_hp();
    for(  i=0 ; i<N ; ++i ) a1[i] *= b1[i] ;
    dtime=gettime_hp()-dtime;
    cout << "double operator* " << dtime << " ms\n" ;

    dtime=gettime_hp();
    a *= b;
    dtime=gettime_hp()-dtime;
    cout << "valarray operator* " << dtime << " ms\n" ;

    dtime=gettime_hp();
    for(  i=0 ; i<N ; ++i ) a[i] *= b[i] ;
    dtime=gettime_hp()-dtime;
    cout << "valarray[i] operator* " << dtime<< " ms\n" ;

    cout << "------------------------------------------------------\n" ;

Здесь вы можете видеть, что я использовал * = вместо c = a * b . В более современных математических библиотеках используются очень сложные механизмы шаблонов выражений, которые устраняют эту проблему. В этом случае на самом деле я получил очень немного более быстрые результаты от valarray, хотя это, вероятно, только потому, что содержимое уже было в кеше. Накладные расходы, которые вы видите, - это просто избыточные временные ряды и ничего не присущие valarray, в частности - вы увидите то же поведение с чем-то вроде std::string .

    
ответ дан Puppy 27.07.2011 в 23:32
источник
0

hmm..I испытал блиц и то же самое, что и valarray .. и больше blitz ++ [] operatpr очень медленный

 #include <blitz/array.h>  
    #include <iostream>
    #ifdef WIN32
    #include "windows.h"
    LARGE_INTEGER sys_freq;
    #endif
    #ifdef LINUX
    <ctime>
    #endif
        using namespace std ;
    SYSTEMTIME stime;


    __forceinline double gettime_hp();
    double gettime_hp()
    {
    #ifdef WIN32
        LARGE_INTEGER tick;
        extern LARGE_INTEGER sys_freq;
        QueryPerformanceCounter(&tick);
        return (double)tick.QuadPart*1000.0/sys_freq.QuadPart;
    #endif
    #ifdef LINUX
        struct timespec timestamp;

        clock_gettime(CLOCK_REALTIME, &timestamp);
        return timestamp.tv_sec * 1000.0 + timestamp.tv_nsec * 1.0e-6;
    #endif
    }
    BZ_USING_NAMESPACE(blitz)

    int main()
    {
        int N = 5*1024*1024 ;

        // Create three-dimensional arrays of double
        Array<double,1> a(N), b(N),c(N);


        int i,j;
    #ifdef WIN32
        QueryPerformanceFrequency(&sys_freq);   
    #endif
        for(  j=0 ; j<8 ; ++j )
        {
            for(  i=0 ; i<N ; ++i ) 
            {
                a[i]=rand();
                b[i]=rand();
            }

            double* a1 = a.data() , *b1 = b.data(), *c1 = c.data() ;
            double dtime=gettime_hp();
            for(  i=0 ; i<N ; ++i ) c1[i] = a1[i] * b1[i] ;
            dtime=gettime_hp()-dtime;
            cout << "double operator* " << dtime << " ms\n" ;

            dtime=gettime_hp();
            c = a*b ;
            dtime=gettime_hp()-dtime;
            cout << "blitz operator* " << dtime << " ms\n" ;

            dtime=gettime_hp();
            for(  i=0 ; i<N ; ++i ) c[i] = a[i] * b[i] ;
            dtime=gettime_hp()-dtime;
            cout << "blitz[i] operator* " << dtime<< " ms\n" ;

            cout << "------------------------------------------------------\n" ;
        }
    }
    
ответ дан user2778496 14.09.2013 в 06:48
источник
-1

Я думаю, что ответ Майкла Берра прав. Возможно, вы можете создать виртуальный тип как тип возвращаемого значения оператора + и перезагрузить другой оператор = для этого виртуального типа, например operator = (virtual type & amp; v) {& amp; valarray = & amp; v; v = NULL; }(грубо говоря). Конечно, трудно реализовать идею о валарме. Но когда вы создаете новый класс, вы можете попробовать эту идею. И тогда эффективность оператора + почти такая же, как оператор + =

    
ответ дан Lin Xuelei 06.05.2015 в 07:45
источник