Erreurs, bugs, questions - page 2165

 
Renat Fatkhullin:

Une vérification a révélé que :

  1. SQRT mappé dans les instructions directes du CPU

  2. SQRT + les calculs mathématiques sont effectués sans branchements et pour une instruction (données de 128 bits) deux racines sont calculées en même temps

    Ce code se transforme en code assembleur SSE suivant :
    C'est une œuvre d'art en fait. 8 racines ont été calculées en 4 appels d'une instruction assembleur. Deux nombres doubles sont évalués en un seul appel.

  3. Lorsque l'on opère dans un tableau, tout se passe comme d'habitude avec des vérifications, des branchements et des pertes lors de la conversion de l'index double -> entier.

  4. En travaillant avec des tableaux dans cet exemple, il y a un mélange constant de FPU/ALU, ce qui est très mauvais pour la productivité.

  5. L'optimisation de l'accès dynamique aux tableaux est formidable - au-delà des éloges. Mais mélanger les opérations FPU/ALU + double -> entier + branchement fait perdre du temps.

La conclusion générale : les mathématiques ont gagné dans MQL5 grâce à une optimisation parfaite. Ce ne sont pas les tableaux qui perdent ici, mais les mathématiques qui gagnent.

Merci beaucoup pour ces informations précieuses.

Les nouvelles sont, bien sûr, plus joyeuses. C'est vraiment cool !

J'ai toujours dit que les MQs sont des beautés !

Mais je réalise aussi qu'il faut être très prudent avec le mélange des types de données. Mais être prévenu, c'est être armé.
Il serait bon d'obtenir des conseils des développeurs à ce sujet.

Maintenant, je vais expérimenter avec les types de variables ordinaires et de tableaux. Je me demande ce qui va sortir.

 
Renat Fatkhullin:

J'ai fait des expériences.

Je n'arrive toujours pas à reconstituer le puzzle.

J'ai réalisé deux variantes. La première - a converti tout en type int. La deuxième - pour doubler.

Oui, il est devenu un peu plus rapide. Mais les freins principaux sont tout de même présents.

Voici le bloc de freinage principal avec la variante int :

 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))];
        }

Il n'a que le type int et aucun mélange de types. Le tableau SQRT lui-même est devenu int.

Il ne fonctionne que 10% plus vite.

La situation est similaire avec la double variante.

Tout est identique, sauf que dans le premier cas, c'est la fonction sqrt() qui est calculée et il y a un mélange de types.

Alors que le second cas fait référence à un tableau d'int et il n'y a pas de mélange de types, et en théorie, seule l'ALU devrait être utilisée.

Mais la deuxième façon est 3 fois plus lente. Eh bien, quelle que soit la raison, c'est le tableau.

Il y a encore une chose importante.

Dans l'exemple de l'int, si le canevas a une taille de 100x100, c'est-à-dire avec les paramètres suivants

nous obtenons un avantage de vitesse lors de l'accès au tableau.

Par exemple, en utilisant un tableau SQRT de taille 20 000, nous gagnons 15-20%, et en utilisant un tableau de taille 3 000 000, nous perdons 200%, malgré des mathématiques absolument identiques.

La taille du tableau est donc la cause des freins ?

Dossiers :
LSD_double.mq5  10 kb
LSD_int.mq5  10 kb
 

Il y a longtemps que les gens ont perdu la capacité de comprendre les résultats des compilateurs C++ modernes.

En outre, vous avez un fouillis de code, ce qui signifie une possibilité quasi nulle de construire des axiomes naïfs "si ces conditions sont réunies, alors le résultat sera ceci". En d'autres termes, l'optimisation résultante réorganisera tout à tel point que vos hypothèses produiront des résultats différents de plusieurs dizaines de pour cent, même avec des modifications minimes du code.

Jetez un autre coup d'œil à l'entassement de 8 racines dans 4 commandes d'assembleur et réalisez que vous n'avez pas la possibilité d'affirmer, d'exiger ou de faire appel à votre logique. Les optimiseurs ont longtemps fonctionné à des niveaux prohibitifs, hors de portée des programmeurs.

La façon dont le compilateur décompose les racines est un art. Et vous essayez de le battre avec des tableaux sans même comprendre la contrainte la plus simple - la lecture d'un tableau est déjà un échec. Travail parfait sur les registres et calcul par lots des racines par rapport aux ramifications (pénalités) et montée en mémoire avec des manques fréquents de cache.

Vous demandez "pourquoi c'est plus rapide sur un petit tampon et échoue misérablement sur un grand" parce que vous ne savez rien du tout des caches L1/L2/L3 du processeur. Si vous avez été mis en cachette, il a été compté rapidement. Non détecté - attendez quelques dizaines de cycles de lecture de données à partir du cache supérieur ou de la mémoire.
 
Renat Fatkhullin:

Il y a longtemps que les gens ont perdu la capacité de comprendre les résultats des compilateurs C++ modernes.

En outre, vous avez un fouillis de code, ce qui signifie qu'il n'y a pratiquement aucune possibilité de construire des axiomes naïfs "si ces conditions sont réunies, alors le résultat sera ceci". En d'autres termes, l'optimisation finale réorganisera tout à tel point que vos hypothèses produiront des résultats différents de dizaines de pour cent, même avec de minuscules changements dans le code.

Jetez un autre coup d'œil à l'entassement de 8 racines dans 4 commandes d'assembleur et réalisez que vous n'avez pas la possibilité d'affirmer, d'exiger ou de faire appel à votre logique. Les optimiseurs ont longtemps fonctionné à des niveaux prohibitifs, hors de portée des programmeurs.

Je vois parfaitement vos résultats par rapport au VS et j'en suis ravi.
Mais la question demeure.

Je m'excuse pour ce code de travail chaotique, mais nous ne parlons que de cette section de code et comparons les deux options d'exécution :

 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));
        }

Il n'y a pas de déchets ici.

Vous avez dit que"l'optimisation de l'accès aux tableaux dynamiques est excellente, au-delà de toute louange".

Mais... voir mon message précédent.

Comment expliquez-vous ma dernière expérience ?

"C'est-à-dire que lorsque nous utilisons un tableau SQRT de taille 20 000, nous avons un gain de 15-20%, et lorsque nous utilisons un tableau de taille 3 000 000, nous perdons 200% avec exactement le même calcul.

Donc la taille du tableau est la cause des freins ?"

 

Lisez attentivement ma réponse précédente - elle est complète et contient une réponse exacte.

Je vais expliquer vos questions simplement : lisez cinq articles techniques de manière réfléchie sur la conception des processeurs en termes de performances et de facteurs les affectant. Vous ne pouvez pas avoir de discussion sans cela, car vous devez expliquer les bases.

 
Renat Fatkhullin:

La façon dont le compilateur a décomposé les racines est un art. Et vous essayez de le battre avec des tableaux sans même comprendre la contrainte la plus simple - la lecture d'un tableau est déjà un échec. Travail parfait sur les registres et calcul par lots des racines par rapport aux ramifications (pénalités) et montée en mémoire avec des manques fréquents de cache.

Vous demandez "pourquoi c'est plus rapide sur un petit tampon et échoue de façon assourdissante sur un grand" parce que vous ne connaissez pas du tout les caches L1/L2/L3 des processeurs. Si vous avez été mis en cachette, il a été compté rapidement. Si vous ne l'avez pas, vous devrez attendre quelques dizaines de cycles de lecture de données depuis le cache supérieur ou la mémoire.
Renat Fatkhullin:

Lisez attentivement ma réponse précédente - elle est terminée par une réponse exacte.

Permettez-moi d'expliquer vos questions simplement : lisez cinq articles techniques de manière réfléchie sur la conception des processeurs en termes de performances et de facteurs les affectant. Vous ne pouvez pas avoir de discussion sans cela, car vous devez expliquer des choses fondamentales.

Yay !!!
Enfin !
Vous, Renat, devriez être pincé pour tout.

Le tableau est maintenant plus clair pour moi.

J'avais tort quand je blâmais votre compilateur. Je suis désolé, j'avais tort. J'aurais pu deviner que la raison était dans les caches limités du processeur. Je suis vraiment mauvais en processeurs modernes et j'ai vraiment besoin de lire à ce sujet.

Pourtant, ce n'est pas pour rien que j'ai écrit ce code - rat de laboratoire - et fait cette vague.

Ainsi, pour les programmeurs qui lisent ce fil, je vais résumer ce que j'ai personnellement découvert à la suite de cette vague :

  • La fonction sqrt() et très probablement de nombreuses autres fonctions élémentaires sont très rapides et sont exécutées non pas au niveau du compilateur, mais au niveau du CPU.
  • Le compilateur MQL5 est si fort en matière d'optimisation de la logique mathématique qu'il bat facilement le compilateur moderne VS C++. Ce qui est très inspirant.
  • Il est raisonnable d'essayer de ne pas mélanger les types dans les tâches à forte intensité de ressources. Le mélange des types entraîne une baisse de la vitesse de calcul.
  • LA TAILLE COMPTE ! (je veux dire la taille du tableau :)) en raison des particularités du fonctionnement du cache multiniveau du processeur et de sa taille limitée. Les programmeurs feraient bien de surveiller la taille totale des tableaux et de comprendre que l'utilisation de grands tableaux peut influencer de manière significative la vitesse des calculs. D'après ce que j'ai compris, nous parlons d'un travail relativement confortable sur des tableaux dont la taille totale ne dépasse pas environ 512kB, et il s'agit de ~65000 éléments de type double ou ~130000 de type int..,

Je suis allé corriger mon code en me basant sur cette information. J'abuse souvent de la taille des tableaux.

Merci à tous !

 

Comment puis-je savoir si le bouton de la croix est enfoncé ou relâché ?

vous pouvez attraper lorsque la molette de la souris est pressée, mais si la souris n'est pas utilisée, comment pouvez-vous le faire ?

 
Alexandr Bryzgalov:

Comment puis-je savoir si le bouton de la croix est enfoncé ou relâché ?

Vous pouvez attraper la molette de la souris en cliquant, mais si la souris n'est pas utilisée, qu'en est-il ?

Pourquoi ne pas le forcer ou le repousser quand c'est nécessaire ?

OUTIL_CROSSHAIR_CARTE

Activer/désactiver l'accès à l'outil "réticule" en appuyant sur le bouton central de la souris.

bool (valeur par défaut true)

 
Alexey Viktorov:

Peut-on le forcer ou le repousser si nécessaire ?

OUTIL_CROSSHAIR_CARTE

Activer/désactiver l'accès à l'outil "réticule" en appuyant sur le bouton central de la souris.

bool (vrai par défaut)

D'après ce que j'ai compris, cela permet seulement d'accéder à l'outil, mais pas de le désactiver.

 

Par exemple, si vous définissez cette propriété à false, le réticule ne sera pas visible sur le graphique lorsque vous cliquez sur la roue,

mais lorsque vous cliquez sur le bouton réticule de la barre d'outils, le réticule apparaît sur le graphique.

Question, est-ce que la souris et le bouton allument deux réticules différents ? )

ZS : et lorsque vous appuyez sur Ctrl+F et que la propriétéCHART_CROSSHAIR_TOOL est désactivée, le réticule apparaît également.

Je me demande ce qu'il désactive alors ?