
这个问题是在执行各种命令时对Arduino性能的研究中出现的(更多内容请参见单独的文章)。 在研究过程中,当操作数的值发生更改时,单个命令的工作时间是否恒定(后来证明,并非不合理)引起了人们的怀疑,并做出了尝试估算单独命令的执行时间的决定。 为此,编写了一个小程序(谁说草图是要离开班级),乍看之下就证实了这一假设。 在结论中,您可以观察到值16和20,但有时会发现28微秒甚至32微秒。 如果将接收到的数据乘以16(MK时钟频率),则将获得以MK周期(从256到512)的执行时间。 不幸的是,重复执行主程序周期(使用相同的初始数据),同时又保持了总体情况,已经给出了执行时间的不同分配,因此实际时间变化与初始数据无关。 最初的假设被反证,但变得有趣,以及与这种显着分散究竟有何关联。
必要说明-我非常了解应该使用更复杂的程序来测量命令的执行时间,但是粗略估计一下,稍后再进行演示就足够了。
因此,时间在变化,而且非常重要的是,我们正在寻找造成这种现象的原因。 首先,我们注意获得的值的多样性,查看一段时间内对工作库的描述,并发现4µsec是一个量度,因此最好进行量子分析并了解我们得到4或5(非常频繁)以及6或7或8(非常稀有)单位。 对于前半部分,一切都很容易-如果测量值介于4到5个单位之间,则分散不可避免。 此外,考虑到样本是独立的,我们可以通过统计方法提高测量精度,这是我们所做的,可以获得可接受的结果。
但是下半年(6,7,8)情况更糟。 我们发现散布与源数据不相关,这意味着这是影响命令执行时间的其他进程的体现。 应当指出的是,排放很少见,对计算的平均值没有显着影响。 完全有可能忽略它们,但这不是我们的风格。 总的来说,在工程领域的多年工作中,我意识到您不会留下任何误解,无论它们看起来多么微不足道,因为它们具有在最不适当的时刻向后退的能力(令人讨厌的)。
我们开始提出
假设1-最方便(在便利性和多功能性上仅次于Creator的直接干预)-软件故障当然不是我的,我的程序永远不会失败,而是连接的库(编译器,操作系统,浏览器等) 。-替代必要的)。 此外,由于我是在
www.tinkercad.com的仿真器中运行程序的,因此您仍然可以参考仿真器的bug并关闭该主题,因为这些资源不可用。 该假设的缺点:
- 从一个周期到另一个周期,偏差的位置会发生变化,这表明了这一点。
- 尽管论点很弱,但该站点仍支持AutoDesk。
- “我们接受这样的假设,即发生的事情不是幻觉,否则将毫无意义。”
下一个假设是某些背景处理对测量结果的影响。 虽然,我们似乎什么也没做,只是相信,虽然...我们将结果输出为Serial。
假设2出现了-输出时间有时会增加(虽然很奇怪,但是它确实发生了),它会加到命令的执行时间上。 尽管有多少输出值得怀疑,但是无论如何-添加Flush并没有帮助,增加了完成输出的延迟,也没有帮助,通常将输出移出了循环-无论如何,时间都在跳-这绝对不是串行的。
好吧,剩下的就是周期本身的组织(要改变周期的持续时间还不尽人意),这就是所有...虽然保留了micros()。 我的意思是,第一次调用此函数与第二次调用的执行时间相同,当减去这两个值时,我得到零,但是如果这种假设是错误的呢?
假设3-时间计数的第二个调用有时要比第一次计数花费的时间长,或者与时间计数相关的动作有时会影响结果。 我们看一下具有时间功能的源代码(arduino-1.8.4 \ hardware \ arduino \ avr \ cores \ arduino \ wireing.c-我对这种事情反复表达了自己的态度,我不会重复自己),我们发现256分之一计数器较年轻部分的硬件增加周期被中断,以使计数器较旧部分递增。
我们的周期执行时间是4到5,因此我们可以预期170 *(4..5)/ 256 =在170个测量段中从三个到四个异常值。 我们看-看起来非常相似,实际上有4个。 为了区分第一个和第二个原因,我们在关键部分进行了禁止中断的计算。 结果变化不大,排放仍然存在,这意味着micros()带来了额外的时间。 在这里我们什么也不能做,尽管源代码可用,但是我们不能更改它-库包含在二进制文件中。 当然,我们可以编写自己的与时间打交道的功能并观察它们的行为,但是有一种更简单的方法。
由于持续时间增加的可能原因是“长时间”中断处理,因此我们排除了其在测量过程中发生的可能性。 为此,请等待其出现,然后我们才执行测量周期。 由于中断的发生频率远低于我们的测量周期,因此我们可以保证中断的发生。 我们编写程序的相应片段(使用从源代码中提取信息的
肮脏黑客 ),“这真是个街头魔术”,一切都变得正常了-我们用166个时钟周期的PT加法运算的执行时间平均值来测量4和5量子的执行时间。对应于先前的测量值。 该假设可以认为是证实的。
还有一个问题-中断需要花费这么长时间,需要花费什么时间
(7.8)-(5)〜2量子= * 4 = 8毫秒* 16 = 128个处理器周期? 我们转到源代码(即,由Godbolt.com上的编译器生成的汇编代码),我们看到中断本身大约执行了70个周期,其中60个周期不断执行,并且在读取时有10个周期的额外开销,点击时总共要花费70个周期中断-少于收到,但足够接近。 我们将差异归因于编译器之间或使用它们的方式之间的差异。
好了,现在我们可以使用各种参数来衡量PT加法命令的实际执行时间,并确保更改参数时它的确发生了很大变化:从0.0的136小数到0.63(幻数)的190小数,而10.63的只有162小数。 归因于此特定库中的对齐方式及其实现的特征,其概率为99.9%,但此研究显然超出了所考虑问题的范围。
附录-程序文本:void setup() { Serial.begin(9600); } volatile float t; // void loop() { int d[170]; unsigned long time,time1; float dt=1/170.; for (int i=0; i<170; ++i) { { // time1=micros(); long time2; do { time2=micros(); } while ((time2 & ~0xFF) == (time1 & ~0xFF)); }; time1=micros(); // t=10.63; // t=t+dt; // time = micros(); // time1=time-time1; d[i]=time1/4; }; // , float sum=0; for (int i=0; i<170; ++i) { sum+=d[i]; Serial.println(d[i]); }; Serial.println((sum/170-2.11)*4*16); //2.11 – Serial.flush(); // , }