{"id":2976,"date":"2017-04-26T11:20:02","date_gmt":"2017-04-26T10:20:02","guid":{"rendered":"http:\/\/roboblog.fatal-fury.de\/?p=2976"},"modified":"2017-04-26T15:49:33","modified_gmt":"2017-04-26T14:49:33","slug":"c-guns-fortran-forall","status":"publish","type":"post","link":"http:\/\/roboblog.fatal-fury.de\/?p=2976","title":{"rendered":"C++ Guns - Fortran forall"},"content":{"rendered":"<p>Diesen Post m\u00f6chte ich mit einem k\u00fcrzlich aufgetretenen Problem in Fortran beginnen.<br \/>\nIn Fortran gibt es das <em>forall<\/em> keyword. Damit ist es m\u00f6glich Schleifen zu bauen, wie die <em>do<\/em> Schleife, nur dass es m\u00f6glich ist, <em>forall<\/em> Schleifen parallel abzuarbeiten.<br \/>\nEine Vorraussetzung daf\u00fcr ist folgende: Wenn <em>data<\/em> der Vector ist, auf den parallel gearbeitet werden soll, dann m\u00fcssen die Operationen auf <em>data(i)<\/em> und <em>data(j)<\/em> mit <em>i != j<\/em> unabh\u00e4ngig voneinander sein.<br \/>\nDas ist, f\u00fcr das Beispiel, welches ich hier vorstellen m\u00f6chte, der Fall.<\/p>\n<p>Beispiel: Jeder Punkt im Array soll transformiert, und in einem neuen Array abgespeichert werden. Hier ist die erste native Implementation:<\/p>\n<pre><code>\r\nprogram LLL\r\n  use naaaah\r\n  implicit none\r\n  integer :: i\r\n  type(Point2D), allocatable :: data(:), data2(:)\r\n  real :: x\r\n  \r\n  allocate(data(100))\r\n  allocate(data2(100))\r\n  \r\n  forall(i=1:size(data))\r\n    x = data(i)%x * cos(data(i)%y)\r\n    data2(i)%x = x\r\n    data2(i)%y = data(i)%y\r\n  end forall\r\nend program\r\n<\/code><\/pre>\n<p>Und die Compiler Fehlermeldung<\/p>\n<pre>\r\nx = data(i)%x * cos(data(i)%y)\r\nWarning: The FORALL with index \u2018i\u2019 is not used on the left side of the assignment at (1) and so might cause multiple assignment to this object\r\n<\/pre>\n<p>Das ist vollkommen richtig, WENN die forall Schleife parallel ausgef\u00fchrt wird, so wird parallel auf x geschrieben. Aber x ist nur ein scalar. Daher der Fehler. Abhilfe w\u00fcrde schaffen, x als Array zu declarieren. Aber hier gehen bei HPC sofort alle roten Alarmglocken an und die Notbremse wird gezogen.<br \/>\nOkay.<br \/>\nUntersuchen wir jetzt das St\u00fcck Code mal nach dem \"Was aber nicht Wie\" Prinzip. WAS soll der Code machen? Hm keine Ahnung. WIE macht er es? Er multipliziert x in Abh\u00e4ngigkeit von y. Okay das ist alles, was man wissen muss, um den Code umzuschreiben. Nennen wir die neue Funktion, welche obige Multiplikation ausf\u00fchrt als \"toLLL\". Damit sieht der Code so aus:<\/p>\n<pre><code>\r\npure function toLLL(point) result(LLLPoint)\r\n  implicit none\r\n  type(Point2D), intent(in) :: point\r\n  type(Point2D) LLLpoint\r\n  \r\n  LLLpoint%x = point%x * cos(point%y*DEGRAD)\r\n  LLLpoint%y = point%y\r\nend function\r\n\r\nforall(i=1:size(data))\r\n  data2(i) = toLLL(data(i))\r\nend forall\r\n<\/code><\/pre>\n<p>Nun compiliert der Code ohne Fehler! Warum? Weil die vermeintliche Variable x nicht mehr tempor\u00e4r vorkommt. Lass es mich nochmal verdeutlichen. Im alten Code hatten wir so etwas:<\/p>\n<pre><code>\r\nxTemp = xalt*fac;\r\nxneu = xTemp\r\n<\/code><\/pre>\n<p>Und im neuen Code haben wir so etwas<\/p>\n<pre><code>\r\nxneu= xalt*fac;\r\n<\/code><\/pre>\n<p>Es gibt also keine tempor\u00e4re Zwischenvariablen die irgendwie von Hand verwaltet werden m\u00fcsste. PLUS, der Code ist lesbarer geworden. PLUS der Code ist performanter, da er u.U. parallel ausgef\u00fchrt werden kann. Ich will nicht behaupten, dass durch das Einf\u00fchren einer neuen Funktion der Code mehr Richtung Funktionale Programmierung geht. Aber er ist definitiv besser. Das w\u00e4re es erst, wenn die Funktion <em>toLLL<\/em> als Funktionsparamter mit \u00fcbergeben w\u00fcrde. Hier ist die Grenze von Fortran erreicht. Ohne Templates muss f\u00fcr jede generische Variante von <em>Point2D<\/em> eine neue Funktion <em>toLLL<\/em> geschrieben werden. Und das inlinen \u00fcber mehrere Compilations Units hinweg ist auch nicht m\u00f6glich.<\/p>\n<p>In C++ gibt es nat\u00fcrlich die selbe Art von Problem, aber sie k\u00f6nnen leicht gel\u00f6st werden. Von Hand geschriebene Schleifen funktionieren nat\u00fcrlich immer. Aber, sobald Parallelisierung in's Spiel kommt. Egal auf welcher Art und Weise auch immer, ist es nicht mehr so einfach wie fr\u00fcher. Parallelisierung bedeutet auf viele Kleinigkeiten achten, auf die man vorher nicht achten musste. Aber wir leben ja nicht im Mittelalter. Wie haben Programme, welche die Checks f\u00fcr uns durchf\u00fchren. Wir m\u00fcssen sie nur benutzen.<br \/>\nEin Beispiel daf\u00fcr w\u00e4re std::transform Hier wird keine Schleife mehr explizit aufgeschrieben. Es wird nur noch gesagt WAS getan werden sollen. Das transformieren von data mit Funktion toLLL nach data2.<br \/>\nHier das komplette C++ Programm<\/p>\n<pre><code>\r\n#include &ltvector>\r\n#include &ltalgorithm>\r\n#include &ltcmath>\r\n#include &ltiostream>\r\n\r\nusing namespace std;\r\n\r\nconstexpr const double DEGRAD = M_PI\/180.0;\r\n\r\nstruct Point2D {\r\n  double x, y;\r\n};\r\n\r\nPoint2D toLLL(const Point2D& point) {\r\n  return {point.x*cos(point.y*DEGRAD), point.y};\r\n}\r\n  \r\n\r\nint main() {\r\n  vector&ltPoint2D> data(180), data2(180);\r\n  for(size_t i=0; i < 180; ++i) {\r\n    data[i].x = 180;\r\n    data[i].y = -90.0+i;\r\n  }\r\n  \r\n  transform(std::execution::par, begin(data), end(data), begin(data2), toLLL);\r\n  \r\n  for(const Point2D& p : data2) {\r\n    cout << p.x << \" \" << p.y << \"\\n\";\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>Der ersten Parameter von <em>transform<\/em> bestimmt, ob der Datensatz parallel oder sequentiell abgearbeitet werden soll. Das Einf\u00fchren der Funktion <em>toLLL<\/em> verhindert tempor\u00e4re Variablen, und die Funktion wird als Parameter \u00fcbergeben. Das generalisieren der Funktion <em>toLLL<\/em> auf einem Point Datentyp mit beliebiger Dimension und fundamentalen Type (int,float) ist nun leicht. Beispiel:<\/p>\n<pre><code>\r\ntemplate&ltclass Point>\r\nPoint toLLL(const Point& point) {\r\n  Point LLLpoint = point;\r\n  LLLpoint[0] *= cos(LLLpoint[1]*DEGRAD);\r\n  return LLLpoint;\r\n}\r\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Diesen Post m\u00f6chte ich mit einem k\u00fcrzlich aufgetretenen Problem in Fortran beginnen. In Fortran gibt es das forall keyword. Damit ist es m\u00f6glich Schleifen zu bauen, wie die do Schleife, nur dass es m\u00f6glich ist, forall Schleifen parallel abzuarbeiten. Eine Vorraussetzung daf\u00fcr ist folgende: Wenn data der Vector ist, auf den parallel gearbeitet werden soll, [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[17],"class_list":["post-2976","post","type-post","status-publish","format-standard","hentry","category-allgemein","tag-cpp"],"_links":{"self":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/2976","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2976"}],"version-history":[{"count":12,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/2976\/revisions"}],"predecessor-version":[{"id":2988,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/2976\/revisions\/2988"}],"wp:attachment":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2976"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2976"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2976"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}