SSE2/3 bringt keine Beschleunigung



  • Hallo

    Ich habe mich zum ersten mal an SSE2/3 heran gewagt in der Hoffnung einen wichtigen Loop zu verschnellern. Der Code funktioniert und gibt die gleichen Resultate wie die serielle Version. Beim Tempo bin ich aber enttäuscht: so gut wie gar kein Speedup.

    Kann mir jemand sagen wo das Problem liegt? Kann es sein dass GCC bereits so gut optimiert dass manuelles SSE2 überflüssig wird?

    Ich denke es muss irgend ein Anfängerfehler sein: zuviele Variablen oder sowas.

    Gruss
    Neph

    math::Vec r;
    
      // constants for SSE
      const double one = 1.0;
      __m128d reg_one = _mm_load1_pd(&one);
      __m128d reg_cut, reg_pref_OH, reg_pref_HH;
      reg_cut = _mm_load1_pd(&m_crf_cut3i);
      const double pref_OH = -0.82 * 0.41 * 138.9354;
      reg_pref_OH = _mm_load1_pd(&pref_OH);
      const double pref_HH = 0.41 * 0.41 * 138.9354;
      reg_pref_HH = _mm_load1_pd(&pref_HH);
    
      //#define sse_align  __attribute__((aligned(16)))
      double  x[4] __attribute__((aligned(16))),
              y[4] __attribute__((aligned(16))),
              z[4] __attribute__((aligned(16)));
      double tx, ty, tz;
    
      double fx_vec[4] __attribute__((aligned(16))),
             fy_vec[4] __attribute__((aligned(16))),
             fz_vec[4] __attribute__((aligned(16)));
      double ri_sum[2] __attribute__((aligned(16))),
             r2_sum[2] __attribute__((aligned(16))),
             vir[2] __attribute__((aligned(16)));
    
      // only one energy group
      const int egroup = topo.atom_energy_group(topo.num_solute_atoms());
      DEBUG(8, "\tspc pair\t" << i << "\t" << j << " egroup " << egroup);
    
      double e_lj, e_crf;;
    
      const int ii = i;
      math::Vec const * const pos_i = &conf.current().pos(ii);
      math::Vec * const force_i = &storage.force(ii);
    
      const int jj = j;
      math::Vec const * const pos_j = &conf.current().pos(jj);
      math::Vec * const force_j = &storage.force(jj);
      DEBUG(9, "ii = " << ii << " jj = " << jj);    
    
      // O - O
      periodicity.nearest_image(*pos_i, *pos_j, r);
    
      tx = r(0) - (*pos_i)(0) + (*pos_j)(0);
      ty = r(1) - (*pos_i)(1) + (*pos_j)(1);
      tz = r(2) - (*pos_i)(2) + (*pos_j)(2);
    
      assert(abs2(r) != 0);
    
      // O - H interactions...
      x[0] = (*pos_i)(0) - (*(pos_j+1))(0) + tx;
      y[0] = (*pos_i)(1) - (*(pos_j+1))(1) + ty;
      z[0] = (*pos_i)(2) - (*(pos_j+1))(2) + tz;
    
      x[1] = (*pos_i)(0) - (*(pos_j+2))(0) + tx;
      y[1] = (*pos_i)(1) - (*(pos_j+2))(1) + ty;
      z[1] = (*pos_i)(2) - (*(pos_j+2))(2) + tz;
    
      x[2] = (*(pos_i+1))(0) - (*(pos_j))(0) + tx;
      y[2] = (*(pos_i+1))(1) - (*(pos_j))(1) + ty;
      z[2] = (*(pos_i+1))(2) - (*(pos_j))(2) + tz;
    
      x[3] = (*(pos_i+2))(0) - (*(pos_j))(0) + tx;
      y[3] = (*(pos_i+2))(1) - (*(pos_j))(1) + ty;
      z[3] = (*(pos_i+2))(2) - (*(pos_j))(2) + tz;
    
      __m128d reg_x[2], reg_y[2], reg_z[2];
      reg_x[0] = _mm_load_pd(x);
      reg_x[1] = _mm_load_pd(x+2);
      reg_y[0] = _mm_load_pd(y);
      reg_y[1] = _mm_load_pd(y+2);
      reg_z[0] = _mm_load_pd(z);
      reg_z[1] = _mm_load_pd(z+2);
    
      __m128d reg_r2[2];
      reg_r2[0] = _mm_add_pd(_mm_add_pd(_mm_mul_pd(reg_x[0], reg_x[0]),_mm_mul_pd(reg_y[0], reg_y[0])), _mm_mul_pd(reg_z[0], reg_z[0]));
      reg_r2[1] = _mm_add_pd(_mm_add_pd(_mm_mul_pd(reg_x[1], reg_x[1]),_mm_mul_pd(reg_y[1], reg_y[1])), _mm_mul_pd(reg_z[1], reg_z[1]));
    
      // replace by reciprocal! (if possible?)
      __m128d reg_r2i[2];
      reg_r2i[0] = _mm_div_pd(reg_one, reg_r2[0]);
      reg_r2i[1] = _mm_div_pd(reg_one, reg_r2[1]);
    
      __m128d reg_ri[2];
      reg_ri[0] = _mm_sqrt_pd(reg_r2i[0]);
      reg_ri[1] = _mm_sqrt_pd(reg_r2i[1]);
    
      // calculate horizontal sums for energy
      __m128d reg_ri_sum = _mm_hadd_pd(reg_ri[0], reg_ri[1]);
      _mm_store_pd(ri_sum, reg_ri_sum);
      __m128d reg_r2_sum = _mm_hadd_pd(reg_r2[0], reg_r2[1]);
      _mm_store_pd(r2_sum, reg_r2_sum);
    
      e_crf += pref_OH * (ri_sum[0] + ri_sum[1] - m_crf_2cut3i * (r2_sum[0] + r2_sum[1]) - 4 * m_crf_cut);
    
      __m128d reg_ff[2];
      reg_ff[0] = _mm_mul_pd(reg_pref_OH, _mm_add_pd(_mm_mul_pd(reg_r2i[0], reg_ri[0]), reg_cut));
      reg_ff[1] = _mm_mul_pd(reg_pref_OH, _mm_add_pd(_mm_mul_pd(reg_r2i[1], reg_ri[1]), reg_cut));
    
      __m128d reg_fx[2], reg_fy[2], reg_fz[2];
      reg_fx[0] = _mm_mul_pd(reg_x[0], reg_ff[0]);
      reg_fx[1] = _mm_mul_pd(reg_x[1], reg_ff[1]);
      _mm_store_pd(fx_vec, reg_fx[0]);
      _mm_store_pd(fx_vec + 2, reg_fx[1]);
      reg_fy[0] = _mm_mul_pd(reg_y[0], reg_ff[0]);
      reg_fy[1] = _mm_mul_pd(reg_y[1], reg_ff[1]);
      _mm_store_pd(fy_vec, reg_fy[0]);
      _mm_store_pd(fy_vec + 2, reg_fy[1]);
      reg_fz[0] = _mm_mul_pd(reg_z[0], reg_ff[0]);
      reg_fz[1] = _mm_mul_pd(reg_z[1], reg_ff[1]);
      _mm_store_pd(fz_vec, reg_fz[0]);
      _mm_store_pd(fz_vec + 2, reg_fz[1]);
    
      (*force_i)(0) += fx_vec[0];
      (*(force_j+1))(0) -= fx_vec[0];
      (*force_i)(1) += fy_vec[0];
      (*(force_j+1))(1) -= fy_vec[0];
      (*force_i)(2) += fz_vec[0];
      (*(force_j+1))(2) -= fz_vec[0];
    
      (*force_i)(0) += fx_vec[1];
      (*(force_j+2))(0) -= fx_vec[1];
      (*force_i)(1) += fy_vec[1];
      (*(force_j+2))(1) -= fy_vec[1];
      (*force_i)(2) += fz_vec[1];
      (*(force_j+2))(2) -= fz_vec[1];  
    
      (*(force_i+1))(0) += fx_vec[2];
      (*(force_j))(0) -= fx_vec[2];
      (*(force_i+1))(1) += fy_vec[2];
      (*(force_j))(1) -= fy_vec[2];
      (*(force_i+1))(2) += fz_vec[2];
      (*(force_j))(2) -= fz_vec[2];
    
      (*(force_i+2))(0) += fx_vec[3];
      (*(force_j))(0) -= fx_vec[3];
      (*(force_i+2))(1) += fy_vec[3];
      (*(force_j))(1) -= fy_vec[3];
      (*(force_i+2))(2) += fz_vec[3];
      (*(force_j))(2) -= fz_vec[3];
    
      // SSE virial
      __m128d reg_vir = _mm_hadd_pd(_mm_mul_pd(reg_x[0], reg_fx[0]), _mm_mul_pd(reg_x[1], reg_fx[1]));
      _mm_store_pd(vir, reg_vir);
      storage.virial_tensor(0, 0) += vir[0] + vir[1];
      reg_vir = _mm_hadd_pd(_mm_mul_pd(reg_x[0], reg_fy[0]), _mm_mul_pd(reg_x[1], reg_fy[1]));
      _mm_store_pd(vir, reg_vir);
      storage.virial_tensor(0, 1) += vir[0] + vir[1];
      reg_vir = _mm_hadd_pd(_mm_mul_pd(reg_x[0], reg_fz[0]), _mm_mul_pd(reg_x[1], reg_fz[1]));
      _mm_store_pd(vir, reg_vir);
      storage.virial_tensor(0, 2) += vir[0] + vir[1];
      reg_vir = _mm_hadd_pd(_mm_mul_pd(reg_y[0], reg_fx[0]), _mm_mul_pd(reg_y[1], reg_fx[1]));
      _mm_store_pd(vir, reg_vir);
      storage.virial_tensor(1, 0) += vir[0] + vir[1];
      reg_vir = _mm_hadd_pd(_mm_mul_pd(reg_y[0], reg_fy[0]), _mm_mul_pd(reg_y[1], reg_fy[1]));
      _mm_store_pd(vir, reg_vir);
      storage.virial_tensor(1, 1) += vir[0] + vir[1];
      reg_vir = _mm_hadd_pd(_mm_mul_pd(reg_y[0], reg_fz[0]), _mm_mul_pd(reg_y[1], reg_fz[1]));
      _mm_store_pd(vir, reg_vir);
      storage.virial_tensor(1, 2) += vir[0] + vir[1];
      reg_vir = _mm_hadd_pd(_mm_mul_pd(reg_z[0], reg_fx[0]), _mm_mul_pd(reg_z[1], reg_fx[1]));
      _mm_store_pd(vir, reg_vir);
      storage.virial_tensor(2, 0) += vir[0] + vir[1];
      reg_vir = _mm_hadd_pd(_mm_mul_pd(reg_z[0], reg_fy[0]), _mm_mul_pd(reg_z[1], reg_fy[1]));
      _mm_store_pd(vir, reg_vir);
      storage.virial_tensor(2, 1) += vir[0] + vir[1];
      reg_vir = _mm_hadd_pd(_mm_mul_pd(reg_z[0], reg_fz[0]), _mm_mul_pd(reg_z[1], reg_fz[1]));
      _mm_store_pd(vir, reg_vir);
      storage.virial_tensor(2, 2) += vir[0] + vir[1];
    
      // H - H interactions...
      x[0] = (*(pos_i+1))(0) - (*(pos_j+1))(0) + tx;
      y[0] = (*(pos_i+1))(1) - (*(pos_j+1))(1) + ty;
      z[0] = (*(pos_i+1))(2) - (*(pos_j+1))(2) + tz;
    
      x[1] = (*(pos_i+1))(0) - (*(pos_j+2))(0) + tx;
      y[1] = (*(pos_i+1))(1) - (*(pos_j+2))(1) + ty;
      z[1] = (*(pos_i+1))(2) - (*(pos_j+2))(2) + tz;
    
      x[2] = (*(pos_i+2))(0) - (*(pos_j+1))(0) + tx;
      y[2] = (*(pos_i+2))(1) - (*(pos_j+1))(1) + ty;
      z[2] = (*(pos_i+2))(2) - (*(pos_j+1))(2) + tz;
    
      x[3] = (*(pos_i+2))(0) - (*(pos_j+2))(0) + tx;
      y[3] = (*(pos_i+2))(1) - (*(pos_j+2))(1) + ty;
      z[3] = (*(pos_i+2))(2) - (*(pos_j+2))(2) + tz;
    
      reg_x[0] = _mm_load_pd(x);
      reg_x[1] = _mm_load_pd(x+2);
      reg_y[0] = _mm_load_pd(y);
      reg_y[1] = _mm_load_pd(y+2);
      reg_z[0] = _mm_load_pd(z);
      reg_z[1] = _mm_load_pd(z+2);
    
      reg_r2[0] = _mm_add_pd(_mm_add_pd(_mm_mul_pd(reg_x[0], reg_x[0]),_mm_mul_pd(reg_y[0], reg_y[0])), _mm_mul_pd(reg_z[0], reg_z[0]));
      reg_r2[1] = _mm_add_pd(_mm_add_pd(_mm_mul_pd(reg_x[1], reg_x[1]),_mm_mul_pd(reg_y[1], reg_y[1])), _mm_mul_pd(reg_z[1], reg_z[1]));
    
      // replace by reciprocal! (if possible?)
      reg_r2i[0] = _mm_div_pd(reg_one, reg_r2[0]);
      reg_r2i[1] = _mm_div_pd(reg_one, reg_r2[1]);
    
      reg_ri[0] = _mm_sqrt_pd(reg_r2i[0]);
      reg_ri[1] = _mm_sqrt_pd(reg_r2i[1]);
    
      // calculate horizontal sums for energy
      reg_ri_sum = _mm_hadd_pd(reg_ri[0], reg_ri[1]);
      _mm_store_pd(ri_sum, reg_ri_sum);
      reg_r2_sum = _mm_hadd_pd(reg_r2[0], reg_r2[1]);
      _mm_store_pd(r2_sum, reg_r2_sum);
    
      e_crf += pref_HH * (ri_sum[0] + ri_sum[1] - m_crf_2cut3i * (r2_sum[0] + r2_sum[1]) - 4 * m_crf_cut);
    
      reg_ff[0] = _mm_mul_pd(reg_pref_HH, _mm_add_pd(_mm_mul_pd(reg_r2i[0], reg_ri[0]), reg_cut));
      reg_ff[1] = _mm_mul_pd(reg_pref_HH, _mm_add_pd(_mm_mul_pd(reg_r2i[1], reg_ri[1]), reg_cut));
    
      reg_fx[0] = _mm_mul_pd(reg_x[0], reg_ff[0]);
      reg_fx[1] = _mm_mul_pd(reg_x[1], reg_ff[1]);
      _mm_store_pd(fx_vec, reg_fx[0]);
      _mm_store_pd(fx_vec + 2, reg_fx[1]);
      reg_fy[0] = _mm_mul_pd(reg_y[0], reg_ff[0]);
      reg_fy[1] = _mm_mul_pd(reg_y[1], reg_ff[1]);
      _mm_store_pd(fy_vec, reg_fy[0]);
      _mm_store_pd(fy_vec + 2, reg_fy[1]);
      reg_fz[0] = _mm_mul_pd(reg_z[0], reg_ff[0]);
      reg_fz[1] = _mm_mul_pd(reg_z[1], reg_ff[1]);
      _mm_store_pd(fz_vec, reg_fz[0]);
      _mm_store_pd(fz_vec + 2, reg_fz[1]);
    
      (*(force_i+1))(0) += fx_vec[0];
      (*(force_j+1))(0) -= fx_vec[0];
      (*(force_i+1))(1) += fy_vec[0];
      (*(force_j+1))(1) -= fy_vec[0];
      (*(force_i+1))(2) += fz_vec[0];
      (*(force_j+1))(2) -= fz_vec[0];
    
      (*(force_i+1))(0) += fx_vec[1];
      (*(force_j+2))(0) -= fx_vec[1];
      (*(force_i+1))(1) += fy_vec[1];
      (*(force_j+2))(1) -= fy_vec[1];
      (*(force_i+1))(2) += fz_vec[1];
      (*(force_j+2))(2) -= fz_vec[1];
    
      (*(force_i+2))(0) += fx_vec[2];
      (*(force_j+1))(0) -= fx_vec[2];
      (*(force_i+2))(1) += fy_vec[2];
      (*(force_j+1))(1) -= fy_vec[2];
      (*(force_i+2))(2) += fz_vec[2];
      (*(force_j+1))(2) -= fz_vec[2];
    
      (*(force_i+2))(0) += fx_vec[3];
      (*(force_j+2))(0) -= fx_vec[3];
      (*(force_i+2))(1) += fy_vec[3];
      (*(force_j+2))(1) -= fy_vec[3];
      (*(force_i+2))(2) += fz_vec[3];
      (*(force_j+2))(2) -= fz_vec[3];
    
      // SSE virial
      reg_vir = _mm_hadd_pd(_mm_mul_pd(reg_x[0], reg_fx[0]), _mm_mul_pd(reg_x[1], reg_fx[1]));
      _mm_store_pd(vir, reg_vir);
      storage.virial_tensor(0, 0) += vir[0] + vir[1];
      reg_vir = _mm_hadd_pd(_mm_mul_pd(reg_x[0], reg_fy[0]), _mm_mul_pd(reg_x[1], reg_fy[1]));
      _mm_store_pd(vir, reg_vir);
      storage.virial_tensor(0, 1) += vir[0] + vir[1];
      reg_vir = _mm_hadd_pd(_mm_mul_pd(reg_x[0], reg_fz[0]), _mm_mul_pd(reg_x[1], reg_fz[1]));
      _mm_store_pd(vir, reg_vir);
      storage.virial_tensor(0, 2) += vir[0] + vir[1];
      reg_vir = _mm_hadd_pd(_mm_mul_pd(reg_y[0], reg_fx[0]), _mm_mul_pd(reg_y[1], reg_fx[1]));
      _mm_store_pd(vir, reg_vir);
      storage.virial_tensor(1, 0) += vir[0] + vir[1];
      reg_vir = _mm_hadd_pd(_mm_mul_pd(reg_y[0], reg_fy[0]), _mm_mul_pd(reg_y[1], reg_fy[1]));
      _mm_store_pd(vir, reg_vir);
      storage.virial_tensor(1, 1) += vir[0] + vir[1];
      reg_vir = _mm_hadd_pd(_mm_mul_pd(reg_y[0], reg_fz[0]), _mm_mul_pd(reg_y[1], reg_fz[1]));
      _mm_store_pd(vir, reg_vir);
      storage.virial_tensor(1, 2) += vir[0] + vir[1];
      reg_vir = _mm_hadd_pd(_mm_mul_pd(reg_z[0], reg_fx[0]), _mm_mul_pd(reg_z[1], reg_fx[1]));
      _mm_store_pd(vir, reg_vir);
      storage.virial_tensor(2, 0) += vir[0] + vir[1];
      reg_vir = _mm_hadd_pd(_mm_mul_pd(reg_z[0], reg_fy[0]), _mm_mul_pd(reg_z[1], reg_fy[1]));
      _mm_store_pd(vir, reg_vir);
      storage.virial_tensor(2, 1) += vir[0] + vir[1];
      reg_vir = _mm_hadd_pd(_mm_mul_pd(reg_z[0], reg_fz[0]), _mm_mul_pd(reg_z[1], reg_fz[1]));
      _mm_store_pd(vir, reg_vir);
      storage.virial_tensor(2, 2) += vir[0] + vir[1];
    
      storage.energies.crf_energy[egroup][egroup] += e_crf;
    


  • wat? 😕



  • nephelauxetic schrieb:

    Ich habe mich zum ersten mal an SSE2/3 heran gewagt in der Hoffnung einen wichtigen Loop zu verschnellern. Der Code funktioniert und gibt die gleichen Resultate wie die serielle Version. Beim Tempo bin ich aber enttäuscht: so gut wie gar kein Speedup.

    Kann mir jemand sagen wo das Problem liegt? Kann es sein dass GCC bereits so gut optimiert dass manuelles SSE2 überflüssig wird?

    Wenn du die Optimierung eingeschaltet hast, kann das fuer ein solches loop sogar sehr gut sein. Ansonsten habe ich mir deinen Code jetzt nicht naeher angeschaut.



  • Hallo,

    könntest Du bitte noch dein Makefile o.ä. posten, wie genau Du die Datei kompilierst?



  • Der Orginalcode besteht im wesentlich aus dem selben nur dass alles anstatt auf zwei _m128d auf einem double[4] ausgeführt wird und der Code manuell unrolled wurde.

    Das Projekt ist unter autotools. Aber im Prinzip nichts weiter als: g++ -O3 -funroll-loops -fno-gcse -DNDEBUG -march=k8 -msse3

    beim seriellen Code lasse ich das -march=k8 -msse3 weg.

    LG
    Neph



  • Nephelauxetic schrieb:

    Aber im Prinzip nichts weiter als: g++ -O3 -funroll-loops -fno-gcse -DNDEBUG -march=k8 -msse3

    Bist Du sicher, dass es g++ ist, versuche nämlich zu kompilieren und bekomme Fehlermeldungen:

    tmp.cpp:8: error: __m128d' undeclared (first use this function) tmp.cpp:10: error:_mm_load1_pd' undeclared (first use this function)

    ...usw.
    In der gcc Doku sind die von Dir benutzten Befehle auch nicht zu finden. Habe hier die neueste pdf genommen: http://gcc.gnu.org/onlinedocs/
    Übrigens, zu -march=k8 steht da folgendes:

    k8, opteron, athlon64, athlon-fx
    AMD K8 core based CPUs with x86-64 instruction set support.
    (This supersets MMX, SSE, SSE2, 3dNOW!, enhanced 3dNOW!
    and 64-bit instruction set extensions.)

    Wird kein SSE3 erwähnt...

    Hier mein Makefile:

    all:
    g++ -c tmp.cpp -o tmp.o -O3 -funroll-loops -fno-gcse -DNDEBUG -march=k8 -msse3

    Und hier meine g++ Version:

    g++ (GCC) 3.4.2 (mingw-special)
    Copyright (C) 2004 Free Software Foundation, Inc.

    Also: würde gerne und aus Neugier kompilieren, aber geht nicht... 😉



  • Nephelauxetic schrieb:

    Der Orginalcode besteht im wesentlich aus dem selben nur dass alles anstatt auf zwei _m128d auf einem double[4] ausgeführt wird und der Code manuell unrolled wurde.

    Solche Loops kann der GCC seit der 4er Version selbst in SSE-Befehle uebersetzen. "Autovectorization" heisst das Schlagwort. Ich wuerd vermuten du hast keinen Speedup, weil der GCC selbst bereits SSE-Code erzeugt hat.



  • Ich hab Autovectorization ausgeschaltet (-fno_tree_vectorize) und hab den selben Speed. Daran kann es also nicht liegen. 😕



  • abc.w schrieb:

    Bist Du sicher, dass es g++ ist, versuche nämlich zu kompilieren und bekomme Fehlermeldungen:

    nimm mal

    #include <pmmintrin.h>
    

    mit rein. Da sind die SSE3 intrinsics so mehr oder weniger compiler unabhängig definiert.

    das -march musst du evt. durch march=pentium4 ersetzten bei dir.
    Die neuen AMDs haben aber SSE3 und mit -msse3 kann man's auch erzwingen. Ich denke auch dass SSE3 Dinge ausgeführt werden denn als ich von SSE2 auf SSE3 gewechselt habe (was ich nur wegen _mm_hadd_pd tun musste) hat das ganze nicht mehr richtig funktioniert und erst durch die -march=k8 -msse3 Kombi bekam ich die richtigen Zahlen raus.

    Das ganze wird so einfach aber nicht kompilieren. Der gepostete Code war nur ein kleines Fragment aus einem recht grossen Projekt. Da fehlt dir zum kompilieren noch eine ganze Menge.

    LG
    Neph



  • Nephelauxetic schrieb:

    Ich hab Autovectorization ausgeschaltet (-fno_tree_vectorize) und hab den selben Speed. Daran kann es also nicht liegen. 😕

    Dann schau dir einfach mal den generierten Assemblercode an. 😕



  • Hallo

    Ich hab mir den generierten Assembler Code angeschaut und es sieht so aus als ob der Loop tatsaechlich bereits vom Compiler mit SSE vektorisiert wurde.
    Das naechste mal tu ich das bevor ich Arbeit ins manuelle Vektorisieren stecke. 🙄

    Die Compiler scheinen ja wirklich gute Arbeit zu leisten 👍

    LG
    Neph



  • was hat dein profiler denn fuer diesen wichtigen loop gesagt, wieviel % der laufzeit geht an dieser stelle drauf (also in beiden versionen). welche stellen im loop sind laut deinem profiler die kritischsten.



  • 🕶
    Dass "-fno_tree_vectorize" nix gebracht hat, liegt vermutlich daran, dass du den Loop bereits manuell expandiert hast. So musste der Compiler ja nichtmal mehr Vektorisieren, er hat nur die "schnellste" Uebersetzung gewaehlt... und wenn da 4 unabhaengige Floating Point Operations nacheinander kommen, bietet sich SSE nunmal an. 🙂



  • rapso schrieb:

    was hat dein profiler denn fuer diesen wichtigen loop gesagt, wieviel % der laufzeit geht an dieser stelle drauf (also in beiden versionen). welche stellen im loop sind laut deinem profiler die kritischsten.

    Welchen Profiler verwendest du, der dir so genaue Infos geben kann? 😮


  • Mod

    Beim Überfliegen fallen ein paar Stellen auf, die wahrscheinlich nicht zu optimalen Code führen. Um das endgültig beurteilen zu können, müssen aber der Assembleroutput bekannt sein. Meine Erfahrung mit diesen Compiler-Intrinsics für SSE&Co. ist eher gemischt - bei hinreichend komplexen Funktionen ist der Compiler schnell überfordert und führt unnötig viele reloads (das äußert sich primär in überflüssigen mov-Befehlen) durch, man kann dabei viel Zeit verlieren - eine handgeschriebene Assemblerroutine kann daher oft noch einmal Einiges herausholen.



  • Blue-Tiger schrieb:

    rapso schrieb:

    was hat dein profiler denn fuer diesen wichtigen loop gesagt, wieviel % der laufzeit geht an dieser stelle drauf (also in beiden versionen). welche stellen im loop sind laut deinem profiler die kritischsten.

    Welchen Profiler verwendest du, der dir so genaue Infos geben kann? 😮

    ich habe noch keinen verwendet der das nicht konnte, welchen verwendest du der das nicht kann?



  • rapso schrieb:

    Blue-Tiger schrieb:

    rapso schrieb:

    was hat dein profiler denn fuer diesen wichtigen loop gesagt, wieviel % der laufzeit geht an dieser stelle drauf (also in beiden versionen). welche stellen im loop sind laut deinem profiler die kritischsten.

    Welchen Profiler verwendest du, der dir so genaue Infos geben kann? 😮

    ich habe noch keinen verwendet der das nicht konnte, welchen verwendest du der das nicht kann?

    um nicht allzu offtopic zu werden: http://www.c-plusplus.net/forum/viewtopic-var-t-is-223162



  • abc.w schrieb:

    [...]
    Und hier meine g++ Version:

    g++ (GCC) 3.4.2 (mingw-special)
    Copyright (C) 2004 Free Software Foundation, Inc.

    Also: würde gerne und aus Neugier kompilieren, aber geht nicht... 😉

    Lol, eine 4 Jahre alte Kompilerversion verwenden und sich dann wundern, wenn was nicht geht 😃

    Übrigens gibt es den GCC inzwischen nicht nur mit der 4 statt der 3 vorne, sondern schon bald wieder mit der 4 an zweiter Stelle, also beeil dich bevor du überrundet wirst 😃 😃



  • rapso schrieb:

    was hat dein profiler denn fuer diesen wichtigen loop gesagt, wieviel % der laufzeit geht an dieser stelle drauf (also in beiden versionen). welche stellen im loop sind laut deinem profiler die kritischsten.

    Ich rufe die Routine (welche Paarweise Interaktionen von SPC Wassermolekülen berechnet) tausende male auf. 42% der Laufzeit fällt auf diesen Teil und dies ändert sich bei meinem SSE loop um ca. 1%.
    Ich habe manuelle Timer für diese Loops die eigentlich recht verlässliche Resultate liefern.
    Mit gprof kann ich die Zeiten nicht so genau aufteilen da viel optimiert und inlined wird. So ist die weitere Aufschlüsselung leider ziemlich nichtssagend.

    Im Loop habe ich gar keine Ahnung und verlasse mich auf mein C++ wissen 🤡 🙄

    camper schrieb:

    Beim Überfliegen fallen ein paar Stellen auf, die wahrscheinlich nicht zu optimalen Code führen. Um das endgültig beurteilen zu können, müssen aber der Assembleroutput bekannt sein. Meine Erfahrung mit diesen Compiler-Intrinsics für SSE&Co. ist eher gemischt - bei hinreichend komplexen Funktionen ist der Compiler schnell überfordert und führt unnötig viele reloads (das äußert sich primär in überflüssigen mov-Befehlen) durch, man kann dabei viel Zeit verlieren - eine handgeschriebene Assemblerroutine kann daher oft noch einmal Einiges herausholen.

    Ja das habe ich auch gesehen. Der Code ist voller mov-s von denen ich auch einige überflüssige gesehen habe. Ich traue es mir aber nicht wirklich zu den Assembler selbst zu schreiben. Die SSE intrinsics waren für mich ein akzeptabler Kompromiss. Bei inline ASM seh ich nicht ganz wie ich das jemals portabel hinkriegen sollte und ehrlich gesagt fehlt mir da schlicht das nötige knowhow.
    Um die mov-s zu verhindern habe ich versucht die Anzahl __m128d "variablen" so klein wie möglich zu halten... aber es braucht halt viele - ich rechne auch viel.

    LG
    Neph



  • Nephelauxetic schrieb:

    Im Loop habe ich gar keine Ahnung und verlasse mich auf mein C++ wissen 🤡 🙄

    naja, das wissen truegt oft. ein profiler wuerde dir die instructions zeigen die am meisten ziehen. wenn du mal die profiling resultate im thread von blue tiger anschaust, siehst du, dass ich so sehen konnte, dass es am speicher lag (in diesem fall), dank profiler. dann konnte ich das optimieren und dieser 'peak' konnte fast vollkommen wegoptimiert werden.

    mein wissen alleine haette mir das nicht sagen koennen 😉


Anmelden zum Antworten