错误、漏洞、问题 - 页 2165

 

我没有得到负值的优化图。

这些数据可以在优化结果中找到。

尝试在你的EA中设置负值。值可以是*-1来检查。

 
Renat Fatkhullin:

经检查发现。

  1. SQRT被直接映射到CPU指令中

  2. SQRT+数学计算 是在没有分支的情况下进行的,对于一条指令(128位数据),可以一次计算两个根

    这段代码变成了以下汇编的SSE代码。
    这实际上是一件艺术作品。一个汇编指令的4次调用就能计算出8个根。两个双数在一次调用中被评估。

  3. 当通过一个数组进行操作时,一切都像往常一样,检查、分支和转换双数->整数索引的损失。

  4. 在这个例子中处理数组时,不断有FPU/ALU混合,这对生产力非常不利。

  5. 对动态数组访问的优化非常好--无可非议。但混合FPU/ALU操作+双数->整数+分支的方式会浪费时间

总的结论是:由于完美的优化,数学在MQL5中获胜。这里输的不是数组,而是数学赢了。

非常感谢您提供的宝贵信息。

当然,这个消息更令人高兴。这真的很酷!

我总是说,MQs是一种美。

但我也意识到,对于数据类型的混合,人们必须非常小心。但是,未雨绸缪就是先入为主。
如果能从开发商那里得到一些这方面的建议,那就太好了。

现在我将对普通变量和数组的类型进行实验。我想知道会有什么结果。

 
Renat Fatkhullin:

我一直在做实验。

我似乎仍然无法把拼图拼起来。

我已经做了两个变体。第一种--把所有东西都转换为int类型。第二条--要翻倍。

是的,它变得有点快。但主要的缺点仍然存在。

这里是带有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))];
        }

它只有int类型,没有类型混合。SQRT数组本身已经变成了int。

它的工作速度只有10%。

双重变体的情况也类似。

嗯,一切都相同--只是在第一种情况下,计算的是sqrt()函数,并且有类型混合。

而第二种情况指的是一个int数组,没有类型混合,理论上应该只使用ALU。

但第二种方式要慢3倍。好吧,不管是什么原因,这就是阵列。

还有一件重要的事。

在int的例子中,如果画布的尺寸是100x100,即有以下参数

我们在访问数组时获得了速度优势。

例如,当使用大小为20 000的SQRT数组时,我们获得15-20%的收益,而当使用大小为3 000 000的数组时,我们损失200%,尽管数学上绝对相同。

所以阵列的大小是导致刹车的原因?

附加的文件:
LSD_double.mq5  10 kb
LSD_int.mq5  10 kb
 

人们早就失去了理解现代C++编译器结果的能力。

此外,你有一堆乱七八糟的代码,这意味着建立天真的公理 "如果这些条件,那么结果将是这样 "的可能性几乎为零。也就是说,最终的优化将重新安排一切,以至于你的假设将产生百分之几十的不同结果,即使代码中的微小变化。

再看看把8个根塞进4个汇编命令,就会发现你没有机会断言、要求或呼吁你的逻辑。长期以来,优化器在程序员无法企及的水平上运行。

编译器分解根的方式是一种艺术。而你试图用数组来打败它,甚至没有理解最简单的约束--从数组中读取已经是一种失败。完美的寄存器工作和批量计算的根与分支(惩罚)和爬到内存的频繁缓存错过。

你问 "为什么它在小缓冲区上更快,而在大缓冲区上却惨遭失败",因为你对处理器的L1/L2/L3缓存一无所知。如果你进入了缓存,很快就会被计算在内。没有抓到--等待几十个周期从上层缓存或内存中读取数据。
 
Renat Fatkhullin:

人们早就失去了理解现代C++编译器结果的能力。

此外,你有一堆乱七八糟的代码,这意味着建立天真的公理 "如果这些条件,那么结果将是这样 "的可能性几乎为零。也就是说,最终的优化将重新安排一切,以至于你的假设将产生百分之几十的不同结果,即使代码中的微小变化。

再看看把8个根塞进4个汇编命令,就会发现你没有机会断言、要求或呼吁你的逻辑。长期以来,优化器在程序员无法企及的水平上运行。

我完全可以看到你与VS相比的结果,我对此感到很高兴。
但问题仍然存在。

我对混乱的工作代码表示歉意,但我们只是在讨论这一段代码,并比较两种执行方案。

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

这里没有垃圾。

你说" 动态数组 访问的优化非常好,无可挑剔"。

但是...见我之前的留言。

你如何解释我的最后一个实验?

"也就是说,当我们使用一个大小为20,000的SQRT数组时,我们的收益为15-20%,而当我们使用一个大小为3,000,000的数组时,在完全相同的数学运算下,我们会损失200%。

所以,阵列的大小是刹车的原因?"

 

仔细阅读我之前的回复--它是完整的,有一个确切的答案。

我将简单地解释你的问题:从性能和影响因素的角度,深思熟虑地阅读五篇关于处理器设计的技术文章。没有这些你就无法进行讨论,因为你需要解释基础知识。

 
Renat Fatkhullin:

编译器对根的分解方式是一种艺术。而你想用数组来打败它,却连最简单的约束都不明白--从数组中读取已经是一种失败。完美的寄存器工作和批量计算的根与分支(惩罚)和爬到内存的频繁缓存错过。

你问的是 "为什么在小的缓冲区上速度更快,而在大的缓冲区上却振聋发聩",因为你根本不了解处理器的L1/L2/L3缓冲区的情况。如果你进入了缓存,很快就会被计算在内。如果你没有得到它,你将不得不等待几十个周期,从上层缓存或内存中读取数据。
雷纳特-法特库林

仔细阅读我之前的回复--它已经完成了一个确切的答案。

让我简单解释一下你的问题:在性能和影响因素方面,深思熟虑地阅读五篇关于处理器设计的技术文章。没有这些,你就无法进行讨论,因为你需要解释基本的东西。

耶!!!。
终于来了!
你,雷纳特,应该为所有的事情被掐死。

对我来说,现在的情况更清楚了。

我在责备你的编译器时是错的。我很抱歉,我错了。我可能已经猜到,原因在于处理器的有限缓存。我在现代处理器方面真的很差,我真的需要读一读。

不过,我写下这段代码--小白鼠--并掀起这股浪潮,也不是没有原因的。

因此,对于那些正在阅读这个主题的程序员,我将总结一下我个人在这一波中所发现的问题。

  • sqrt()函数和最有可能的许多其他基本函数都非常快,而且不是在编译器层面执行,而是在CPU层面执行。
  • MQL5编译器在优化数学逻辑方面非常强大,可以轻松地击败现代VS C++编译器。这是非常鼓舞人心的。
  • 在资源密集型的任务中,尽量不要混合类型是合理的。混合类型会导致计算速度降低。
  • 尺寸的重要性!( 我指的是阵列的大小:))因为处理器的多级缓存 工作的特殊性和它的有限大小。而程序员最好注意数组的总大小,了解使用大数组可能会大大影响计算的速度。据我所知,重点是关于总大小不超过512kB的数组的相对舒适的工作,它是~65000个双倍类型的元素或~130000个int类型的元素。

我根据这些信息去修改我的代码。我经常滥用数组的大小。

谢谢大家!

 

我怎么知道十字准星按钮是被按下还是被释放了?

你可以在按下鼠标滚轮时抓取,但如果鼠标不在使用中,你怎么能做到这一点?

 
Alexandr Bryzgalov:

我怎么知道十字准星按钮是被按下还是被释放了?

你可以抓住鼠标滚轮的点击,但如果鼠标不在使用中,那怎么办?

在需要的时候强迫它或把它推回去如何?

图表_十字绣_工具

通过按下鼠标中键启用/禁用访问 "十字准线 "工具

bool (默认值为true)

 
Alexey Viktorov:

如果有必要,是否可以强制或推倒重来?

图表_十字绣_工具

通过按下鼠标中键来启用/禁用 "十字准线 "工具

bool (默认为真)

据我所知,它只能访问工具,但不能关闭它。