Motorola zu Intel konvertieren ....
-
Das einlesen geht ueber unterschiedliche API's die ich vom hersteller bekomme ....
bei einem muss ich pollen .... beim anderen muss ich dem nen zeiger aufn Event geben, der setzt das event wenn ne nachricht eintrifft, und dann dann kann ich mir nen Array mit daten abholen ....
die API's geben die Daten schon telegrammorinetiert raus ... also ich bekomm nen array von ner Struct.
In der struct stehen
zeitstempel (64bit int),
telegrammID(16 bit Int),
DLC(laenge der nutzdaten in byte, 8bit int),
und nen char[8] array (8 ist max laenge)TelegramID ist spezifiziert, die kommt auch richtig rum an .... weil die telegrammID auch was mit der Prioritaet aufn Bus zu tun hat (iss nen serieller Bus mit 1 oder 2 leitungen)
DLC kommt auch richtig rum an (istd spezifiziert), weil die auch fuer die uebertragung und stopbits usw wichtig ist.Nur bei den Datenbytes ist es nicht spezifiziert ...
Die telegramme Sind in Signale aufgeteilt. diese signale werden in ner Zentralen DB gepflegt. Da steht dann drinn, das bspweise am telegram 0x116 das bit 8 - 15 nen Signal X enspricht ... fuer das Signal gibts dann Offset und Aufloesung so dass man sich den realen wert mit offset + x*aufloesung errechnen kannwie gesagt bei den Motorola buss iss nu alles verkehrt rum ... waehrend ich in der DB wenigstens schon mal die Startbits der signale umrechnen kann, kommen am treiber die Bytes nun aber bitverdreht an ...
Das nur zur allgemeinen Info ... wer jetzt wegen den 8 Byte laenge an CAN denkt, liegt auch genau richtig ...
die ganze anbindung an den CAN-Bus der Stg's wird von industriellen CAN-Controllern uebernommen, da sind also TelegrammID und laenge spezifiziert. Aber was in den Daten drinnesteht, intressiert die controller nich, sondern das wird von den FPGA's oder CPU der STG's befuellt. wenn da nun einige Motorola Teile verwenden, schreiben die einfach die eigenen Speicherbereiche in die puffer der CAN Controller, und die sind gegenueber Intel bitverdreht ... intressiert aber nich, weil alle stg's am buss (monentan noch) dann motorola sprechen.
Nur wir muessen die Daten interpretieren ... und die meisten karten schieben eh nur den puffer der CANController in den Speicher des PC's (Intel) und voila, alles ist verdreht ^^Willst wirklich noch ne ausleseroutine sehen ? ^^
btw: aber wir wissen ja nicht wie schnell's ein soll. vielleicht trudeln die bits nur mit ein paar kHz ein, dann ist das ja alles egal
eigentlich human .... 500kbit bei extrem schlechter nutzdatenrate ^^ (CAN halt)
im schnitt max 3800 Nachrichten = 2+1+8 byte pro sekunde ... der zeitstempel wird beim empfang generiert.
Problem ist, dass unsere SW auch auf mobilen industrierechnern laufen muss, die haben zwar windows drauf, aber die rechenleistung ist ... urks ^^ glaub die miesesten haben nen 500Mhz cyrix prozessor oder so. Da kommts eben halt auch extrem aufn stromverbrauch und temperatur / ruettelfestigkeit an ....
Ciao ...
-
@net ich will ja jetzt nicht drauf rumreiten, aber auch wenn du oderst brauchst du eine maske...
und ein oder ist auch nicht schneller als ein add vielleicht früher mal.bitte pseudocode mir das doch mal "online" bitweise ohne maske.
das umdrehen per lookuptable kann man sich dann auch gleich schenken..wenn man die daten natürlich von nicht änderbarem code bekommt, dann kann man natürlich nichts machen. aber ob dann der umdrehen per lookuptable schneller ist, als ein bitweises übertragen von register zu register, das wage ich dann doch zu bezweifeln.
aber das bezweifel ich auch einfach aus der lust heraus.nicht, dass es wirklich wichtig wäre
vielleicht hat ja trotzdem jemand lust, das mal auszumessen. bestimmt ne ganz gute anfänger übung...ciao,
jenz
-
jenz schrieb:
bitte pseudocode mir das doch mal "online" bitweise ohne maske.
ganz einfach, so ungefähr
unsigned long table[32] = { 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020, ... ... ... 0x20000000, 0x40000000, 0x80000000, } ... unsigned long result = 0; int s; for (s=0; s<32; s++) { if (read_bit() == 1) // hi-bit kommt als erstes result |= table[s]; } // result enthält nun umgedrehtes bitmuster ...
@RHBaum
was habt ihr denn für'n adapter am pc der die can-messages bitweise abliefert? sowas ist doch eher ungewöhnlich
-
das geht dann allerdings auch ohne LUT, indem wir die maske als laufvariable der schleife missbrauchen (vorausgesetzt, der entsprechende typ ist exakt 32bit breit):
unsigned result = 0; for (unsigned mask=1; mask!=0; mask<<=1) { if (read_bit() == 1) // hi-bit kommt als erstes result |= mask; }
-
@net, na da hast du ja mal eine richtig tolle lösung gefunden.
du schreibst also lieber die ganzen masken hin, anstatt die zu schiften?
so ganz kann ich das leider nicht akzeptieren.zumal der nächste auch gleich wieder die maske vorschlägt. natürlich ein bisschen cleverer implementiert als ich aber mit maske...
ciao,
jenz
-
jenz schrieb:
@net, na da hast du ja mal eine richtig tolle lösung gefunden.
du schreibst also lieber die ganzen masken hin, anstatt die zu schiften?
so ganz kann ich das leider nicht akzeptieren.toll ist das alles nicht (wegen der 32 schleifendurchläufe). aber lookuptables sind immer ein tickchen schneller. kannst es ja mal testen (beide versionen einige millionen mal aufrufen und zeit messen).
-
toll ist das alles nicht (wegen der 32 schleifendurchläufe). aber lookuptables sind immer ein tickchen schneller. kannst es ja mal testen (beide versionen einige millionen mal aufrufen und zeit messen).
okay, wenn sich bis samstag nicht irgendein anderer daran versucht hat werde ich das mal durchmessen.
ich bin mir sicher, dass die variante ohne lut schneller ist. vor allem dann, wenn auch noch threadwechsel stattfinden. wovon man wohl ausgehen sollte.bis dahin,
jenz
-
jenz schrieb:
ich bin mir sicher, dass die variante ohne lut schneller ist. vor allem dann, wenn auch noch threadwechsel stattfinden. wovon man wohl ausgehen sollte.
das könnte schon sein, weil der shiftwert in 'nem register gehalten wird. andererseits könnte ein geschickter compiler die schleife auflösen und die werte aus dem array direkt nehmen (also ohne array-zugriffe zu machen). dann wär' wieder die lut-variante schneller
-
Es gibt noch eine Möglichkeit (ohne Schleife):
Zuerst swappt man die 4 Bytes (d.h. aus "abcd" wird "dcba").
Dann greift man über eine Lookup-Table mit 256 Werten für jedes Byte darauf zu und swappt jeweils die 8 Bits.0 -> 0 00000000 -> 00000000
1 -> 128 00000001 -> 10000000
2 -> 64 00000010 -> 01000000
...
254 -> 127 11111110 -> 01111111
255 -> 255 11111111 -> 11111111
-
@th: das swappen ist schon längst nicht mehr unser problem.
wir reden über ein möglichst effizientes einlesen...ciao,
jenz
-
Ich habe hier mal ein Testprogramm geschrieben, mit dem ich verschiedene Varianten ausprobiert habe. Die Lookuptabelle gewinnt bei mir um ca. den Faktor 6. Hier ist meine Rotationsfunktion, die ich auch zum initialisieren der Lookuptabelle verwende:
template <typename T> T rotbits(T v, unsigned bits = 8) { T ret = 0; for (unsigned n = 0; n < bits; ++n) { ret = (ret << 1) | (v & 1); v >>= 1; } return ret; }
Wenn Interesse besteht, kann ich auch mein komplettes Testprgramm posten.
Tntnet
-
ich hatte auch mal etwas lange weile:
#include <stdio.h> #include <windows.h> // dummy int read_bit (void) { return 1; } unsigned long read32_reverse_table (void) { static unsigned long table[] = { 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000, 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000, 0x00800000, 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000 }; register unsigned long result = 0; register unsigned long s; for (s=0; s<32; s++) { if (read_bit() == 1) result |= table[s]; } return result; } unsigned long read32_reverse_loopless (void) { register unsigned long result = 0; if (read_bit() == 1) result |= 0x00000001; if (read_bit() == 1) result |= 0x00000002; if (read_bit() == 1) result |= 0x00000004; if (read_bit() == 1) result |= 0x00000008; if (read_bit() == 1) result |= 0x00000010; if (read_bit() == 1) result |= 0x00000020; if (read_bit() == 1) result |= 0x00000040; if (read_bit() == 1) result |= 0x00000080; if (read_bit() == 1) result |= 0x00000100; if (read_bit() == 1) result |= 0x00000200; if (read_bit() == 1) result |= 0x00000400; if (read_bit() == 1) result |= 0x00000800; if (read_bit() == 1) result |= 0x00001000; if (read_bit() == 1) result |= 0x00002000; if (read_bit() == 1) result |= 0x00004000; if (read_bit() == 1) result |= 0x00008000; if (read_bit() == 1) result |= 0x00010000; if (read_bit() == 1) result |= 0x00020000; if (read_bit() == 1) result |= 0x00040000; if (read_bit() == 1) result |= 0x00080000; if (read_bit() == 1) result |= 0x00100000; if (read_bit() == 1) result |= 0x00200000; if (read_bit() == 1) result |= 0x00400000; if (read_bit() == 1) result |= 0x00800000; if (read_bit() == 1) result |= 0x01000000; if (read_bit() == 1) result |= 0x02000000; if (read_bit() == 1) result |= 0x04000000; if (read_bit() == 1) result |= 0x08000000; if (read_bit() == 1) result |= 0x10000000; if (read_bit() == 1) result |= 0x20000000; if (read_bit() == 1) result |= 0x40000000; if (read_bit() == 1) result |= 0x80000000; return result; } unsigned long read32_reverse_mask (void) { register unsigned long result = 0; register unsigned long mask; for (mask=1; mask!=0; mask<<=1) { if (read_bit() == 1) result |= mask; } return result; } #define REPETITIONS 10000000L void test (void) { unsigned long timer; unsigned long s; // mask timer = GetTickCount(); for (s=0; s<REPETITIONS; s++) read32_reverse_mask(); timer = GetTickCount() - timer; printf ("mask: %lu\n", timer); // table timer = GetTickCount(); for (s=0; s<REPETITIONS; s++) read32_reverse_table(); timer = GetTickCount() - timer; printf ("table: %lu\n", timer); // w/o loop timer = GetTickCount(); for (s=0; s<REPETITIONS; s++) read32_reverse_loopless(); timer = GetTickCount() - timer; printf ("loopless: %lu\n", timer); printf ("------------------------------\n"); } void main (void) { SetPriorityClass (GetCurrentProcess(), REALTIME_PRIORITY_CLASS); SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); test(); test(); test(); test(); }
ergebnis:
mask: 1656 table: 1672 loopless: 1125 ------------------------------ mask: 1656 table: 1672 loopless: 1125 ------------------------------ mask: 1656 table: 1672 loopless: 1141 ------------------------------ mask: 1656 table: 1688 loopless: 1125 ------------------------------ Press any key to continue
demnach ist die lut-version etwas schlechter als die mit dem shiften.
aber ohne schleife ist es um ca. 1/3 schneller....
-
@net: Ich glaube, einer von uns beiden hat das Problem nicht richtig verstanden. Du gehst von einer Funktion read_bit aus, welches ein bit nach dem anderen liefert. Ich gehe davon aus, daß ein 32-Bit-Wort zur verfügung steht, welches bitweise umgedreht werden soll.
Auch ist Deine Lookup-tabelle was anderes, als meine. Die Idee ist doch, daß man ein Byte umdreht, indem man in einer Tabelle mit den 256 Byte-Werten den entsprechenden umgedrehten Byte nachschaut.
Bei mir sieht das so aus:
static unsigned char lut[256]; // initialize lookup-table for (unsigned i = 0; i < 256; ++i) lut[i] = rotbits(i); unsigned char ch = getIrgendeinByte(); unsigned char rch = lut[ch]; // schlage in der Lookup-tabelle nach
Und diese Variante ist in der Tat wesentlich schneller als etwa:
unsigned char rch = rotbits(ch);
welches jedes einzelne Byte algorithmisch undreht.
Tntnet
-
die API's geben die Daten telegrammorinetiert raus
ich bekomm nen array von ner Struct:
[...]
char[8] arrayunsigned char b; b = ((b * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
-
@tntnet, net programmiert da etwas anderes. das ist so ne kleine privatfehde.
@net, fleißig. sieht man mal, das unsere ersten varianten nicht so besonders waren. die ausgerollte ist halt am besten.
einigen wir uns darauf, dass wir beide danebenlagen?
die paar ticks sind dann ja wirklich egal.also back to topic, vielleicht können wir dem tntnet noch was zeigen
ciao,
jenz
-
RHBaum schrieb:
im schnitt max 3800 Nachrichten = 2+1+8 byte pro sekunde ... der zeitstempel wird beim empfang generiert.
3800 Botschaften pro Sekunde wären aber schon recht viel für 500k. Ihr fahrt da hoffentlich nur mit einem Sender, oder?
-
jenz schrieb:
@net, fleißig. sieht man mal, das unsere ersten varianten nicht so besonders waren. die ausgerollte ist halt am besten.
ja, und die moral von der geschicht? schleifen haben in zeitkritischen codes nix zu suchen :p
-
net schrieb:
jenz schrieb:
@net, fleißig. sieht man mal, das unsere ersten varianten nicht so besonders waren. die ausgerollte ist halt am besten.
ja, und die moral von der geschicht? schleifen haben in zeitkritischen codes nix zu suchen :p
Ich würde eher sagen: die Moral von der Geschicht: Wer was misst misst Mist.
Ich habe den Code auch mal ein wenig getestet und bin zu dem Ergebnis gekommen, daß es verschiedene Faktoren gibt, die das Ergebnis wesentlich beeinflussen können.
Zunächst habe ich den Code auf Linux portiert, da ich hier kein Windows habe. Aber das sollte auf der Qualität der Aussagen keinen Einfluß haben. Auch habe ich die REPETITIONS ein wenig erhöht, um durch eine längere Laufzeit eine geringere Varianz zu erreichen.
Wer einen großen Einfluß auf die Performance hat, ist natürlich der Optimierer. Und wenn der die read_bit-Funktion sieht, da lacht er doch nur darüber und optimiert sie weg. Und schon ist der ganze Test futsch :p .
Also erst mal die read_bit-Funktion in eine separate Compilationseinheit und der Datensalat sieht schon anders aus. Und dann mit unterschiedlichen Optimierungsstufen testen. Unter Linux habe ich -O2 und -O3 getestet.
Mit -O2 kam heraus, daß die "table"- und "loopless"-Varianten in etwa gleichauf sind. "mask" ist weit abgeschlagen. Mit -O3 dreht ändert sich das Bild. Da geben sich die Varianten nicht viel. Da ist die "loopless"-Variante eher ein wenig langsamer als die anderen. Also hier meine Ergebnisse:
Mit -O2:
mask: 22445 table: 10314 loopless: 9053 ------------------------------ mask: 21564 table: 9924 loopless: 10185 ------------------------------ mask: 21574 table: 10838 loopless: 9654 ------------------------------ mask: 21998 table: 11176 loopless: 8998 ------------------------------
Und mit -O3:
mask: 8213 table: 8611 loopless: 8869 ------------------------------ mask: 8734 table: 7332 loopless: 9590 ------------------------------ mask: 7781 table: 7943 loopless: 8882 ------------------------------ mask: 7802 table: 8169 loopless: 8890 ------------------------------
Das gilt natürlich nur für den einen bestimmten Prozessor mit einem bestimmten Compiler unter einem bestimmten Betriebssystem. Bei solchen manuellen loop-unrolling spielt auch die Größe des Caches im Prozessor eine Rolle, wenn man das weiter treibt.
Tntnet
-
cool, noch ein messer.
da interessiert mich aber mal der generierte assemblercode. wundert mich, dass es sich nur durch die optimierung noch ändert.
leider habe ich zur zeit keinen zugriff auf meine werkzeuge, bin ein bisschen in urlaub und habe nicht den eigenen rechner dabei...
read_bit() sozusagen auszulagern finde ich ich gut. clever.
aber was man auch noch berücksichtigen müsste irgendwie, wäre dass threadwechsel stattfinden, im normalfall. und dann auch gleich die luts aus den caches gefegt werden. höchstwahrscheinlich, oder nicht?
still interested,
jenz
-
Die Zeitscheiben im Betriebssystem sind schon groß genug, daß das mit der Cache-entleerung sicherlich keine allzu grosse Rolle spielt. Bis zum nächsten Prozeßwechsel schafft das Programm sicherlich einige Millionen Iterationen. Da macht es nicht viel aus, wenn der Cache bei der 1. direkt aus dem RAM lesen muß. Und selbst das gilt nur, wenn es etwas anderes zu tun gibt und das andere den Cache vollständig austauscht. Auf einem ansonsten unbelasteten Rechner sollte das also gar nichts ausmachen.
Tntnet