17.6 C
New York
lundi, mai 29, 2023

Dein erstes – und meins – dummes Open-Cl-Programm ???? – Sonstiges – 24. April 2023


Hello .

Das Wichtigste zuerst, sehen Sie sich das folgende Video an:

Intestine, jetzt ist dies mein erstes OpenCL-Programm, daher kann es Probleme mit der Terminologie usw. geben, aber das Ziel ist, ein möglichst einfaches Beispiel zu haben, nicht nur, weil es hilfreich ist, sondern auch, weil das alles ist, was ich im Second tun kann ????.

Lass uns gehen

Wie Sie im Video gesehen haben, müssen wir einen Kontext mit einem Gerät erstellen und diesem Kontext werden wir unsere Funktion (Kernel) zuführen.

Wenn Sie C kennen, haben Sie Glück, ich behalte das nicht im Hinterkopf.

Beginnen wir additionally mit den Grundlagen, bauen das Fundament auf und wenn das funktioniert, gehen wir auch an die Berechnungen.

Beachten Sie, dass einige Tutorials Ihnen möglicherweise den Eindruck vermitteln, dass Sie mehrere Geräte einem Kontext zuweisen können, aber tatsächlich ist es ein Gerät professional Kontext.

Ich denke, Sie können jedoch mehrere gleichzeitige Kontexte haben (einen professional Gerät) -> wie ich folgere, nicht getestet, nicht vertraut.

Als erstes erstellen wir einen context . Wir stellen fest, dass das Protokoll bei erfolgreicher Erstellung den Namen des zugewiesenen Geräts erhält:

#property copyright "Lorentzos Roussos"
#property hyperlink      "https://www.mql5.com/en/customers/lorio"
#property model   "1.00"
#embody <OpenCLOpenCL.mqh>

bool busy=false,loaded=false;
int ctx=INVALID_HANDLE;
int OnInit()
  {
  ctx=INVALID_HANDLE;
  busy=false;
  loaded=false;
  EventSetMillisecondTimer(44);
  return(INIT_SUCCEEDED);
  }

void OnTimer(){
  if(!busy){
  busy=true;
  
  if(!loaded){
  EventKillTimer();
  
    ctx=CLContextCreate(CL_USE_GPU_DOUBLE_ONLY);
    if(ctx!=INVALID_HANDLE){
      Print("CL.Context Created");
      }
  }
  
  busy=false;
  }
  }



void OnDeinit(const int cause)
  {

  if(ctx!=INVALID_HANDLE){CLContextFree(ctx);} 
  }



void OnTick()
  {

   
  }

Dies ist die Struktur des Codes, den wir im Allgemeinen ausführen werden. Bei deinit geben wir den Kontext frei, wenn er gültig wäre.

Einfache Sachen bisher.

Beachten Sie auch, dass es 2 Dokumentationsseiten für OpenCL gibt, eine in der Standardbibliothek und eine für die Unterstützung nativer Befehle

Ich denke, die Standardbibliothek ist die alte, nicht sicher, nicht klar, sowieso nicht geklärt, nicht, dass irgendetwas auf dieser Seite ist, aber lassen Sie uns mit der nativen arbeiten und wenn etwas fehlt, wissen wir, wo wir suchen müssen.

Die nativen Funktionen haben additionally diesen Befehl aufgerufen Programmerstellen der einen String-Quellcode empfängt, der in „OpenCL C“ (der Sprache openCl) geschrieben ist

Der KernelCreate Der Befehl erhält jedoch das Deal with eines Programms und den Namen des Kernels, was impliziert, dass wir den gesamten Quellcode unserer Programme in die Zeichenfolge werfen, die wir bei der Erstellung des Programms übergeben, und dann die Kernel deklarieren.

Für diesen Take a look at haben wir nur eine « Funktion » (Kernel), additionally spielt es vorerst keine Rolle.

Additionally, eine einfache Funktion in OpenCL C, kay, und ich kenne C nicht, additionally wie wäre es, wenn wir ein Array und eine Zahl senden und der « Kernel » die Werte im Array mit dieser Zahl multipliziert.

Aber wie im Video betont wird, ist der beste Weg, die Leistung aus parallelen Berechnungen herauszuholen, wenn wir sie nicht linear ausführen. Stellen Sie sich das als eine Schleife mit Iterator i vor, die nicht wirklich auf den Index mit der Iterationsvariablen i zugreift, sondern mit einem anderen « Pool » von Indizes, der allen « Recheneinheiten » zur Verfügung steht.

Lasst uns anders darüber nachdenken, damit es Sinn macht. Wenn Sie selbst parallele Operationen in mql5 erstellen wollten, bräuchten Sie ein gemeinsames Protokoll darüber, welche « Aufgaben » noch verfügbar sind, und sobald eines Ihrer Diagramme eine Aufgabe beendet hat, würden sie die nächste aus diesem gemeinsamen Pool von unfertigen auswählen Aufgaben.

Die « Funktion » kann additionally wissen, welchen Index sie im Pool hat, wenn sie mit get_global_id (0) (1. Dimension) ausgeführt wird.

__kernel void biscuit(__global double *array,
                               double by,
                                  int total_items){
int idx=get_global_id(0);
if(idx>total_items){return;}
array(idx)*=by;
}

Okay, eine weitere Frage, die sich stellt, ist, ob die get_global_id bei 0 oder 1 beginnt? , das Beispiel im Programm create der Dokumentation zeigt an, dass es aus 1 besteht. Wir werden den Gedanken sehen, eine Möglichkeit besteht darin, das Array mit dem Index zu multiplizieren, ja, lass uns das tun.

Muss ich additionally typisieren? … hmmm

Additionally 3 Fragen, die beantwortet werden müssen:

  • beginnt die get_global_id(0) (und get_local_id(0) in dieser Angelegenheit) bei 1?
  • Wenn der Pool der « übrig gebliebenen » Aufgaben gleich der Anzahl der Aufgaben ist, die wir erstellen, warum muss ich beenden, wenn der Index über die Gesamtzahl der Aufgaben hinausgeht?
    Ist es nicht unmöglich, das Restrict zu überschreiten?
  • muss ich das int umwandeln, um das Array zu multiplizieren?

Lassen Sie uns dann die Funktion so ändern und herausfinden:

__kernel void biscuit(__global double *array){
int idx=get_global_id(0);
array(idx)*=idx;
}

Und lassen Sie uns das Programm mit this erstellen, wobei wir hier 3 Fehler vom openCL-Compiler erwarten.

Okay, es heißt Programm erstellt! Eindrucksvoll

      string biscuit_source_code="__kernel void biscuit(__global double *array){rn"
                                 "int idx=get_global_id(0);rn"
                                 "array(idx)*=idx;}rn";
      string build_log="";
      program_handle=CLProgramCreate(ctx,biscuit_source_code,build_log);
      if(program_handle!=INVALID_HANDLE){
        Print("Program created!");
        }else{
        Alert("Errorsn"+build_log);
        }

Dann erstelle ich die Erinnerung, nehme ich an

buffer_handle=CLBufferCreate(ctx,1000,CL_MEM_READ_WRITE);

Ich verwende viele Handles, ich könnte Deal with-Handler (und Unloader) hier erstellen, aber das ist ein Take a look at.

Dann der Kernel, wir senden hier das Programm-Deal with, additionally muss der Kernelname derselbe sein wie der im Quellcode, den wir gesendet haben.

In den Dokumenten steht « Der Title des Kernels, von dem aus die Ausführung beginnt », sodass alle « Unterfunktionen » nicht « gekernt » werden müssen. diese Frage 4 denke ich.

Okay, bisher keine Fehler, sie werden wahrscheinlich bei der Ausführung auftauchen.

Das ist, was ich bis jetzt habe

#property copyright "Lorentzos Roussos"
#property hyperlink      "https://www.mql5.com/en/customers/lorio"
#property model   "1.00"

bool busy=false,loaded=false;
int ctx=INVALID_HANDLE;
int program_handle,kernel_handle,buffer_handle;
int OnInit()
  {
  ctx=INVALID_HANDLE;
  program_handle=INVALID_HANDLE;
  kernel_handle=INVALID_HANDLE;
  buffer_handle=INVALID_HANDLE;
  busy=false;
  loaded=false;
  EventSetMillisecondTimer(44);
  return(INIT_SUCCEEDED);
  }

void OnTimer(){
  if(!busy){
  busy=true;
  
  if(!loaded){
  EventKillTimer();
  
    ResetLastError();
    ctx=CLContextCreate(CL_USE_GPU_DOUBLE_ONLY);
    if(ctx!=INVALID_HANDLE){
      ResetLastError();
      Print("CL.Context Created");
      string biscuit_source_code="__kernel void biscuit(__global double *array){rn"
                                 "int idx=get_global_id(0);rn"
                                 "array(idx)*=idx;}rn";
      string build_log="";
      program_handle=CLProgramCreate(ctx,biscuit_source_code,build_log);
      if(program_handle!=INVALID_HANDLE){
        ResetLastError();
        Print("Program created!");
        buffer_handle=CLBufferCreate(ctx,1000,CL_MEM_READ_WRITE);
        if(buffer_handle!=INVALID_HANDLE){
        ResetLastError();
        Print("buffer created");
        kernel_handle=CLKernelCreate(program_handle,"biscuit");
        if(kernel_handle!=INVALID_HANDLE){
        ResetLastError();
        Print("Kernel created");
        
        }else{Print("Can not create kernel #"+IntegerToString(GetLastError()));}
        }else{Print("Can not create buffer #"+IntegerToString(GetLastError()));}
        }else{Alert("Errors #"+IntegerToString(GetLastError())+"n"+build_log);}
      }else{Print("Can not create CL.context #"+IntegerToString(GetLastError()));}
  }
  
  busy=false;
  }
  }



void OnDeinit(const int cause)
  {

  if(kernel_handle!=INVALID_HANDLE){CLKernelFree(kernel_handle);}
  if(buffer_handle!=INVALID_HANDLE){CLBufferFree(buffer_handle);}
  if(program_handle!=INVALID_HANDLE){CLProgramFree(program_handle);}
  if(ctx!=INVALID_HANDLE){CLContextFree(ctx);} 
  
  }



void OnTick()
  {

   
  }

Jetzt muss ich die Argumente für den Kernel deklarieren

Hier gibt es 3 Varianten:

  • CLSetKernelArg
  • CLSetKernelArgMem
  • CLSetKernelArgMemLocal

Additionally, der erste – ich nehme an – ist für die Übergabe von Konstanten, wie wenn wir ein Vielfaches senden würden, wäre es mit diesem

Der zweite ist für den globalen Speicher und der dritte für den lokalen Speicher. Der lokale Speicher erhält ein Größenargument und kein Pufferhandle, sodass Speicher im Gerät lokal in den CUs zugewiesen wird.

5. Frage ist, wo ist der konstante Speicher oder wird er intern gehandhabt? wahrscheinlich

Hier habe ich additionally ein globales Array, additionally verwende ich das CLSetKernelArgMem für das 1. Argument

Okay

        if(CLSetKernelArgMem(kernel_handle,0,buffer_handle)){
        ResetLastError();
        Print("Reminiscence arg assigned to kernel");
        
        }else{Print("Can not assign reminiscence arg#"+IntegerToString(GetLastError()));}

Was jetzt ? Ich muss den Speicher füllen, ich schicke das Array nach unten, das ist praktisch.

Aber warten Sie, ich habe kein Array, verdammt noch mal. Wir testen gleichzeitig den Index (die get_global_id(0)), additionally erstellen wir ein Scheinarray mit dem Wert 1.0 für alle emelents .

Okay, ich habe hier den ersten Fehler getroffen, endlich ???? heißt es Fehler 5110

        Print("Reminiscence arg assigned to kernel");
        double arr();
        ArrayResize(arr,1000,0);
        ArrayFill(arr,0,1000,1.0);
        uint crammed=CLBufferWrite(buffer_handle,arr,0,0,1000);
        if(crammed==1000){
        Print("Crammed "+IntegerToString(crammed)+"objects in buffer");
        }else{Print("Can not fill buffer #"+IntegerToString(GetLastError()));}

Welcher Fehler ist das, mal sehen  » ERR_OPENCL_WRONG_BUFFER_SIZE » falsche Puffergröße, aber warum?

Okay, die Puffergröße beim Erstellen des Puffers bezieht sich auf Bytes, nicht auf Elemente! Intestine zu wissen, stand fairerweise in den Unterlagen. mein Fehler.

So was jetzt ? ausführen ?

Ja, okay, das ist additionally standardmäßig asynchron, denke ich, das ist Frage 6, additionally rufe ich die Ausführung auf, setze dann den Timer erneut und frage den Standing der Kernel-Ausführung ab.

Lassen Sie uns mit der standardmäßigen Ausführungsvariante fortfahren. Ich sehe kein Blockierungsflag (wie in den Movies), daher muss es standardmäßig asynchron sein.

Hier ist es :

void OnTimer(){
  if(!busy){
  busy=true;
  
  if(!loaded){
  EventKillTimer();
  
    ResetLastError();
    ctx=CLContextCreate(CL_USE_GPU_DOUBLE_ONLY);
    if(ctx!=INVALID_HANDLE){
      ResetLastError();
      Print("CL.Context Created");
      string biscuit_source_code="__kernel void biscuit(__global double *array){rn"
                                 "int idx=get_global_id(0);rn"
                                 "array(idx)*=idx;}rn";
      string build_log="";
      program_handle=CLProgramCreate(ctx,biscuit_source_code,build_log);
      if(program_handle!=INVALID_HANDLE){
        ResetLastError();
        Print("Program created!");
        buffer_handle=CLBufferCreate(ctx,1000*8,CL_MEM_READ_WRITE);
        if(buffer_handle!=INVALID_HANDLE){
        ResetLastError();
        Print("buffer created");
        kernel_handle=CLKernelCreate(program_handle,"biscuit");
        if(kernel_handle!=INVALID_HANDLE){
        ResetLastError();
        Print("Kernel created");
        if(CLSetKernelArgMem(kernel_handle,0,buffer_handle)){
        ResetLastError();
        Print("Reminiscence arg assigned to kernel");
        double arr();
        ArrayResize(arr,1000,0);
        ArrayFill(arr,0,1000,1.0);
        uint crammed=CLBufferWrite(buffer_handle,arr,0,0,1000);
        if(crammed==1000){
        ResetLastError();
        Print("Crammed "+IntegerToString(crammed)+"objects in buffer");
        
          if(CLExecute(kernel_handle)){
          Print("Executing");
          
            EventSetMillisecondTimer(44);
          
            loaded=true;
          }else{Print("Can not execute kernel #"+IntegerToString(GetLastError()));}          
        }else{Print("Can not fill buffer #"+IntegerToString(GetLastError()));}
        }else{Print("Can not assign reminiscence arg#"+IntegerToString(GetLastError()));}
        }else{Print("Can not create kernel #"+IntegerToString(GetLastError()));}
        }else{Print("Can not create buffer #"+IntegerToString(GetLastError()));}
        }else{Alert("Errors #"+IntegerToString(GetLastError())+"n"+build_log);}
      }else{Print("Can not create CL.context #"+IntegerToString(GetLastError()));}
  }
  else if(loaded){
  
    ENUM_OPENCL_EXECUTION_STATUS standing=(ENUM_OPENCL_EXECUTION_STATUS)CLExecutionStatus(kernel_handle);
    Remark("Kernel("+IntegerToString(kernel_handle)+" Standing("+EnumToString(standing)+")");
  }
  
  busy=false;
  }
  }

Das battle -offensichtlich- sehr schnell erledigt, aber was wir wollen, ist einen Blick in das Array zu werfen.

Additionally, wenn es fertig ist, lesen, drucken und sprinten (beenden) ????

    if(standing==CL_COMPLETE){
      double get();
      ArrayResize(get,1000,0);
      ArrayFill(get,0,1000,0.0);
      ResetLastError();
      if(CLBufferRead(buffer_handle,get,0,0,1000)){
      
        string msg="";
        for(int i=0;i<10;i++){
           msg+=DoubleToString(get(i),2)+"n";
           }
        Alert(msg);
      }else{Print("Can not learn buffer #"+IntegerToString(GetLastError()));}
      Print("Exit");
      ExpertRemove();
      }

uuund hier ist, was wir zurückbekommen haben, das erste Aspect ist 0 , was bedeutet, dass get_global_id(0) bei 0 beginnt? aber der Relaxation sind 1,00

Jetzt muss ich herausfinden, ob ich typisieren muss, bevor ich multipliziere, aber lasst uns die Multiplikationslinie ganz schnell auf diese ändern, ich habe eine Vermutung

array(idx)=array(idx)*idx;

Nein, additionally erstelle ich einen zweiten Puffer, ein int, und wir füllen ihn mit den Indexwerten auf, um dem auf den Grund zu gehen.

Additionally, was machen wir :

  1. Ändern Sie den Quellcode-String
  2. Puffer erstellen
  3. fügen Sie den arg-Puffer hinzu, schreiben Sie nur dieses Mal (ich nehme an, diese Aufzählungen stammen von der Geräteseite, nicht von uns)
  4. Lesen Sie den neuen int-Puffer und wenn wir 0,0,0,0,0,0 sehen, geraten wir in Panik

Beachten Sie auch, dass wir keinen Hinweis auf einen Fehler erhalten haben und wir nicht nach der Grenze des Arrays suchen, was bedeutet, dass ich etwas nicht verstanden habe, es nicht allgemein und sofort ersichtlich ist oder es keine Rolle spielt, wie die Arbeitsgruppen sind an das Gerät weitergeleitet. So viele Unbekannte.

Wie auch immer. Ich hasse diese Struktur, sodass sie mit dem zweiten Puffer noch hässlicher aussehen wird, aber es ist ein Take a look at.

hier ist der aktualisierte Code:

void OnTimer(){
  if(!busy){
  busy=true;
  
  if(!loaded){
  EventKillTimer();
  
    ResetLastError();
    ctx=CLContextCreate(CL_USE_GPU_DOUBLE_ONLY);
    if(ctx!=INVALID_HANDLE){
      ResetLastError();
      Print("CL.Context Created");
      string biscuit_source_code="__kernel void biscuit(__global double *array,__global int *idx_array){rn"
                                 "int idx=get_global_id(0);rn"
                                 "idx_array(idx)=idx;rn"
                                 "array(idx)=array(idx)*idx;}rn";
      string build_log="";
      program_handle=CLProgramCreate(ctx,biscuit_source_code,build_log);
      if(program_handle!=INVALID_HANDLE){
        ResetLastError();
        Print("Program created!");
        buffer_handle=CLBufferCreate(ctx,1000*8,CL_MEM_READ_WRITE);
        buffer_handle2=CLBufferCreate(ctx,1000*4,CL_MEM_WRITE_ONLY);
        if(buffer_handle!=INVALID_HANDLE&&buffer_handle2!=INVALID_HANDLE){
        ResetLastError();
        Print("buffer created");
        kernel_handle=CLKernelCreate(program_handle,"biscuit");
        if(kernel_handle!=INVALID_HANDLE){
        ResetLastError();
        Print("Kernel created");
        if(CLSetKernelArgMem(kernel_handle,0,buffer_handle)&&CLSetKernelArgMem(kernel_handle,1,buffer_handle2)){
        ResetLastError();
        Print("Reminiscence arg assigned to kernel");
        double arr();
        ArrayResize(arr,1000,0);
        ArrayFill(arr,0,1000,1.0);
        uint crammed=CLBufferWrite(buffer_handle,arr,0,0,1000);
        if(crammed==1000){
        ResetLastError();
        Print("Crammed "+IntegerToString(crammed)+"objects in buffer");
        
          if(CLExecute(kernel_handle)){
          Print("Executing");
          
            EventSetMillisecondTimer(44);
          
            loaded=true;
          }else{Print("Can not execute kernel #"+IntegerToString(GetLastError()));}          
        }else{Print("Can not fill buffer #"+IntegerToString(GetLastError()));}
        }else{Print("Can not assign reminiscence arg#"+IntegerToString(GetLastError()));}
        }else{Print("Can not create kernel #"+IntegerToString(GetLastError()));}
        }else{Print("Can not create buffer #"+IntegerToString(GetLastError()));}
        }else{Alert("Errors #"+IntegerToString(GetLastError())+"n"+build_log);}
      }else{Print("Can not create CL.context #"+IntegerToString(GetLastError()));}
  }
  else if(loaded){
  
    ENUM_OPENCL_EXECUTION_STATUS standing=(ENUM_OPENCL_EXECUTION_STATUS)CLExecutionStatus(kernel_handle);
    Remark("Kernel("+IntegerToString(kernel_handle)+" Standing("+EnumToString(standing)+")");
    if(standing==CL_COMPLETE){
      double get();
      ArrayResize(get,1000,0);
      ArrayFill(get,0,1000,0.0);
      int get_idx();
      ArrayResize(get_idx,1000,0);
      ArrayFill(get_idx,0,1000,-1);
      ResetLastError();
      if(CLBufferRead(buffer_handle,get,0,0,1000)&&CLBufferRead(buffer_handle2,get_idx,0,0,1000)){
      
        string msg="";
        for(int i=0;i<10;i++){
           msg+=DoubleToString(get(i),2)+"(idx:"+IntegerToString(get_idx(i))+")n";
           }
        Alert(msg);
      }else{Print("Can not learn buffer #"+IntegerToString(GetLastError()));}
      Print("Exit");
      ExpertRemove();
      }
  }
  
  busy=false;
  }
  }

Und ich bekomme 0 für alle Indexwerte … hmmm . was die Frage aufwirft, wird der erste Array-Wert multipliziert, der Relaxation dann nicht?

Führt es additionally nur das erste aus?

Okay, lasst uns ganz schnell die Limitprüfung hinzufügen.

Nein nicht.

Okay, was ist, wenn die CLExecute nur einmal ausgeführt wird?

Hmm, okay, additionally was, wenn ich einen eigenen Zähler erstelle und ihn weiter pumpe, bis er fertig ist, würde dies jedoch 44 Sekunden dauern (44 ms * 1000), additionally werde ich die Elemente auf 100 reduzieren.

Aber wie ist das parallel? Wtf . Das ist das Drawback mit der mql5-Dokumentation, die Particular person, die den Code versteht, schreibt die Dokumentation und langweilt sich, oder die Particular person, die den Code und die Dokumentation schreibt, ist anders. Im Höhepunkt versteht man etwas ganz, dann sollte es uns Bauern ausführlich erklärt werden. Warum? weil es Ihr Ökosystem schneller wachsen lässt! Stellen Sie sich vor , wenn 10000 Programmierer die Dokumentation dafür lesen , wenn ein größerer % es in kürzerer Zeit versteht , dann werden sie früher mehr Zeug erstellen . Mehr Zeug wird mehr Aktivität anziehen usw. Wir können nicht erraten, was ihr Denkprozess battle, als sie diese Instruments jedes Mal einsetzten … ich meine … jedenfalls.

Rant over, mal sehen, was sie sowieso geteilt haben, ich sehe dort auch Arrays anstelle von Integern für die Größen. Okay

Ja, okay, additionally muss es zusätzliches Lernen und eine Kristallkugel-Interaktion geben, um zu versuchen, das in Beziehung zu setzen global_work_offset() und die global_work_size() und das local_work_size() Arrays zum obigen Tutorial-Video, aber ich habe das Offset-Array mit einem Aspect (eine Dimension) auf 0 und das Arbeitsgrößen-Array mit einem Aspect (Dimension) auf 1000 gesetzt und es hat funktioniert.

Additionally beginnt get_global_id(0) bei 0 , additionally haben ihre Dokumente einen kleinen Fehler, es sei denn, ich vermisse etwas anderes – das ist auch nirgendwo dokumentiert –

Hier ist der Code

Ihre und meine öffnen zuerst cl, ich hänge es an, da es über 64k ging

Und hier ist der zweite Teil des obigen Movies, in dem OpenCL C ausführlicher behandelt wird

Fragen übrig:

  • Wenn der Pool der « übrig gebliebenen » Aufgaben gleich der Anzahl der Aufgaben ist, die wir erstellen, warum muss ich beenden, wenn der Index über die Gesamtzahl der Aufgaben hinausgeht?
    Ist es nicht unmöglich, das Restrict zu überschreiten?
  • muss ich das int umwandeln, um das Array zu multiplizieren?
  • Wo sind die konstanten Speicherfunktionen?
  • Müssen Unterfunktionen als Kernel erstellt werden?

Zweites Video.

Ich habe das so geschrieben, wie ich es durchdacht habe, additionally hoffe ich, dass es hilfreich ist.

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles