Pytanie:
Zalecane użycie pól bitowych w strukturach
anthropomo
2014-10-01 05:19:12 UTC
view on stackexchange narkive permalink

Muszę śledzić dużą ilość danych (dla Arduino) w programie, jednocześnie zajmując się sporą ilością innych spraw.

Zacząłem od takiej struktury:

  struct MyStruct {// uwaga: te nazwy równie dobrze mogą mieć postać foo bar baz uint8_t color; stan logiczny; obszar uint8_t; numer uint8_t; uint8_t len;};  

Mam tablicę 800 z nich. Przy 5 bajtach każdy, to 4Kb, czyli połowa pamięci RAM w Arduino Mega.

Zmieniłem na taką strukturę:

  struct MyStruct {uint8_t color: 3; // wartość maksymalna 7 // uint8_t stan: 1; *** przeniesiony, dzięki Ignacio *** uint8_t area: 5; // m.v. 31 uint8_t numer; uint8_t len: 5; // m.v. 31 uint8_t state: 1; // m.v. 1};  

Rozumiem, że zmniejsza to rozmiar każdej instancji do 3 bajtów, co daje łącznie 2,4 KB w mojej tablicy.

Czytałem, że w niektórych implementacje / chipsety używające pól bitowych w strukturach mogą prowadzić do mniej wydajnego wykonywania. Oczywiście jest to bardziej efektywne wykorzystanie pamięci, ale moje pytanie brzmi:

Jak Arduino radzi sobie z polami bitowymi? Czy będzie lepszy, gorszy, czy pomijalnie inny pod względem lub szybkości podczas iteracji przez tablicę struktur pól bitowych? Czy istnieje dobry sposób, aby to przetestować?

Ta kolejność zajmie 4 bajty. Uzyskaj to 1-bitowe pole spomiędzy pozostałych 8.
@Ignacio Niesamowite ... właśnie zmniejszyłeś moje całkowite zużycie pamięci o kolejne 9%.
Powodem, dla którego pola bitowe w strukturach mogą prowadzić do mniej wydajnego wykonywania, są ograniczenia wyrównania słów. Na przykład. jeśli przekraczają naturalne wyrównanie 32/64, powodując wielokrotny dostęp. Nie powinno to stanowić problemu dla ATmega
Pytanie, które musisz sobie zadać, dotyczy tego, czy najbardziej martwisz się wyczerpaniem pamięci RAM, pamięci programu lub cykli procesora - jest to sytuacja, w której możesz zoptymalizować trochę dla jednego kosztem drugiego i mieć wpływ na trzeci. W pewnym momencie powinieneś również zapytać, czy Arduino oparte na ATmega jest naprawdę właściwą platformą.
Udało mi się zrestrukturyzować program tak, aby używał indeksu tablicy, w którym używałem 8-bitowej liczby, więc do 2 bajtów na strukturę, kolejne 9% ogolone. Wygląda na to, że mogę oszczędzić cykle. Iteruję przez 800 struktur co 50 milisekund, jednocześnie obsługując sporadyczne żądania http. Bez problemów. W tym przypadku przekraczałem limity ATmega, ale wydaje mi się, że znajduję się znacznie poniżej.
Oto kilka dalszych lektur, na które natknąłem się później: http://www.catb.org/esr/structure-packing/
Trzy odpowiedzi:
jdr5ca
2014-10-01 13:04:39 UTC
view on stackexchange narkive permalink

To, przed czym stoisz, to klasyczny kompromis między czasem a pamięcią. Pola bitowe będą mniejsze w pamięci, ale operacja na nich zajmie więcej czasu. Możesz policzyć, że bez względu na procesor, pola bitowe będą wolniejsze.

Używasz słowa wydajne , ale słowo to nie ma określonego znaczenia bez miernika tego, co jest dobre lub zły. Gdy masz tylko 8 kB RAM, używanie pamięci jest złe, czas może być tani. Jeśli masz ograniczenia czasu rzeczywistego, używanie czasu jest złe, a pamięć może być tania. Ogólnie rzecz biorąc, możesz wykupić tylko wyjście z tego kompromisu. Innymi słowy, jeśli uznasz, że zarówno czas, jak i pamięć są złe, wydaj gotówkę i użyj większego chipa. Nie ma jednej odpowiedzi na to, co jest dobre, a co złe. To jest jeden z powodów, dla których mikrokontrolery mają tak duży wybór, że ludzie dopasowują chip do aplikacji, a aplikację do chipa.

Wypełnianie bitów pola bitowego będzie wolniejsze niż wypełnianie całych bajtów. Weźmy na przykład

  x = 5; ... asimplestruct.len = x; // vsabitfield.len = x;  

Pierwszy prosty przypadek będzie po prostu:

  • załaduj wartość x do rejestru
  • zapisz ją do bajtu dla len

Druga robi coś takiego:

  • ładuje aktualną wartość abitfield
  • ładuje maskę
  • czyści bity dla len
  • ładuje aktualną wartość x
  • wczytuje maskę
  • usuwa nieużywane bity x
  • przesuwa bity x
  • lub x z polem abitfield
  • zapisuje bieżącą wartość z powrotem w pamięci

Jeśli wszystkie twoje operacje pakują dane do pola bitowego lub wypakowują je z pola bitowego, powinieneś spodziewać się wolniejszego wykonania. Pola bitowe są rodzajem kompresji - kosztują takty.

Ale poruszanie się po polach bitowych będzie szybsze, ponieważ jest mniej bajtów do załadowania i przechowywania. Jeśli sortujesz tę tablicę, mniejsza liczba bajtów może być zaletą. Jeśli przenosisz je przez port szeregowy, skompresowany rozmiar pola bitowego może być zwycięzcą.

W odniesieniu do twojego pytania:

Czy istnieje dobry sposób, aby to przetestować?

Jedynym dobrym sposobem na sprawdzenie tego jest napisanie przypadki testowe dla obu podejść przy użyciu wzorca, który ściśle pasuje do Twojej aplikacji. Naprawdę ważne jest, jaką kombinację operacji wykonasz, aby zdecydować, czy różnica jest nieistotna czy znacząca.

Podczas tego rodzaju eksperymentu optymalizacyjnego zdecydowanie użyj kontroli źródła w swoim projekcie. Możesz utworzyć lokalne repozytorium GIT lub Mercurial za pomocą kilku kliknięć. Utrzymanie łańcucha punktów kontrolnych pozwala na rozerwanie kodu, badając skutki różnych implementacji. Jeśli skręcisz w złą stronę, repozytorium pozwoli ci po prostu wrócić do ostatniego dobrego punktu i wypróbować inną ścieżkę.

(Uwaga dodatkowa: tym razem wymiana pamięci istnieje również w przeciwnym kierunku. Jeśli spojrzysz poprzez opcje kompilatora dla procesorów klas dla komputerów stacjonarnych znajdziesz coś, co nazywa się pakowaniem struktury . Ta opcja pozwala na dodanie pustych bajtów między polami jednobajtowymi, tak aby pozostały wyrównane na granicach słowa lub podwójnego słowa. celowe wyrzucanie pamięci RAM wydaje się szalone, ale w procesorach z rejestrami i magistralami o szerokości 16 lub 32 bitów tak zwane operacje pamięciowe wyrównane słowem lub dwordami mogą być szybsze niż operacje na bajtach).

JRobert
2014-10-01 22:23:53 UTC
view on stackexchange narkive permalink

Dyskusja na temat pól bitowych nie byłaby kompletna bez wspomnienia, że:

Struktury z polami bitowymi mogą być używane jako przenośny sposób na zmniejszenie ilości miejsca wymaganego dla struktury (z prawdopodobnym kosztem zwiększenia przestrzeni instrukcji i czasu potrzebnych do uzyskania dostępu do pól) lub jako nieprzenośny sposób opisania układu pamięci znanego na poziomie bitowym.

[Kernighan & Ritchie, Język programowania C, wydanie drugie , dodatek A-8.3]

Inne plakaty, w tym ty, dotyczyły kompromisu czasowo-przestrzennego. Ale to, czego czasami brakuje (być może nie dotyczy twojej aplikacji -?), To pełna zależność od implementacji układu pamięci masowej . Ma to znaczenie, gdy aplikacje współużytkują te dane z innym procesem: sprzęt do odczytu lub zapisu, którego przypisania bitów rejestrów są koniecznie naprawione; przekazywanie danych do innego systemu (przez sieć lub urządzenie magazynujące nie ma znaczenia); lub do innej aplikacji w tym samym systemie, która mogła zostać skompilowana za pomocą innego kompilatora lub wersji kompilatora.

soerium
2014-10-01 16:26:33 UTC
view on stackexchange narkive permalink

Mała wskazówka - widzę, że struktura nie jest 1B wyrównana dla 8-bitowego procesora. Na końcu swojej struktury możesz dodać dopełnienie 2-bitowe długości lub przemyśleć zwiększenie rozmiaru state”.

  struct MyStruct {uint8_t color : 3; // wartość maksymalna 7 // uint8_t stan: 1; *** przeniesiony, dzięki Ignacio *** uint8_t area: 5; // m.v. 31 uint8_t numer; uint8_t len: 5; // m.v. 31 uint8_t state: 1; // m.v. 1 dopełnienie uint8_t: 2;};  
Dodanie wypełnienia nie zmienia pamięci dynamicznej w czasie kompilacji. Wybacz moją ignorancję, ale wygląda na to, że kompilator zajmuje się dopełnieniem. Czy może dzieje się coś w czasie wykonywania, aby zrekompensować brak dopełnienia?
To prawda. W tym przypadku bardziej przypomina dobrą praktykę.


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...