Loop () içindeki sonsuz döngü daha hızlı çalışır mı?


19

Tipik bir çizim yazarken, genellikle loop()Arduino çalıştığı sürece tekrar tekrar çağrılmaya güvenirsiniz. loop()Fonksiyona girip çıkmak küçük bir ek yük getirmelidir.

Bundan kaçınmak için, muhtemelen kendi sonsuz döngünüzü oluşturabilirsiniz, örneğin:

void loop()
{
    while (true)
    {
        // do stuff...
    }
}

Bu performansı arttırmanın uygun bir yolu mu? loop()Asla geri dönmezse başka sorunlara neden olur mu?

Yanıtlar:


18

ATmega çekirdeğindeki kodun setup () ve loop () işlevini yapan bölümü aşağıdaki gibidir:

#include <Arduino.h>

int main(void)
{
        init();

#if defined(USBCON)
        USBDevice.attach();
#endif

        setup();

        for (;;) {
                loop();
                if (serialEventRun) serialEventRun();
        }

        return 0;
}

Oldukça basit, ama serialEventRun () yükü var; Orada.

İki basit çizimi karşılaştıralım:

void setup()
{

}

volatile uint8_t x;

void loop()
{

    x = 1;

}

ve

void setup()
{

}

volatile uint8_t x;

void loop()
{
    while(true)
    {
        x = 1;
    }
}

X ve volatile, optimize edilmediğinden emin olmak içindir.

Üretilen ASM'de farklı sonuçlar elde edersiniz: İki karşılaştırması

While (true) komutunun birkaç talimatı geri alırken, loop () bir çıkarma, karşılaştırma ve çağrı gerçekleştirir. Bu 4 talimat vs 1 talimat.

Yukarıdaki gibi ASM oluşturmak için avr-objdump adlı bir araç kullanmanız gerekir. Bu, avr-gcc'ye dahildir. Konum işletim sistemine göre değişir, bu nedenle adıyla aramak en kolay yoldur.

avr-objdump .hex dosyaları üzerinde çalışabilir, ancak orijinal kaynak ve yorumlar eksik. Yeni kod oluşturduysanız, bu verileri içeren bir .elf dosyanız olacaktır. Yine, bu dosyaların konumu işletim sistemine göre değişir - onları bulmanın en kolay yolu, tercihlerde ayrıntılı derlemeyi açmak ve çıktı dosyalarının nerede depolandığını görmektir.

Komutu aşağıdaki gibi çalıştırın:

avr-objdump -S output.elf> asm.txt

Ve çıktıyı bir metin düzenleyicide inceleyin.


Tamam, fakat serialEventRun () işlevini çağırmanın bir nedeni yok mu? Bu ne için?
jfpoilpret

1
HardwareSerial tarafından kullanılan işlevselliğin bir parçasıdır, Seri'ye gerek olmadığında neden çıkarılmadığından emin değildir.
Cybergibbons

2
İnsanların kendilerini kontrol edebilmeleri için ASM çıktısını nasıl oluşturduğunuzu kısaca açıklamak faydalı olacaktır.
jippie

@Cybergibbons asla alınmaz çünkü main.cArduino IDE tarafından kullanılan standardın bir parçasıdır . Ancak bu, SketchSerial kütüphanesinin çiziminize dahil edildiği anlamına gelmez; Eğer (kullanmak yoksa aslında dahil olmadığını var neden en if (serialEventRun)içinde main()işlevine Eğer HardwareSerial kütüphanesi sonra kullanmazsanız. serialEventRunhükümsüz olacaktır dolayısıyla hiçbir çağrı.
jfpoilpret

1
Evet, alıntılandığı gibi main.c'nin bir parçasıdır, ancak gerekli değilse optimize edilmesini beklerim, bu yüzden Seri'nin yönlerinin her zaman dahil olduğunu düşünüyorum. Sık sık döngü () asla dönecek ve Seri ile ilgili sorunları fark etmeyecek kod yazmak.
Cybergibbons

6

Cybergibbons'un cevabı , montaj kodu üretimini ve iki teknik arasındaki farkları oldukça iyi açıklıyor. Bu, pratik farklılıklar, yani her iki yaklaşımın yürütme süresi açısından ne kadar bir fark yaratacağı konusuna bakarak tamamlayıcı bir cevap olması amaçlanmaktadır .


Kod Varyasyonları

Aşağıdaki varyasyonları içeren bir analiz yaptım :

  • Temel void loop()(derleme satır içine alınır)
  • Çizgisiz void loop()(kullanılıyor __attribute__ ((noinline)))
  • İle döngü while(1)(optimize edilir)
  • Optimize edilmemiş döngü while(1)(ekleyerek __asm__ __volatile__("");. Bu, nopbir volatiledeğişkenin ek genel giderleri ile sonuçlanmadan döngü optimizasyonunu engelleyen bir talimattır )
  • void loop()Optimize edilmiş, çizgisizwhile(1)
  • void loop()Optimize edilmemiş, çizgisizwhile(1)

Eskizler burada bulunabilir .

Deney

Bu çizimlerin her birini 30 saniye boyunca çalıştırdım, böylece her biri 300 veri noktası biriktirdim . delayHer döngüde 100 milisaniyelik bir çağrı vardı (bu kötü şeyler olmadan ).

Sonuçlar

Daha sonra her bir döngünün ortalama yürütme sürelerini hesapladım, her birinden 100 milisaniye çıkardım ve ardından sonuçları çizdim.

http://raw2.github.com/AsheeshR/Arduino-Loop-Analysis/master/Figures/timeplot.png

Sonuç

  • while(1)İçindeki optimize edilmemiş bir döngü void loop, optimize edilmiş bir derleyiciden daha hızlıdır void loop.
  • Optimize edilmemiş kod ile varsayılan Arduino optimizasyon kodu arasındaki zaman farkı pratikte önemsizdir . Size avr-gccyardımcı olması için Arduino IDE'ye bağlı olmak yerine kendi optimizasyon bayraklarınızı kullanarak ve kullanarak manuel olarak derlemek daha iyi olacaktır (mikrosaniye optimizasyonlarına ihtiyacınız varsa).

NOT: Gerçek zaman değerleri burada önemli değildir, aralarındaki farktır. İcra zaman ~ 90 mikrosaniye için bir çağrı içerir Serial.println, microsve delay.

NOT2: Bu, Arduino IDE ve sağladığı varsayılan derleyici bayrakları kullanılarak yapıldı.

NOT3: Analiz (çizim ve hesaplamalar) R kullanılarak yapıldı.


1
İyi iş. Grafiğin milisaniye mikrosaniye değil büyük bir sorunu vardır.
Cybergibbons

@Cybergibbons Bu tüm ölçümler mikrosaniye içinde olduğundan ve 'herhangi bir yerde değişen ölçekleri engellemediğimden bu pek olası değil :)
asheeshr
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.