loop optimieren



  • Hallo

    Ich hab die cache misses ja gar nicht gepostet... wahrscheinlich hab ich nur von getraeumt 🙄

    Events recorded abbreviations are:
        Ir : I cache reads (ie. instructions executed) 
        I1mr: I1 cache read misses 
        I2mr: L2 cache instruction read misses 
        Dr : D cache reads (ie. memory reads) 
        D1mr: D1 cache read misses 
        D2mr: L2 cache data read misses 
        Dw : D cache writes (ie. memory writes) 
        D1mw: D1 cache write misses 
        D2mw: L2 cache data write misses
    
    --------------------------------------------------------------------------------
    Profile data file 'callgrind.out.7741' (creator: callgrind-3.3.0-Debian)
    --------------------------------------------------------------------------------
    I1 cache: 65536 B, 64 B, 2-way associative
    D1 cache: 65536 B, 64 B, 2-way associative
    L2 cache: 1048576 B, 64 B, 8-way associative
    Timerange: Basic block 0 - 3810525588
    Trigger: Program termination
    Profiled target:  /home/nschmid/gromos/svn/gromosXX/x86_64/bin/md @f md.inp (PID 7741, part 1)
    Events recorded:  Ir Dr Dw I1mr D1mr D1mw I2mr D2mr D2mw
    Events shown:     Ir Dr Dw I1mr D1mr D1mw I2mr D2mr D2mw
    Event sort order: Ir Dr Dw I1mr D1mr D1mw I2mr D2mr D2mw
    Thresholds:       99 0 0 0 0 0 0 0 0
    Include dirs:     
    User annotated:   
    Auto-annotation:  on
    
    --------------------------------------------------------------------------------
                Ir             Dr            Dw    I1mr        D1mr       D1mw    I2mr       D2mr       D2mw 
    --------------------------------------------------------------------------------
    59,696,737,439 20,453,280,842 8,831,821,039 601,767 200,229,353 40,511,699 168,776 31,171,410 20,088,219  PROGRAM TOTALS
    
    --------------------------------------------------------------------------------
    -- Auto-annotated source: /home/nschmid/gromos/svn/gromosXX/x86_64/src/interaction/../../../src/interaction/nonbonded/interaction/solvent_innerloop.cc
    --------------------------------------------------------------------------------
               Ir          Dr          Dw I1mr       D1mr  D1mw I2mr    D2mr D2mw 
    
    -- line 27 ----------------------------------------
                .           .           .    .          .     .    .       .    .    math::Vec * const force_i = &storage.force(i);
                .           .           .    .          .     .    .       .    .  
                .           .           .    .          .     .    .       .    .    math::Vec const * const pos_j = &conf.current().pos(j);
                .           .           .    .          .     .    .       .    .    math::Vec * const force_j = &storage.force(j);
                .           .           .    .          .     .    .       .    .    DEBUG(9, "i = " << i << " j = " << j);    
                .           .           .    .          .     .    .       .    .  
                .           .           .    .          .     .    .       .    .    // first atom vs. first atom
                .           .           .    .          .     .    .       .    .    periodicity.nearest_image(*pos_i, *pos_j, r);
      168,138,585 168,138,585           0    0      1,421     0    0       2    .    const double tx = r(0) - (*pos_i)(0) + (*pos_j)(0);
      100,883,151 100,883,151           0    0        184     .    .       .    .    const double ty = r(1) - (*pos_i)(1) + (*pos_j)(1);
      100,883,151 100,883,151           0  100         90     0  100       .    .    const double tz = r(2) - (*pos_i)(2) + (*pos_j)(2);
                .           .           .    .          .     .    .       .    .    
                .           .           .    .          .     .    .       .    .    double e_lj = 0.0, e_crf = 0.0;
                .           .           .    .          .     .    .       .    .    
                .           .           .    .          .     .    .       .    .    // loop over atom pairs
      638,926,623 100,883,151 168,138,585  100          0   272  100       0   10    for(unsigned int param = 0, atom_i = 0; atom_i < num_solvent_atoms; ++atom_i) {
      403,532,604 201,766,302 100,883,151  100    134,553     0  100   1,613    .      const double xi = (*(pos_i+atom_i))(0) + tx;
      302,649,453 100,883,151 100,883,151    0    123,708     0    0   1,448    .      const double yi = (*(pos_i+atom_i))(1) + ty;
      706,182,057 201,766,302 201,766,302    0    125,634     0    0   1,000    .      const double zi = (*(pos_i+atom_i))(2) + tz;
    1,614,130,416 302,649,453 403,532,604    0          0 4,949    0       0  746      for(unsigned int atom_j = 0; atom_j < num_solvent_atoms; ++atom_j, ++param) {
                .           .           .    .          .     .    .       .    .        DEBUG(15, "\tatoms: i: " << atom_i << " j: " << atom_j);
      907,948,359 907,948,359           0    0  6,537,647     0    0 171,849    .        const double x = xi - (*(pos_j+atom_j))(0);
      605,298,906 605,298,906           0  100  6,587,194     0  100 173,798    .        const double y = yi - (*(pos_j+atom_j))(1);
      605,298,906 605,298,906           0    0  6,629,181     0    0 171,226    .        const double z = zi - (*(pos_j+atom_j))(2);
                .           .           .    .          .     .    .       .    .        
    2,421,195,624           0           0  100          0     0  100       .    .        const double r2 = x * x + y * y + z * z;
                .           .           .    .          .     .    .       .    .        DEBUG(15, "\tr2: " << r2);
                .           .           .    .          .     .    .       .    .        assert(r2 != 0.0);
      605,298,906 302,649,453           0    0      3,836     0    0     184    .        const double r2i = 1.0 / r2;
    1,210,597,812           .           .    .          .     .    .       .    .        const double ri = sqrt(r2i);
      907,948,359           0           0  100          0     0  100       .    .        const double dist6i = r2i * r2i * r2i;
    1,513,247,265 605,298,906           0  100      3,413     0  100     584    .        const double dist6i_c12 = pair_parameter[param].c12 * dist6i;
                .           .           .    .          .     .    .       .    .  
    1,815,896,718 302,649,453           0    0     16,331     0    0   1,348    .        e_lj += (dist6i_c12 - pair_parameter[param].c6) * dist6i;
    2,118,546,171 907,948,359           0  100      8,117     0  100   1,083    .        e_crf += pair_parameter[param].q * (ri - m_crf_2cut3i * r2 - m_crf_cut);
                .           .           .    .          .     .    .       .    .  
                .           .           .    .          .     .    .       .    .        const double f = (dist6i_c12 + dist6i_c12 - pair_parameter[param].c6) * 6.0 * dist6i * r2i +
    2,723,845,077 605,298,906           0    2      5,440     0    2     684    .                pair_parameter[param].q * (ri * r2i + m_crf_cut3i);
                .           .           .    .          .     .    .       .    .  
      605,298,906           .           .    .          .     .    .       .    .        const double fx = f * x;
      605,298,906           .           .    .          .     .    .       .    .        const double fy = f * y;
      302,649,453           .           .    .          .     .    .       .    .        const double fz = f * z;
                .           .           .    .          .     .    .       .    .  
    1,210,597,812 605,298,906 302,649,453    0    262,421     0    0   3,871    .        (*(force_i+atom_i))(0) += fx;
      907,948,359 302,649,453 302,649,453  100 33,260,417     0  100 971,453    .        (*(force_j+atom_j))(0) -= fx;
      907,948,359 302,649,453 302,649,453    0    193,492     0    0   4,409    .        (*(force_i+atom_i))(1) += fy;
      907,948,359 302,649,453 302,649,453    0 10,177,012     0    0 365,584    .        (*(force_j+atom_j))(1) -= fy;
      907,948,359 302,649,453 302,649,453    0    186,904     0    0   2,404    .        (*(force_i+atom_i))(2) += fz;
      907,948,359 302,649,453 302,649,453    0  9,921,693     0    0 255,507    .        (*(force_j+atom_j))(2) -= fz;
                .           .           .    .          .     .    .       .    .  
    1,210,597,812 302,649,453 302,649,453  200          0     0  200       .    .        storage.virial_tensor(0, 0) += x * fx;
    1,210,597,812 302,649,453 302,649,453    .          .     .    .       .    .        storage.virial_tensor(0, 1) += x * fy;
      907,948,359 302,649,453 302,649,453    0      5,199     0    0     685    .        storage.virial_tensor(0, 2) += x * fz;
    1,210,597,812 302,649,453 302,649,453  100          0     0  100       .    .        storage.virial_tensor(1, 0) += y * fx;
    1,210,597,812 302,649,453 302,649,453    .          .     .    .       .    .        storage.virial_tensor(1, 1) += y * fy;
      907,948,359 302,649,453 302,649,453  100          0     0  100       .    .        storage.virial_tensor(1, 2) += y * fz;
      907,948,359 302,649,453 302,649,453    0      2,577     0    0     259    .        storage.virial_tensor(2, 0) += z * fx;
      907,948,359 302,649,453 302,649,453  100          0     0  100       .    .        storage.virial_tensor(2, 1) += z * fy;
      907,948,359 302,649,453 302,649,453    0      1,503     0    0     293    .        storage.virial_tensor(2, 2) += z * fz;
                .           .           .    .          .     .    .       .    .      }
                .           .           .    .          .     .    .       .    .    }
      100,883,151  67,255,434  33,627,717    0    283,981     0    0     261    .    storage.energies.lj_energy[egroup][egroup] += e_lj;
      168,138,585 100,883,151  67,255,434    0      4,627     0    0   1,017    .    storage.energies.crf_energy[egroup][egroup] += e_crf;
                .           .           .    .          .     .    .       .    .  }
                .           .           .    .          .     .    .       .    .  
                .           .           .    .          .     .    .       .    .  
                .           .           .    .          .     .    .       .    .  template<typename t_nonbonded_spec>
                .           .           .    .          .     .    .       .    .  inline void 
                .           .           .    .          .     .    .       .    .  interaction::Nonbonded_Innerloop<t_nonbonded_spec>::spc_innerloop
                .           .           .    .          .     .    .       .    .  (
                .           .           .    .          .     .    .       .    .   topology::Topology & topo,
    -- line 97 ----------------------------------------
    

    Hier sind die Resultate:



  • Nephelauxetic schrieb:

    Das ueberlasse ich dem Compiler. Der compiler optimiert mit den Profiling Informationen und kann also die Loops unrollen wenn er will. Bringen tut das aber nicht viel.

    Ich dachte, Loops unrollen macht er nur wenn er kann - ist aber egal. Ich denke auch, das bringt wirklich nicht viel, 20E6 Takte hört sich nach viel an, ist aber bei einer 1 GHz CPU nur etwa 20 ms. 1 fps, hehe.

    Nephelauxetic schrieb:

    g++ 4.2.3, Linux, -O3 -funroll-loops -fno-gcse

    Man könnte noch folgende Flags ausprobieren:

    -ffast-math -fno-rtti
    

    Ansonsten, ich geb auf 😉 Ich lege großen Wert auf Lesbarkeit und "Debugbarkeit" eines Programms...



  • Ich weiss nicht genau wie die OpenMP Implementierung skaliert. Ich hab mich nur mit MPI beschaeftigt und da waer das nicht trivial zu implementieren. Aber daran gedacht hab ich auch schon

    Aus

    #pragma omp parallel
    {
      #pragma omp for
      for(int i=0; i<n; ++i){
        ...
      }
    }
    

    wird meistens etwas wie:

    #pragma omp parallel
    {
      const int begin = n*omp_get_thread_num() / omp_get_num_threads();
      const int end = n*(omp_get_thread_num()+1) / omp_get_num_threads();
      for(int i=begin; i<end; ++i){
        ...
      }
    }
    

    Skaliert also recht gut mit n wenn die Laufzeit von ... nicht von i abhängt. Bringt aber nur etwas wenn die Anzahl der Cores <<< n

    Ein #pragma parallel for num_threads(2) ist ja schnell vor eine Schleife geschrieben von daher schadet testen wahrscheinlich nicht. Ich glaub aber nicht wirklich dran, dass das bei 2 Cores viel bringt und bei vielen Cores bin ich mir auch nicht sicher.

    Ansonsten sehe ich leider kein Potential mehr in dem Code, sorry.



  • Hast du evtl. ein vollständiges Testprogramm für diese Funktion? Dann könntest du das in Gänze posten (http://rafb.net/paste/ & co.) damit wir eigene Experimente anstellen können. Bloßes Draufschauen hilft bei solchen Microoptimierungen leider oft nicht.



  • OK, ich habe schon ewig nicht mehr mit der gcc rumgespielt, aber wenn ich mich recht entsinne ist es ganz sinnvoll -march zu verwenden.



  • Wenn ihr aus dem Code noch mehr als 10% rausholt, bekommt ihr nen virtuellen Keks.



  • camper schrieb:

    Hast du evtl. ein vollständiges Testprogramm für diese Funktion? Dann könntest du das in Gänze posten (http://rafb.net/paste/ & co.) damit wir eigene Experimente anstellen können. Bloßes Draufschauen hilft bei solchen Microoptimierungen leider oft nicht.

    Ich kann dir den Code per Mail schicken. Das Problem ist das du ganze Bibliothek (fuer Objekte wie topo, conf etc.) einfach riesig ist. Zudem wuerd ich den Code in dieser Form nur ungern ins Netz stellen.

    Helium schrieb:

    aber wenn ich mich recht entsinne ist es ganz sinnvoll -march zu verwenden.

    Ja in der Regel schon. Hier bringt es nichts.

    Ben04 schrieb:

    Skaliert also recht gut mit n wenn die Laufzeit von ... nicht von i abhängt.

    Ausser in den trivialsten Faellen hat man das aber nie.

    LG
    Neph



  • Das hat man sogar erstaunlich oft. Oder war das nur auf dein Programm bezogen?



  • Walli schrieb:

    Das hat man sogar erstaunlich oft. Oder war das nur auf dein Programm bezogen?

    Nein allgemein. Kann sein dass ich aber bis jetzt einfach nur Pech hatte dass ich noch nie eines dieser schoenen Daten-parallelen Lehrbuchbeispiele in der Praxis hatte. 😃

    Aber das ist hier OT.



  • abc.w schrieb:

    -ffast-math -fno-rtti
    

    Das bringt fast 10% auf dem ganzen Programm 🙂 Dies liegt aber am fast-math. no-rtti hat keinen Effekt.



  • Nephelauxetic schrieb:

    Dies liegt aber am fast-math. no-rtti hat keinen Effekt.

    -fno-rtti durfte sich laut gcc manual u.U. auf den Speicherverbrauch auswirken. -ffast-math bewirkt u.a. dass bei Berechnungen keine bedingten Sprungbefehle generiert werden. Damit spart man wahrscheinlich CPU-Takte. Optimal wäre, überhaupt keine Sprünge im Code zu haben, keine Schleifen, keine if-s, keine switches, am besten nur lauter nops 😉 Vielleicht lohnt es sich doch, die verschachtelte Schleife in deiner Funktion irgendwie zu beseitigen. Das ist halt nur problematisch, weil nun ja, Algorithmus umdenken und so was, ist nicht trivial , kompliziert und man muss sich dabei anstrengen 😉 Aber damit hättest Du dann num_solvent_atoms mal was weiss ich wieviele Takte noch mal gespart. Und mit Sicherheit deinen Schein oder Prüfungsnote bekommen.



  • pleevsevsi@farifluset.mailexpire.com

    Die Wirkung der Optimierungsoptionen ist leider recht unzuverlässig, interessant vielleicht auch Acovea

    Evtl bringt es hier etwas, ein wenig mit --param max-reload-search-insns=x herumzuspielen mit großen x (geht wahrscheinlich auf Kosten der Compilierzeit). In der Regel basiert die Suche nach den besten Optionen auf trial&error.



  • Und konnte noch viel aus dem Code rausgeholt werden?



  • by the hammer


Anmelden zum Antworten