Fehler, Irrtümer, Fragen - Seite 2165

 

Ich erhalte kein Optimierungsdiagramm für negative Werte.

Die Daten sind in den Optimierungsergebnissen verfügbar.

Versuchen Sie, negative Werte in Ihren EAs einzustellen. Die Werte können * -1 sein, um zu prüfen.

 
Renat Fatkhullin:

Eine Überprüfung ergab dies:

  1. SQRT in direkte CPU-Befehle umgewandelt

  2. SQRT + mathematische Berechnungen werden ohne Verzweigungen durchgeführt und für eine Anweisung (128-Bit-Daten) werden zwei Wurzeln auf einmal berechnet

    Dieser Code wird zu folgendem SSE-Assembler-Code:
    Das ist wirklich ein Kunstwerk. 8 Wurzeln wurden in 4 Aufrufen einer Assembler-Anweisung berechnet. Zwei doppelte Zahlen werden in einem Aufruf ausgewertet.

  3. Beim Operieren durch ein Array geht alles wie gewohnt mit Prüfungen, Verzweigungen und Verlusten bei der Umwandlung von Double -> Integer-Index.

  4. Bei der Arbeit mit Arrays in diesem Beispiel kommt es zu einer ständigen Vermischung von FPU und ALU, was sehr schlecht für die Produktivität ist.

  5. Die Optimierung des dynamischen Array-Zugriffs ist großartig - mehr als lobenswert. Aber das Mischen von FPU/ALU-Operationen + Double -> Integer + Verzweigung verschwendet Zeit

Die allgemeine Schlussfolgerung: Die Mathematik hat in MQL5 dank der perfekten Optimierung gewonnen. Nicht die Arrays verlieren hier, sondern die Mathematik gewinnt.

Vielen Dank für die wertvollen Informationen.

Die Nachricht ist natürlich viel erfreulicher. Das ist wirklich cool!

Ich habe schon immer gesagt, dass MQs etwas Schönes sind!

Aber mir ist auch klar, dass man beim Mischen von Datentypen sehr vorsichtig sein muss. Aber Vorsicht ist besser als Nachsicht.
Es wäre schön, von den Entwicklern diesbezüglich einen Ratschlag zu erhalten.

Jetzt werde ich mit den Typen von normalen Variablen und Arrays experimentieren. Ich bin gespannt, was dabei herauskommen wird.

 
Renat Fatkhullin:

Ich habe ein wenig experimentiert.

Ich scheine das Puzzle immer noch nicht zusammensetzen zu können.

Ich habe zwei Varianten gemacht. Die erste - alles in den Typ int konvertiert. Die zweite - zu verdoppeln.

Ja, es ist ein bisschen schneller geworden. Aber die Hauptbremsen sind immer noch vorhanden.

Hier ist der Hauptbremsblock mit der int-Variante:

 if(arr)
        {  // расчет квадратных корней через массив значений SQRT[]
         D1=SQRT[((X1-X)*(X1-X)+(Y1-Y)*(Y1-Y))];
         D2=SQRT[((X2-X)*(X2-X)+(Y2-Y)*(Y2-Y))];
         D3=SQRT[((X3-X)*(X3-X)+(Y3-Y)*(Y3-Y))];
         D4=SQRT[((X4-X)*(X4-X)+(Y4-Y)*(Y4-Y))];
         D5=SQRT[((X5-X)*(X5-X)+(Y5-Y)*(Y5-Y))];
         D6=SQRT[((X6-X)*(X6-X)+(Y6-Y)*(Y6-Y))];
         D7=SQRT[((X7-X)*(X7-X)+(Y7-Y)*(Y7-Y))];
         D8=SQRT[((X8-X)*(X8-X)+(Y8-Y)*(Y8-Y))];
        }

Es hat nur den Typ int und keine Typenmischung. Das SQRT-Array selbst ist zu int geworden.

Er arbeitet nur 10 % schneller.

Ähnlich verhält es sich mit der doppelten Variante.

Nun, alles ist identisch - nur im ersten Fall wird die Funktion sqrt() berechnet und es gibt eine Typenvermischung.

Der zweite Fall hingegen bezieht sich auf ein int-Array, und es gibt keine Typenmischung, so dass theoretisch nur ALU verwendet werden sollte.

Aber der zweite Weg ist dreimal langsamer. Nun, was auch immer der Grund ist, es ist die Anordnung.

Es gibt noch eine weitere wichtige Sache.

Im Beispiel von int, wenn die Leinwand die Größe 100x100 hat, d.h. mit folgenden Parametern

erhalten wir einen Geschwindigkeitsvorteil beim Zugriff auf das Array.

D.h. wenn wir ein SQRT-Array der Größe 20 000 verwenden, gewinnen wir 15-20%, und wenn wir ein Array der Größe 3 000 000 verwenden, verlieren wir 200%, trotz absolut identischer Mathematik.

Die Größe des Feldes ist also die Ursache für das Bremsen?

Dateien:
LSD_double.mq5  10 kb
LSD_int.mq5  10 kb
 

Die Menschen sind schon lange nicht mehr in der Lage, die Ergebnisse moderner C++-Compiler zu verstehen.

Außerdem hat man ein Durcheinander von Code, was bedeutet, dass man fast keine Möglichkeit hat, naive Axiome zu erstellen, die besagen: "Wenn diese Bedingungen erfüllt sind, dann ist das Ergebnis dies". Das heißt, die daraus resultierende Optimierung wird alles so umgestalten, dass Ihre Hypothesen selbst bei geringfügigen Änderungen des Codes zu zig Prozent anderen Ergebnissen führen werden.

Schauen Sie sich noch einmal an, wie Sie 8 Wurzeln in 4 Assembler-Befehle packen und stellen Sie fest, dass Sie keine Chance haben, Ihre Logik zu behaupten, zu fordern oder zu appellieren. Optimierer haben lange Zeit auf einem unerschwinglichen Niveau gearbeitet, das für Programmierer unerreichbar war.

Die Art und Weise, wie der Compiler die Wurzeln zerlegt, ist eine Kunst. Und Sie versuchen, es mit Arrays zu schlagen, ohne auch nur die einfachste Einschränkung zu verstehen - das Lesen aus einem Array ist bereits ein Fehler. Perfekte Registerarbeit und Batch-Berechnung von Wurzeln vs. Verzweigungen (Strafen) und Klettern in den Speicher mit häufigen Cache-Misses.

Sie fragen, "warum es bei kleinen Puffern schneller ist und bei großen Puffern kläglich versagt", weil Sie keine Ahnung von L1/L2/L3-Caches der CPU haben. Wenn man in den Cache kam, wurde er schnell gezählt. Nicht gefangen - warten Sie ein paar Dutzend Zyklen des Lesens von Daten aus dem oberen Cache oder Speicher.
 
Renat Fatkhullin:

Die Menschen sind schon lange nicht mehr in der Lage, die Ergebnisse moderner C++-Compiler zu verstehen.

Außerdem hat man ein Durcheinander von Code, was bedeutet, dass man fast keine Möglichkeit hat, naive Axiome zu erstellen, die besagen: "Wenn diese Bedingungen erfüllt sind, dann ist das Ergebnis dies". Das heißt, die daraus resultierende Optimierung wird alles so umgestalten, dass Ihre Hypothesen selbst bei geringfügigen Änderungen des Codes zu zig Prozent anderen Ergebnissen führen werden.

Schauen Sie sich noch einmal an, wie Sie 8 Wurzeln in 4 Assembler-Befehle packen und stellen Sie fest, dass Sie keine Chance haben, Ihre Logik zu behaupten, zu fordern oder zu appellieren. Optimierer haben lange Zeit auf einem unerschwinglichen Niveau gearbeitet, das für Programmierer unerreichbar war.

Ich kann Ihre VS-Vergleichsergebnisse sehr gut sehen und bin sehr zufrieden damit.
Aber die Frage bleibt.

Ich entschuldige mich für den chaotischen Arbeitscode, aber wir sprechen nur über diesen Abschnitt des Codes und vergleichen die beiden Ausführungsoptionen:

 if(arr)
        {  // расчет квадратных корней через массив значений SQRT[]
         D1=SQRT[((X1-X)*(X1-X)+(Y1-Y)*(Y1-Y))];
         D2=SQRT[((X2-X)*(X2-X)+(Y2-Y)*(Y2-Y))];
         D3=SQRT[((X3-X)*(X3-X)+(Y3-Y)*(Y3-Y))];
         D4=SQRT[((X4-X)*(X4-X)+(Y4-Y)*(Y4-Y))];
         D5=SQRT[((X5-X)*(X5-X)+(Y5-Y)*(Y5-Y))];
         D6=SQRT[((X6-X)*(X6-X)+(Y6-Y)*(Y6-Y))];
         D7=SQRT[((X7-X)*(X7-X)+(Y7-Y)*(Y7-Y))];
         D8=SQRT[((X8-X)*(X8-X)+(Y8-Y)*(Y8-Y))];
        }
 else // расчет квадратных корней через функцию кв. корня sqrt()
        {
         D1=(int)sqrt((X1-X)*(X1-X)+(Y1-Y)*(Y1-Y));
         D2=(int)sqrt((X2-X)*(X2-X)+(Y2-Y)*(Y2-Y));
         D3=(int)sqrt((X3-X)*(X3-X)+(Y3-Y)*(Y3-Y));
         D4=(int)sqrt((X4-X)*(X4-X)+(Y4-Y)*(Y4-Y));
         D5=(int)sqrt((X5-X)*(X5-X)+(Y5-Y)*(Y5-Y));
         D6=(int)sqrt((X6-X)*(X6-X)+(Y6-Y)*(Y6-Y));
         D7=(int)sqrt((X7-X)*(X7-X)+(Y7-Y)*(Y7-Y));
         D8=(int)sqrt((X8-X)*(X8-X)+(Y8-Y)*(Y8-Y));
        }

Hier gibt es keinen Müll.

Sie sagten: "Die Optimierung des dynamischen Array-Zugriffs ist hervorragend, mehr als lobenswert."

Aber... siehe meine vorherige Nachricht.

Wie erklären Sie sich mein letztes Experiment?

"Das heißt, wenn wir ein SQRT-Array der Größe 20.000 verwenden, haben wir einen Gewinn von 15-20%, und wenn wir ein Array der Größe 3.000.000 verwenden, verlieren wir 200% mit genau der gleichen Mathematik.

Die Größe des Feldes ist also die Ursache für die Bremsen?"

 

Lesen Sie meine vorherige Antwort sorgfältig durch - sie ist vollständig und enthält eine genaue Antwort.

Ich erkläre Ihnen Ihre Fragen ganz einfach: Lesen Sie fünf technische Artikel über die Entwicklung von Prozessoren im Hinblick auf ihre Leistung und die Faktoren, die sie beeinflussen. Ohne das kann man keine Diskussion führen, denn man muss die Grundlagen erklären.

 
Renat Fatkhullin:

Die Art und Weise, wie der Compiler die Wurzeln zerlegt hat, ist eine Kunst. Und Sie versuchen, es mit Arrays zu schlagen, ohne auch nur die einfachste Einschränkung zu verstehen - das Lesen aus einem Array ist bereits ein Fehler. Perfekte Registerarbeit und Batch-Berechnung von Wurzeln vs. Verzweigungen (Strafen) und Klettern in den Speicher mit häufigen Cache-Misses.

Sie fragen: "Warum ist er bei einem kleinen Puffer schneller und versagt bei einem großen ohrenbetäubend?", weil Sie keine Ahnung von L1/L2/L3-Caches von Prozessoren haben. Wenn man in den Cache kam, wurde er schnell gezählt. Wenn Sie es nicht haben, müssen Sie einige Dutzend Zyklen warten, um Daten aus dem oberen Cache oder Speicher zu lesen.
Renat Fatkhullin:

Lesen Sie meine vorherige Antwort aufmerksam - sie ist mit einer genauen Antwort abgeschlossen.

Lassen Sie mich Ihre Fragen ganz einfach erklären: Lesen Sie fünf technische Artikel über die Entwicklung von Prozessoren im Hinblick auf ihre Leistung und die Faktoren, die sie beeinflussen, mit Bedacht. Ohne das kann man keine Diskussion führen, denn man muss grundlegende Dinge erklären.

Juhu!!!
Endlich!
Du, Renat, solltest für alles gekniffen werden.

Für mich ist das Bild jetzt klarer.

Ich hatte Unrecht, als ich Ihrem Compiler die Schuld gab. Es tut mir leid, ich habe mich geirrt. Ich hätte vermuten können, dass der Grund dafür in den begrenzten Caches des Prozessors liegt. Ich kenne mich wirklich schlecht mit modernen Prozessoren aus, und ich muss unbedingt darüber lesen.

Trotzdem habe ich nicht umsonst diesen Code geschrieben - Laborratte - und diese Welle gemacht.

Für die Programmierer, die diesen Thread lesen, werde ich also zusammenfassen, was ich persönlich als Ergebnis dieser Welle herausgefunden habe:

  • Die Funktion sqrt() und höchstwahrscheinlich viele andere elementare Funktionen sind sehr schnell und werden nicht auf der Compiler-Ebene, sondern auf der CPU-Ebene ausgeführt.
  • Der MQL5-Compiler ist bei der Optimierung der mathematischen Logik so stark, dass er den modernen VS C++-Compiler leicht übertrifft. Das ist sehr inspirierend.
  • Es ist vernünftig, bei ressourcenintensiven Aufgaben zu versuchen, die Typen nicht zu mischen. Das Mischen von Typen führt zu einer geringeren Berechnungsgeschwindigkeit.
  • DIE GRÖSSE ZÄHLT! (Ich meine die Größe des Arrays :)) wegen der Besonderheiten der Arbeit des mehrstufigen Caches des Prozessors und seiner begrenzten Größe. Programmierer tun gut daran, auf die Gesamtgröße von Arrays zu achten und zu verstehen, dass die Verwendung großer Arrays die Geschwindigkeit von Berechnungen erheblich beeinträchtigen kann. Soweit ich verstanden habe, ist der Punkt über relativ komfortable Arbeit von Arrays mit Gesamtgröße nicht mehr als etwa 512kB, und es ist ~65000 Elemente des Typs double oder ~130000 von int ..,

Ich habe meinen Code auf der Grundlage dieser Informationen korrigiert. Ich habe oft die Größe von Arrays missbraucht.

Ich danke Ihnen allen!

 

Woher weiß ich, ob die Fadenkreuztaste gedrückt oder losgelassen ist?

Sie können das Drücken des Mausrads auffangen, aber wie können Sie das tun, wenn die Maus nicht benutzt wird?

 
Alexandr Bryzgalov:

Woher weiß ich, ob die Fadenkreuztaste gedrückt oder losgelassen ist?

Sie können das Klicken des Mausrads auffangen, aber was ist, wenn die Maus nicht in Gebrauch ist?

Wie wäre es, sie zu erzwingen oder bei Bedarf zu verschieben?

DIAGRAMM_FADENKREUZ_WERKZEUG

Aktivieren/Deaktivieren des Zugriffs auf das "Fadenkreuz"-Werkzeug durch Drücken der mittleren Maustaste

bool (Standardwert true)

 
Alexey Viktorov:

Kann sie notfalls erzwungen oder zurückgedrängt werden?

DIAGRAMM_FADENKREUZ_WERKZEUG

Aktivieren/Deaktivieren des Zugriffs auf das "Fadenkreuz"-Werkzeug durch Drücken der mittleren Maustaste

bool (standardmäßig true)

Soweit ich weiß, wird damit nur das Werkzeug aufgerufen, aber nicht ausgeschaltet.

Grund der Beschwerde: