Lasersköld

2018-11-21

#teknik

Prestandatest på morgonen (C++)

Jag tenderar att fundera vilken typ av loopar som man ska använda, och om optimeringar gör någon nytta. Här har vi några exempel på hur loopar, och olika optimeringar kan fungera.

I det här exemplet så funderade jag dels på hur effektiva for-each-loopar är jämfört med vanliga loopar, och dels så funderade jag på hur effektivt min kompilator optimerade i kombination med hur min processor optimerar i sig.

Jag har någon slags stationär dator som är rätt snabb (bra specifikationer va?).

Här är koden.

#include <iostream>
#include <chrono>
#include <string>
#include <random>

using namespace std;

chrono::high_resolution_clock::time_point start;

void tic() {
    start = std::chrono::high_resolution_clock::now();
}

double toc() {
    auto finish = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> duration(finish - start);
    return duration.count();
}

int main(int argc, char **argv) {
    vector<float> a(100000000);
    vector<float> b;
    b.reserve(a.size());

    cout << endl;
    cout << "generating numbers " << endl;

    for (auto &f : a) {
        f = (float)(rand() % 10000) / 10000. - .5;
    }

    cout << "random generation finished... numbers like:" << endl;

    for (int i = 0; i < 10; ++i) {
        cout << a[i] << " ";
    }
    cout << endl;

    tic();
    for (auto f : a) {
        b.push_back(f * (f > 0.));
    }
    cout << "multiplications for each " << toc() << endl;

    b.clear();
    b.reserve(a.size());

    tic();
    for (int i = 0; i < a.size(); ++i) {
        auto f = a[i];
        b.push_back(f * (f > 0));
    }
    cout << "multiplications regular-for: " << toc() << endl;

    b.clear();
    b.resize(a.size());

    tic();
    for (int i = 0; i < a.size(); ++i) {
        auto f = a[i];
        b[i] = f * (f > 0);
    }
    cout << "indexes multiplications regular-for: " << toc() << endl;

    b.clear();
    b.resize(a.size());

    tic();
    for (int i = 0; i < a.size(); ++i) {
        auto f = a[i];
        if (f > 0) {
            b[i] = f;
        } else {
            b[i] = 0;
        }
    }
    cout << "indexes if-statements regular for " << toc() << endl;
}

Resultatet blev som följande: Utan några optimeringar givna till g++ får jag följande resultat.

generating numbers random generation finished... numbers like: 0.4383 -0.4114 -0.2223 0.1915 0.2793 0.3335 0.0386 -0.4508 0.1649 -0.3579 multiplications for each 4.21264 multiplications regular-for: 2.96703 indexes multiplications regular-for: 1.37391 indexes inline function regular-for: 1.43445

if statements for each 3.5061 if-statements regular for 3.10215 indexes if-statements regular for 1.64089

Med optimeringar (-O3) får jag följande resultat.

generating numbers random generation finished... numbers like: 0.4383 -0.4114 -0.2223 0.1915 0.2793 0.3335 0.0386 -0.4508 0.1649 -0.3579 multiplications for each 0.27903 multiplications regular-for: 0.158509 indexes multiplications regular-for: 0.15735 indexes inline function regular-for: 0.161381

if statements for each 0.572224 if-statements regular for 0.587925 indexes if-statements regular for 0.150071

Så vad kan man dra för slutsatser? Jag tar med mig följande tumregler.

1: Ska det gå snabbt: Se till att använda kompilatorns inbyggda optimeringar. (Det går ungefär tio gånger snabbare i det här exemplet).

2: Det går oftast snabbare att använda en vanlig indexerad for-loop än att använda en for-each-loop (om prestanda är väldigt väldigt viktigt).

3: Egna optimeringar kan fungera, som exempelvis när man som ovan multiplicerar med en siffra för att undvika en if-sats, men inte alls i samma grad som den inbyggda kompilatorn. Ska man mikrooptimera så är det viktigt att testa koden för att hitta det som fungerar snabbast på den datorn där koden ska köras.

4: Inline-funktioner drar ner prestandan lite (i det här fallet runt 5%).

Not: Kom alltid ihåg att det finns saker som gör att det inte blir som man tror att det är som: Kompilatorn optimerar på ett sätt som man inte vet om, cache-missar och branch-prediction (varje grej förtjänar några googlingar på internettet).


Andra inlägg från bloggen

2024

Använda cpp som ett skriptspråk (2024-12-03)

Mekaniskt tangentbord Lasersköld L1 del 1 (2024-11-14)

Alphasmart neo 2 font editor (2024-10-11)

Bloggen är äntligen fyttad (2024-09-24)

2022

Figurer i skuggan (2022-06-04)

2018

prestandatest pa morgonen c (2018-11-21)

2017

Framsteg och frustration (2017-08-31)

2015

fix audacity startup bug on ubuntu 1510 (2015-11-24)

git subtree (2015-06-21)

add native code to android studio (2015-06-21)

2014

Ingenting (2014-11-08)

Äldre inlägg...