In vielen Programmiersprachen werden primitive als auch komplexe
Datentypen nach Bedarf in einen anderen Datentyp überführt.
Kleines Beispiel in der Programmiersprache C:
1
inta_i=3;
2
floatb_f=a_i;//float b_f = (float)a_i;
Ich würde gerne wissen, ob so eine Typenumwandlung in der
Programmiersprache C Ressourcen kostet?
Damit meine ich Laufzeit, Assembly Instruktionen oder sonstiges.
Hintergrund ist der, dass ich mit fremden Code anschaue und
Verbesserungsvorschläge suche. In den Sourcen wird häufig von
1
int
nach
1
float
und wieder zurück nach
1
int
konvertiert ohne das eine direkte Operation auf den float Attribut
statt findet. Abgesehen davon, dass dies nicht unbedingt sinnvoll und
sauber ist, brauche ich argumente dafür nur Typenkonvertierung zu
betreiben, wenn diese auch wirklich nötig ist.
Vielen Dank schonmal in vorraus
Carapa schrieb:> Ich würde gerne wissen, ob so eine Typenumwandlung in der> Programmiersprache C Ressourcen kostet?>> Damit meine ich Laufzeit, Assembly Instruktionen oder sonstiges.
Das kann so sein, muß aber nicht.
Es gibt casts, die letztlich nur etwas bewirken in der Art
"interpreteiere den Ausdruck nur anders", z.B. ein char* nach int*
casten.
Das kostet dann auch nichts.
Andere casts haben eine Bedeutung in der Art "wandle den Ausdruck in ein
anderes Format um", z.B. double d=(double)2/5
Dann kann es sein, daß der Compiler schon zur Übersetzungszeit weiß, was
rauskommt und gleich das Ergebnis einsetzt, es kann aber auch sein, daß
zur Laufzeit dann mehr oder weniger aufwendige Aktionen passieren.
Also kein Ja der Nein, so pauschal geht das nicht.
> In den Sourcen wird häufig von> int> nach> float> und wieder zurück nach> int> konvertiert ohne das eine direkte Operation auf den float Attribut> statt findet. Abgesehen davon, dass dies nicht unbedingt sinnvoll> und sauber ist, brauche ich argumente dafür nur Typenkonvertierung> zu betreiben, wenn diese auch wirklich nötig ist.
Wie wärs mit der obersten 'Direktive' aller Programmierer:
Schreibe sauberen, übersichtlichen Code!
Denselben Wert in 20 unterschiedlichen Variablen zu haben, die sich nur
durch den Datentyp unterscheiden, ist nicht wirklich dazu angetan, die
Übersicht und damit die Wartbarkeit zu erhöhen.
Nicht umsonst ist eine der Grundregeln zb im Datenbankdesign: Speichere
niemals denselben Wert in einer Datenbank an 2 verschiedenen Stellen ab.
Im besten Fall gewinnst du dadurch nichts, im schlimmsten Fall laufen
dir die Werte auseinander.
Und float ist sowieso so eine Sache für sich. Wenns nicht gerade
Millionen floats sind und somit das Speicherplatzargument greift, ist
double einem float bei numerischen Berechnungen praktisch immer
vorzuziehen.
> brauche ich argumente dafür nur Typenkonvertierung zu> betreiben, wenn diese auch wirklich nötig ist.
Letzten Endes musst du damit Casten.
Casts aber sind Waffen!
Man setzt sie niemals leichtfertig und unnötigerweise ein, oder gar nach
dem Muster: Oooch, da caste ich mal ein wenig.
Effektiv hebelt man mit einem expliziten Cast die Kontrolle der
Datentypen durch den Compiler aus und verlagert damit einen
Kontrollmechanismus vom Compiler zum Programmierer. Und das letzteres
des öfteren schon mal schief geht, weil eben der Programmierer Fehler
macht, der Compiler an dieser Stelle aber nicht, ist auch bekannt. ->
Lass den Compiler seinen Job tun, der macht den zuverlässiger.
Und wenn eine Typumwandlung ansteht, dann sollte der erste Lösungsansatz
darin bestehen, sich zu überlegen, ob man diese Umwandlung durch
Änderungen im Programmdesign nicht loswerden kann. Ekzessives Casten ist
oft ein Zeichen dafür, dass im Programm etwas Grundsätzliches nicht
stimmt. Es ist fast wie in der Physik: Wenn in einer Formel links und
rechts vom Gleichheitszeichen nicht die gleichen Einheiten (=Datentyp in
einem Programm) stehen, dann ist etwas faul. In diesem Sinne kann man
auch Datentypen sehen: Im Regelfall stimmen sie überein.
In diesem Sinne sind derartige Typumwandlungen als implizite Casts zu
sehen, die jeweils eigene Untersuchungen und Analysen benötigen, um
deren Zulässigkeit und Korrektheit zeigen zu können.
Vor allem: Wenn man Annahmen über die Eigenschaften von Daten auf
Maschinenebene macht (und genau das tut man bei bestimmten casts) sollte
es nicht zuviel verlangt sein sich den generierten Assemblercode (an den
man ja bei gcc usw. problemlos herankommt) mal zu überfliegen und ggf
mit dem von Alternativen zu vergleichen.
Danke für die Antworten. Mir fehlt aber leider "DAS Argument" dass den
Kunden dazu beweget daran etwas zu tun.
Ich persöhnlich würde sofort eine Änderung durchführen, wenn ein
Attribut gecastet wird aber dies nicht notwendig ist. Es gibt
ausreichend viele Codierrichtlinien die das Beschreiben (MISRA-C, PEP,
...). Aber auch ohne diese ist es einfach sauber wenn man typecasts nur
dann verwendet wenn sie notwendig sind und diese dann auch im Quellcode
beschreibt. Alles andere führt zu verwirrung und die Wartbarkeit, Pflege
und das Testen benötigt schon mal etwas mehr Zeit und kostet Geld.
Hier geht es aber um Modellbasierte Softwareentwicklung in Simulink und
Auto-Codegeneratoren. Modellentwickler interessiert ein Datentyp nicht.
Hauptsache es läuft :-) Lange Geschichte und ich möchte nicht zu sehr
darauf eingehen.
Um tatsächlich so einen Verbesserungsvorschlag machen zu können, ist
meine einzige Möglichkeit Ressourcenersparnisse darzustellen. SW-Design,
Strukturierung hat alles bisher nichts genützt.
Der Kunde wird nicht auf mich hören wenn ich ihm nur sage: "Wartbarkeit"
und "Fehleranfälligkeit". Wenn ein Typecast auch nur eine zusätzliche
ASM-Instruktion liefert habe ich gewonnen, da dieser Fall ca. 500 mal im
Code vorkommt (Auto-Code-Generator).
Carapa schrieb:> Hintergrund ist der, dass ich mit fremden Code anschaue und> Verbesserungsvorschläge suche.
Warum willst Du fremden Code verbessern? Das wäre doch in erster Linie
die Aufgabe desjenigen, der diesen Code schreibt.
Oder hast Du das Luxusproblem, dass Dein Anteil am Code zu 100%
implementiert, getestet und dokumentiert ist, und Du nicht weißt was
Du noch tun sollst? ;-)
Carapa schrieb:> Danke für die Antworten. Mir fehlt aber leider "DAS Argument" dass den> Kunden dazu beweget daran etwas zu tun.
Das ist leider ein großes Problem.
Es ist ein bischen so, wie das Aufräumen der Abstellkammer.
Es wird solange rausgeschoben, bis es gar nicht mehr anders geht. Erst
wenn man ein teures Gerät ein 2-tes mal unnötig kauft, weil man es in
der Kammer nicht findet, wird dann irgendwann aufgeräumt. Solange man
nur die M8 Schrauben für Pfennigbeträge nachkaufen muss, ist die
Grundhaltung halt oft: Ist jetzt auch kein Beinbruch, wenn ich die
Schraubenschachtel nicht finde.
> Der Kunde wird nicht auf mich hören wenn ich ihm nur sage: "Wartbarkeit"> und "Fehleranfälligkeit".
Das nächste Problem.
"Wieso, läuft doch?"
Wir alle (also die Profis hier), haben diesen Satz schon hundertemale
gehört. Erst wenn ein blöder Fehler teuer wird, ist dann plötzlich ein
Einlenken zu bemerken.
> Wenn ein Typecast auch nur eine zusätzliche> ASM-Instruktion liefert habe ich gewonnen, da dieser Fall ca. 500 mal im> Code vorkommt (Auto-Code-Generator).
Wie es Klaus oben schon gesagt hat: Man muss sich jeden Fall einzeln
ansehen.
Vielleicht kriegst du so einen Fuss in die Tür:
Die saubere Art einen Floating Point Typ an einen int zuzuweisen, geht
immer noch über runden
int = (int)( float + 0.5 );
(bzw. mit einer Fallunterscheidung für negative Zahlen). Es hat schon
Fälle gegeben, bei denen nach einer Berechnung rein mathematisch 2.0
rauskommen müsste, tatsächlich kommt aber 1.9999999 raus, was mit einer
naiven Zuweisung zu 1 wird.
OK, IEEE 754 (also das am häufigsten verwendete Floating Point Format),
macht die Zusicherung, dass bis zu einer gewissen Größe Ganzzahlen exakt
dargestellt werden können, so gesehen sollt bei
int = float = int
nix passieren, solange der int sich im Rahmen hält, Schwachsinn ist es
aber dennoch, zumal bei einem Cast von int nach float bzw. umgekehrt ja
auch Taktzyklen verbraucht werden, solange der Optimizer die komplette
Berechnung nicht überhaupt entfernt. Gerade bei Floating Point Dingen
wäre ich da aber vorsichtig. Zumindest früher haben Optimizer da ganz
gerne die Finger davon gelassen.
Vorallem ist ein Cast int->float und float->int nicht zwangsweise
verlustlos. also könnte es sein, das der vermeintlich nutzlose Code
doch etwas macht was auf den ersten Blick nicht ersichtlich ist. Ohne
Unittest sollte man an sowas garnichts ändern!
Nun meine Aufgabe möchte ich hier aus Geheimhaltungsgründen nicht näher
beschreiben. Mir ist auf jeden Fall nicht langweilig und leider bin ich
in meiner Rolle kein Entwickler, da ich sonst selbst solche Dinge
"gerade ziehen" würde.
Als C-Programmierer im Embedded Bereich lernt man im laufe der Zeit viel
darüber, was eine saubere Code-Strukturierung, sprich Design bedeutet
und wie wichtig es auch heute noch ist mir Ressourcen Strukturiert und
Dokumentiert vorzugehen. Spätestens nachdem man Module abgibt oder neue
übernimmt wird klar, wie wichtig das Thema ist.
Modellbasiertes Entwickeln, hier mittels Simulink, ist nicht für
Programmierer entwickelt sondern für Physiker, Kybernetiker, sonstige..
sprich für Entwickler die sich nicht mit einer Programmiersprache
Rumschlagen wollen. Da alles Bilder sind und keine Syntax soll es
angeblich einfacher sein. Diese Bilder werden von sogenannten
Auto-Code-Generatoren in die Sprache C überführt, und das Modell kann in
einem Mikrocontroller ausgeführt werden.
So ein Auto-Code-Generator ist einstellbar. D.h. der Anwender kann einem
bestimmen wie das Ausgangssignal seines Blockschaltbildes im generiertem
Quellcode erzeugt wird. Default ist allerdings bei Simulink der Typ
<c>float</c> und ein Modellentwickler interessiert sich in der Regel
nicht dafür. So nun haben wir ein Modell, dass mittlerweile richtig groß
ist und viele Entwickler wirken mit. Man muss sich vorstellen, dass so
eine Typänderung nicht von einem durchgeführt werden muss sondern von
vielen. D.h. man muss ein Druckmittel haben. Nur weil es schöner wäre
oder strukturierter, dass zählt nicht. Jemand der kein Verständnis für
Hand-Code Programmierung hat, wird sich davon nicht überzeugen lassen.
Man braucht Fakten wie: Ressourcenverbrauch. Ressourcen kosten Geld und
das ist nicht gut.
Ich werde mir mal im laufe der Woche das Disassembly zu der Typecast
geschichte anschauen. Dann habe ich es schwarz auf weiß oder muss das
Thema unterordnen.
Carapa schrieb:> Ich werde mir mal im laufe der Woche das Disassembly zu der Typecast> geschichte anschauen. Dann habe ich es schwarz auf weiß oder muss das> Thema unterordnen.
Wenn tatsächlich "nichts" gemacht wird, sollte jeder bessere Compiler
sowas auch komplett wegoptimieren.
Läubi .. schrieb:> Wenn tatsächlich "nichts" gemacht wird, sollte jeder bessere Compiler> sowas auch komplett wegoptimieren.
Womit sich auch die "Notwendigkeit" zu handeln erübrigen würde.
Konvertierungen zwischen ganzzahligen Datentypen und Fliesskommatypen
können u.A. bei PC-Prozessoren deutlich teuerer sein als einfache
Rechenoperationen wie add/sub/mul innerhalb einer Rechenart.
Grund: Die beiden Rechenarten finden u.U. in völlig verschiedenen
Bereichen des Cores und zwischen getrennten Registersets statt und der
Transport dazwischen kann ziemlich umständlich sein.
So existiert beim in 32-Bit Programmierung üblichen x87-Befehlsatz kein
Befehl für Register zu Register Konvertierung, das läuft aufwendig über
Store-Load.
Aber auch bei 64-Bit Programmierung mit dem SSE Befehlssatz wird man bei
AMD Prozessoren feststellen, dass je nach Modell eine oder auch beide
Richtungen mangels Datenpfad ebenfalls recht teuer sind und AMD zur
Optimierung an Stelle des direkten Einzelbefehls das aufgeteilte
Store-Load Verfahren empfiehlt.
Wenn es also keinen zwingenden Grund gibt, dann sollte man für abhängige
Operationsketten innerhalb einer Rechenart bleiben.
Carapa schrieb:> Ich werde mir mal im laufe der Woche das Disassembly zu der Typecast> geschichte anschauen. Dann habe ich es schwarz auf weiß oder muss das> Thema unterordnen.
Das Disassembly-Listing nützt aber nur begrenzt, wenn man die Kosten der
Operationen nicht im Blick hat. Einen guten Einblick darin gibt
http://www.agner.org/optimize/
Ohne das Projekt im Detail zu kennen, ist es imho garnicht so einfach
abzuschätzen, ob ein Weglassen der Floats wirklich was bringt. Wir
rechnen im Moment auch noch alles in fixed-point (> 1000 Seiten Doku),
aber manchmal wäre die Verwendung von float wesentlich eleganter und
Resourcenschonender, weil dann viele Umrechnungen wegfallen würden. Bei
einer Zielplattform mit einer einigermaßen performanten FPU wäre das in
Zukunft mein Ziel, weil wir damit viel Arbeit und gleichzeitig Resourcen
sparen könnten.
Auch die Code-Generatoren von Simulink sind so schlecht nicht, besonders
der Embedded Coder erzeugt erstaunlich gut lesbaren Code. Der RTW-Coder,
ähem, funktioniert schon, ist aber nicht für embedded gemacht. Wenn ihr
den einsetzt, ist die Suche nach Typecasts evtl. der falsche Ansatz.
Gerade die Tatsache, dass sich die Physiker und Konsorten eben nicht
um jedes Bit kümmern müssen, kann man mit modellbasierter SW-Entwicklung
durchaus nennenswert Zeit und Geld sparen. Zum Teil bezahlt man das zwar
mit höheren Anforderungen an die Plattform, aber wer legt sein Design
schon auf 99% Ausnutzung aus, wenn modellbasiert entwickelt werden soll.
Bei PC-Prozessoren mit den heute sehr leistungsfähigen FPUs ergibt der
Ersatz von skalaren Fliesskommaoperationen durch Festkommaoperationen in
meinen Augen keinen Sinn, jedenfalls was die Performance angeht. Man
verliert dabei wohl eher an Performance als dass man gewinnt.
Karl Heinz Buchegger schrieb:> Und float ist sowieso so eine Sache für sich. Wenns nicht gerade> Millionen floats sind und somit das Speicherplatzargument greift, ist> double einem float bei numerischen Berechnungen praktisch immer> vorzuziehen.
Wenn es nicht grad um Prozessoren der PC-Klasse geht kann der
Performanceunterschied zwischen float und double deutlich spürbar sein.
Mittlerweile ist ja zulässig, dass ein C Compiler nicht mehr implizit
alles in double rechnet und nicht jede FPU ist bei double ähnlich
schnell. Manche Controller haben eine auf float Operationen optimierte
FPU. Und wenn sie überhaupt keine haben, dann sowieso.
Wenn man andererseits an die Kante der Leistungsfähigkeit heutiger
Prozessoren geht und beim PC mit SSE SIMD-Operationen arbeitet, dann
kann float ebenfalls kräftig an Performance gewinnen. Auch ganz ohne
Speicherplatzeffekte.
Carapa schrieb:> Hier geht es aber um Modellbasierte Softwareentwicklung in Simulink und> Auto-Codegeneratoren.
Wenn sich nun herausstellt, dass ein paar "casts" überflüssig sind, wie
groß sind die Chancen, die Generatoren wegzulassen bzw. zu verändern?
Ich verstehe die Motivation nicht. Wenn es modellgetrieben ist,
interessiert in erster Linie die Güte des Modells.
Carapa schrieb:> man muss ein Druckmittel haben. Nur weil es schöner wäre> oder strukturierter, dass zählt nicht. Jemand der kein Verständnis für> Hand-Code Programmierung hat, wird sich davon nicht überzeugen lassen.> Man braucht Fakten wie: Ressourcenverbrauch. Ressourcen kosten Geld und> das ist nicht gut.
Das sehe ich differenzierter. Beide Ansätze haben ihre Berechtigung. Was
ist Dein Ziel? Generatoren verändern oder ganz weglassen? Wie viel
Kosten denn die Ressourcen und welche sind das konkret? Was kostet dazu
im Vergleich die Umstellung des Entwicklungsprozesses eines "richtig
großen Projektes mit vielen Entwicklern"? Sind das
"Schönheitsoperationen" oder wird der Flash/RAM/was-auch-immer knapp?
Von welchen Stückzahlen reden wir hier?
Kurz: Wie sieht denn der "business case" bzw. dessen Berechnung aus?
Vielleicht wäre es wirksamer, für zukünftige Projekte Regeln vorzugeben,
wie die Datentypen vom Entwickler ausgesucht werden sollen? Wenn die
offizielle Regelung bisher "jeder wie er will" oder "lass halt die
Standardvoreinstellung, passt schon" ist, dann liegt das Problem doch
eher in den Programmierrichtlinien der Firma, möchte ich mal vermuten.