Pytanie:
Czy niepotrzebne instrukcje Serial.print () spowolnią mój program?
Alexander Mills
2016-09-04 06:49:55 UTC
view on stackexchange narkive permalink

Mam wiele instrukcji Serial.print () i Serial.println () w moim (dość dużym) programie do debugowania, gdy coś się nie powiedzie. Skomentowałem instrukcję Serial.begin () , więc nie spowalniam wykonywania przez uruchomienie portu szeregowego. Chciałbym jednak zostawić wywołania Serial.print () , więc później wystarczy odkomentować Serial.begin () .

Jeśli na urządzeniu nie ma aktywnego portu szeregowego, to czy linie Serial.print () są nadal wykonywane w czasie wykonywania, czy kompilator je ignoruje? Czy jawne zakomentowanie tych wierszy ma przewagę wydajności?

Zamiast tego możesz napisać program w kodzie maszynowym, wtedy będziesz dokładnie wiedzieć, co robi mikrokontroler.
Co to ma wspólnego z nadrukami seryjnymi?
Trzy odpowiedzi:
KIIV
2016-09-04 11:01:22 UTC
view on stackexchange narkive permalink

Nie, nie ma USART nie włączonego, więc pomiń wszystkie print . Wszystko musi być zrobione tak czy inaczej (wywołania, konwersje na stringi, zapisy do UDR - USART Data Register ). Po prostu nie jest wysyłany na zewnątrz przez USART i nie musi czekać na UDRE (flaga USART Data Register Empty ), więc może być nieco szybciej.

Możesz użyć podobnej techniki jak Loggers - zmienna poziomu dziennika i if s (można to zmienić w czasie wykonywania).

Lub możesz użyć makra:

  // zakomentuj tę linię, jeśli chcesz wyświetlić logi: #define NDEBUG # ifdef NDEBUG #define LOG (...) #define LOGLN (...) # else #define LOG (...) Serial.print (__ VA_ARGS__) #define LOGLN (...) Serial.println (__ VA_ARGS __) # endif  

A później:

  LOG (F ("Coś: 0x")); LOGLN (zmienna, HEX);  

A jak to wygląda po podstawieniu makra? Kod:

  if (...) LOG (F ("coś")); else LOG (F ("coś innego"));  

Po zdefiniowaniu NDEBUG jest rozwijany do:

  if (...); else;  

I bez zdefiniowania NDEBUG jest rozszerzany do:

  if (...) Serial.print (F ("coś")); else Serial.print (F ("coś innego"));  
John Burger
2016-09-04 12:23:00 UTC
view on stackexchange narkive permalink

Kompilator nie może wiedzieć, jaka jest sytuacja z urządzeniem w czasie wykonywania. Sama funkcja musi to określić, więc wywołanie nastąpi , z argumentami i wszystkim. Dobrze napisana funkcja sprawdzałaby wcześnie, czy jest coś do zrobienia, a jeśli nie, to zwróciłaby wcześnie - ale to nadal marnuje czas.

Normą w programowaniu jest posiadanie dwóch wersji kodu: jednej oznaczony etykietą „DEBUG”, który ma wiele funkcji print () , a jeden oznaczony etykietą „RELEASE”, który używa konstrukcji językowych do ich ignorowania. Osiąga się to na wiele różnych sposobów - ale Arduino tak naprawdę nie obsługuje tego po wyjęciu z pudełka.

Odpowiedź @ KIIV, używając #define , jest jednym ze sposobów na zrobienie tego . Problem z tym pojawia się, gdy masz wiele modułów lub używasz bibliotek, które same wykonują własne funkcje print () . Jeśli masz tylko swój główny szkic, działałby całkiem dobrze.

Nick Gammon
2016-09-04 12:54:53 UTC
view on stackexchange narkive permalink

Szybki test pokazuje, że nie wykonanie Serial.begin powoduje, że rzeczy działają 10 razy szybciej. Na przykład:

  void setup () {Serial.begin (115200); pinMode (13, WYJŚCIE); } // koniec setupvoid loop () {digitalWrite (13, WYSOKI); for (byte i = 0; i < 100; i ++) Serial.println ("Ale czy wypływa z niego krew kozy?"); digitalWrite (13, NISKI); opóźnienie (500); } // koniec pętli  

Wykonanie 100 x takich wydruków na moim Uno zajęło 292 ms. Jeśli skomentuję wywołanie Serial.begin, zajmie to 29 ms. Więc szybciej, ale nie niesamowicie szybko.

Na mojej stronie o debugowaniu zasugerowałem kod włączania lub wyłączania wydruków debugowania. Podstawowym pomysłem jest zdefiniowanie Trace, które pozwoli ci wyświetlać rzeczy, jeśli debugowanie jest włączone, na przykład:

  #define DEBUG false // debugowanie warunkowe #if DEBUG #define beginDebug () do {Serial.begin (115200); } while (0) #define Trace (x) Serial.print (x) #define Trace2 (x, y) Serial.print (x, y) #define Traceln (x) Serial.println (x) #define Traceln2 (x , y) Serial.println (x, y) #define TraceFunc () do {Serial.print (F ("W funkcji:")); Serial.println (__PRETTY_FUNCTION__); } while (0) #else #define beginDebug () ((void) 0) #define Trace (x) ((void) 0) #define Trace2 (x, y) ((void) 0) #define Traceln (x) ((void) 0) #define Traceln2 (x, y) ((void) 0) #define TraceFunc () ((void) 0) #endif // DEBUGvoid setup () {beginDebug (); pinMode (13, WYJŚCIE); } // koniec setupvoid loop () {digitalWrite (13, WYSOKI); for (byte i = 0; i < 100; i ++) Traceln ("Ale czy wypływa z niego krew kozy?"); digitalWrite (13, NISKI); opóźnienie (500); } // koniec pętli  

Teraz w tym przypadku, gdy DEBUG jest zdefiniowany jako false , czas potrzebny na wysoki pin 13 wynosi teraz tylko 5 µs - znaczna przyspieszenie.

Jeśli zdefiniujesz DEBUG jako prawda, to będzie on znacznie wolniejszy - 290 ms, jak w pierwszym teście.


Sugestia KIIV zakończy się niepowodzeniem, jeśli masz instrukcje if :

  #ifdef NDEBUG #define LOG ( ...) #define LOGLN (...) # else #define LOG (...) Serial.print (__ VA_ARGS__); #define LOGLN (...) Serial.println (__ VA_ARGS __); # endif  

Na przykład:

  if (coś) LOG („coś jest prawdą”); digitalWrite (8, HIGH);  

Wyobraź sobie, że debugowanie jest wyłączone. Teraz puste define sprawia, że ​​kod wygląda następująco:

  if (something) digitalWrite (8, HIGH);  

Masz więc efekt uboczny polegający na tym, że kod zachowuje się inaczej, jeśli debugowanie jest wyłączone.


Średnik nie jest częścią makra, więc zostanie rozwinięty do: if (something);

Zobacz Co robi prekompilator c z makrami zdefiniowanymi jako (void) 0 - istnieją subtelne problemy z używaniem pusta definicja w porównaniu z void (0).

Na przykład:

  Serial.println ("cześć"), LOGLN ("debugowanie");  

Nie można się skompilować (z pustą definicją), jeśli debugowanie jest wyłączone. Wersja void (0) nie kompiluje się. Wprawdzie jest to skrajny przypadek, ale mimo to ...

Średnik nie jest częścią makra, więc zostanie rozwinięty do: `if (coś);`
Sposób na obejście problemu `if (...) [makro]` polega na tym, że gdy debugowanie jest wyłączone, należy zdefiniować makra tak, aby były równe `{}`, na przykład `#define LOG (...) {}` , to `if (...)` zamienia się w `if (...) {}`
@Majenko Ale jeśli masz: `#define MACRO () {}` i użyjesz go jako: `if (...) MACRO (...); else ... `(zauważ, że średnik) zmieni się w` if (...) {}; inaczej ... ". Dlatego wzorzec `do {...} while (0)` jest używany czasami zamiast prostego bloku `{...}`
`Średnik nie jest częścią makra '- edytowałeś swój post po tym, jak utworzyłem swój. W swoim oryginalnym poście (jak widać z tego, co skopiowałem / wkleiłem) miałeś średniki.


To pytanie i odpowiedź zostało automatycznie przetłumaczone z języka angielskiego.Oryginalna treść jest dostępna na stackexchange, za co dziękujemy za licencję cc by-sa 3.0, w ramach której jest rozpowszechniana.
Loading...