Errores, fallos, preguntas - página 2165

 

No obtengo un gráfico de optimización para los valores negativos.

Los datos están disponibles en los resultados de la optimización.

Intente establecer valores negativos en sus EAs. Los valores pueden ser * -1 para comprobar.

 
Renat Fatkhullin:

Una comprobación reveló que:

  1. SQRT mapeado en instrucciones directas de la CPU

  2. SQRT + los cálculos matemáticos se realizan sin ramas y para una instrucción (datos de 128 bits) se calculan dos raíces a la vez

    Este código se convierte en el siguiente código SSE en ensamblador:
    Esto es una obra de arte en realidad. Se calcularon 8 raíces en 4 llamadas de una instrucción de ensamblador. Se evalúan dos números dobles en una sola llamada.

  3. Cuando se opera a través de un array, todo va como de costumbre con comprobaciones, bifurcaciones y pérdidas al convertir el índice doble -> entero.

  4. Al trabajar con arrays en este ejemplo hay una mezcla constante de FPU/ALU que es muy mala para la productividad

  5. La optimización del acceso a las matrices dinámicas es genial, más allá de los elogios. Pero la mezcla de operaciones FPU/ALU + doble -> entero + bifurcación hace perder tiempo

La conclusión general: las matemáticas han ganado en MQL5 debido a la perfecta optimización. Aquí no pierden las matrices, sino que ganan las matemáticas.

Muchas gracias por la valiosa información.

La noticia es, por supuesto, más alegre. Es realmente genial.

Siempre he dicho que los MQ son una belleza.

Pero también soy consciente de que hay que tener mucho cuidado con la mezcla de tipos de datos. Pero quien está prevenido está prevenido.
Sería estupendo recibir algún consejo de los desarrolladores en este sentido.

Ahora experimentaré con los tipos de las variables ordinarias y de las matrices. Me pregunto qué saldrá.

 
Renat Fatkhullin:

He estado experimentando.

Parece que todavía no soy capaz de armar el rompecabezas.

He hecho dos variantes. El primero - convirtió todo a tipo int. El segundo - para doblar.

Sí, se hizo un poco más rápido. Pero de todos modos los frenos principales siguen presentes.

Aquí está el bloque principal de frenado con 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))];
        }

Sólo tiene tipo int y no tiene mezcla de tipos. La propia matriz SQRT se ha convertido en int.

Sólo funciona un 10% más rápido.

La situación con la doble variante es similar.

Bueno, todo es idéntico, sólo que en el primer caso se calcula la función sqrt() y hay mezcla de tipos.

Mientras que el segundo caso se refiere a un array de int y no hay mezcla de tipos, y en teoría sólo se debería utilizar ALU.

Pero la segunda forma es 3 veces más lenta. Bueno, sea cual sea la razón, es la matriz.

Hay una cosa más importante.

En el ejemplo int, si el lienzo tiene un tamaño de 100x100, es decir, con los siguientes parámetros

obtenemos una ventaja de velocidad al acceder a la matriz.

Por ejemplo, al utilizar una matriz SQRT de tamaño 20 000, ganamos un 15-20%, y al utilizar una matriz de tamaño 3 000 000, perdemos un 200%, a pesar de que las matemáticas son absolutamente idénticas.

¿Así que el tamaño de la matriz es la causa de los frenos?

Archivos adjuntos:
LSD_double.mq5  10 kb
LSD_int.mq5  10 kb
 

Hace tiempo que la gente perdió la capacidad de entender los resultados de los compiladores modernos de C++.

Además, tienes un lío de código, lo que significa casi cero posibilidad de construir axiomas ingenuos "si estas condiciones, entonces el resultado será esto". Es decir, la optimización resultante lo reordenará todo tanto que sus hipótesis producirán decenas de resultados diferentes incluso con cambios minúsculos en el código.

Vuelve a ver cómo meter 8 raíces en 4 comandos de ensamblador y date cuenta de que no tienes posibilidad de afirmar, exigir o apelar a tu lógica. Los optimizadores han funcionado durante mucho tiempo a niveles prohibitivos, fuera del alcance de los programadores.

La forma en que el compilador descompone las raíces es un arte. Y estás tratando de ganarle a los arrays sin siquiera entender la restricción más simple: leer de un array ya es una falla. Trabajo perfecto de los registros y cálculo por lotes de las raíces frente a las bifurcaciones (penalizaciones) y la subida a la memoria con frecuentes pérdidas de caché.

Estás preguntando "por qué es más rápido en un buffer pequeño y falla estrepitosamente en uno grande" porque no sabes nada de las cachés L1/L2/L3 del procesador. Si se entraba en el caché, se contaba rápidamente. No se coge - espera un par de docenas de ciclos de lectura de datos de la caché superior o de la memoria.
 
Renat Fatkhullin:

Hace tiempo que la gente perdió la capacidad de entender los resultados de los compiladores modernos de C++.

Además, tienes un lío de código, lo que significa casi cero posibilidad de construir axiomas ingenuos "si estas condiciones, entonces el resultado será esto". Es decir, la optimización resultante lo reordenará todo tanto que sus hipótesis producirán decenas de resultados diferentes incluso con cambios minúsculos en el código.

Vuelve a ver cómo meter 8 raíces en 4 comandos de ensamblador y date cuenta de que no tienes posibilidad de afirmar, exigir o apelar a tu lógica. Los optimizadores han funcionado durante mucho tiempo a niveles prohibitivos, fuera del alcance de los programadores.

Veo perfectamente los resultados de su comparación VS y estoy encantado con ella.
Pero la pregunta sigue siendo.

Pido disculpas por el caótico código de trabajo, pero sólo estamos hablando de esta sección de código y comparando las dos opciones de ejecución:

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

Aquí no hay basura.

Has dicho que"la optimización delacceso a la matriz dinámica es excelente, más allá de los elogios".

Pero... ver mi mensaje anterior.

¿Cómo explicas mi último experimento?

"Es decir, cuando usamos una matriz SQRT de tamaño 20.000, estamos en una ganancia del 15-20%, y cuando usamos una matriz de tamaño 3.000.000, perdemos el 200% con exactamente las mismas matemáticas.

¿Así que el tamaño de la matriz es la causa de los frenos?"

 

Lea atentamente mi respuesta anterior: está completa con una respuesta exacta.

Te explicaré tus dudas de forma sencilla: lee cinco artículos técnicos de forma reflexiva sobre el diseño de los procesadores en cuanto a su rendimiento y los factores que lo afectan. No se puede tener una discusión sin eso, ya que hay que explicar lo básico.

 
Renat Fatkhullin:

La forma en que el compilador ha descompuesto las raíces es un arte. Y tratas de vencerlo con arrays sin entender siquiera la restricción más sencilla: leer de un array ya es un fallo. Trabajo perfecto de los registros y cálculo por lotes de las raíces frente a las bifurcaciones (penalizaciones) y la subida a la memoria con frecuentes pérdidas de caché.

Estás preguntando "por qué es más rápido en un buffer pequeño y falla estrepitosamente en uno grande" porque no sabes nada de las cachés L1/L2/L3 del procesador. Si se entraba en el caché, se contaba rápidamente. Si no lo tiene, tendrá que esperar un par de decenas de ciclos de lectura de datos de la caché superior o de la memoria.
Renat Fatkhullin:

Lee atentamente mi respuesta anterior: está terminada con una respuesta exacta.

Permíteme explicar tus preguntas de forma sencilla: lee cinco artículos técnicos de forma reflexiva sobre el diseño de los procesadores en cuanto a su rendimiento y los factores que lo afectan. No se puede tener una discusión sin eso, ya que hay que explicar cosas básicas.

¡¡¡Sí!!!
¡Por fin!
Tú, Renat, deberías ser pellizcado por todo.

El panorama es ahora más claro para mí.

Me equivoqué al culpar a su compilador. Lo siento, me equivoqué. Podría haber adivinado que la razón estaba en las limitadas memorias caché del procesador. Soy muy malo con los procesadores modernos y realmente necesito leer sobre ellos.

Aun así, no es por nada que escribí este código -rata de laboratorio- e hice esta ola.

Así que, para aquellos programadores que lean este hilo, resumiré lo que personalmente he descubierto como resultado de esta ola:

  • La función sqrt() y probablemente muchas otras funciones elementales son muy rápidas y no se ejecutan a nivel del compilador, sino de la CPU.
  • El compilador MQL5 es tan fuerte en la optimización de la lógica matemática que supera fácilmente al moderno compilador VS C++. Lo cual es muy inspirador.
  • Es razonable tratar de no mezclar tipos en las tareas que requieren muchos recursos. La mezcla de tipos conduce a una menor velocidad de cálculo.
  • ¡EL TAMAÑO IMPORTA! (me refiero al tamaño del array :)) debido a las peculiaridades del trabajo de la caché multinivel del procesador y su tamaño limitado. Y los programadores harían bien en vigilar el tamaño total de las matrices y comprender que el uso de matrices grandes puede influir significativamente en la velocidad de los cálculos. Por lo que he entendido, estamos hablando de un trabajo relativamente cómodo de arrays con un tamaño total que no supera unos 512kB, y son ~65000 elementos de tipo double o ~130000 de int..,

Fui a arreglar mi código basándome en esta información. A menudo he abusado del tamaño de las matrices.

Gracias a todos.

 

¿cómo puedo saber si el botón del retículo está pulsado o liberado?

se puede capturar cuando se pulsa la rueda del ratón, pero si el ratón no está en uso ¿cómo se puede hacer esto?

 
Alexandr Bryzgalov:

¿cómo puedo saber si el botón del retículo está pulsado o liberado?

Puedes coger la rueda del ratón haciendo clic, pero si el ratón no está en uso, ¿qué pasa con él?

¿Qué tal si lo forzamos o lo hacemos retroceder cuando sea necesario?

HERRAMIENTA_DE_TRABAJO_CRUZADO

Activar/desactivar el acceso a la herramienta "retícula" pulsando el botón central del ratón

bool (valor por defecto true)

 
Alexey Viktorov:

¿Se puede forzar o retrasar si es necesario?

HERRAMIENTA_DE_TRABAJO_CRUZADO

Activar/desactivar el acceso a la herramienta "crosshair" pulsando el botón central del ratón

bool (verdadero por defecto)

Por lo que tengo entendido sólo accede a la herramienta, pero no la apaga.