Frage

Ich habe eine C-Bibliothek mit zahlreichen mathematischen Routinen für den Umgang mit Vektoren, Matrizen, Quaternionen und so weiter. Es muss in C bleiben, weil ich es oft für eingebettete Arbeit verwenden und als Lua-Erweiterung. Darüber hinaus habe ich C ++ Klassen-Wrapper für bequemere Objektverwaltung und Betreiber Überlastung für mathematische Operationen mit dem C-API zu ermöglichen. Der Wrapper nur aus einer Header-Datei besteht und wie viel Einsatz auf inlining ist wie möglich gemacht.

Gibt es eine beträchtliche Strafe für das Einwickeln des C-Code im Vergleich zu portieren und inlining die Umsetzung direkt in die C ++ Klasse? Diese Bibliothek wird in zeitkritischen Anwendungen eingesetzt. So funktioniert der Schub durch die Beseitigung indirection für die Wartung Kopfschmerzen von zwei Ports kompensieren?

Beispiel von C-Schnittstelle:

typedef float VECTOR3[3];

void v3_add(VECTOR3 *out, VECTOR3 lhs, VECTOR3 rhs);

Beispiel für C ++ Wrapper:

class Vector3
{
private:
    VECTOR3 v_;

public:
    // copy constructors, etc...

    Vector3& operator+=(const Vector3& rhs)
    {
        v3_add(&this->v_, this->v_, const_cast<VECTOR3> (rhs.v_));
        return *this;
    }

    Vector3 operator+(const Vector3& rhs) const
    {
        Vector3 tmp(*this);
        tmp += rhs;
        return tmp;
    }

    // more methods...
};
War es hilfreich?

Lösung

Ihre Wrapper selbst werden inline sein, erfordert jedoch Ihre Methode zur C-Bibliothek wird in der Regel nicht. (Dies würde Link-Time-Optimierungen erfordern, die technisch möglich sind, aber AFAIK rudimentär bestenfalls in der heutigen Tool)

Im Allgemeinen ist ein Funktionsaufruf als solche nicht sehr teuer. Die Zykluskosten hat sich in den letzten Jahren zurückgegangen, und es kann leicht vorhergesagt werden, so dass die der Anruf Strafe als solche ist zu vernachlässigen.

Allerdings öffnet inlining die Tür zu mehr Optimierungen: Wenn Sie v = a haben + b + c, Ihre Wrapper-Klasse zwingt die Erzeugung von Stack-Variablen, während für reihten Anrufe kann die Mehrheit der Daten in der FPU gehalten werden Stapel. Auch inlined-Code ermöglicht die Vereinfachung Anweisungen, wenn man bedenkt konstante Werte und vieles mehr.

Während also die Maßnahme, bevor Sie investieren Regel gilt, würde ich hier etwas Raum für Verbesserungen erwarten.


Eine typische Lösung ist, die C implementaiton in ein Format zu bringen, dass es entweder als Inline-Funktionen oder als „C“ Körper verwendet werden kann:

// V3impl.inl
void V3DECL v3_add(VECTOR3 *out, VECTOR3 lhs, VECTOR3 rhs)
{
    // here you maintain the actual implementations
    // ...
}

// C header
#define V3DECL 
void V3DECL v3_add(VECTOR3 *out, VECTOR3 lhs, VECTOR3 rhs);

// C body
#include "V3impl.inl"


// CPP Header
#define V3DECL inline
namespace v3core {
  #include "V3impl.inl"
} // namespace

class Vector3D { ... }

Das macht wahrscheinlich nur Sinn für ausgewählte Methoden mit comparedly einfachen Körpern. Ich würde die Methoden zu einem eigenen Namensraum für die Umsetzung ++ C bewegen, wie Sie in der Regel sie nicht direkt benötigen.

(Beachten Sie, dass die Inline nur ein Compiler Hinweis ist, ist es nicht die Methode zwingt inlined werden. Aber das ist gut so: Wenn die Codegröße einer inneren Schleife des Befehls-Cache überschreitet, inlining leicht Leistung weh tut)

Ob der Pass / Return-by-Referenz kann auf die Stärke des Compilers gelöst werden, hängt, ich habe viele gesehen, wo     foo (X * out) Kräfte Stapelvariablen, während     X foo () halten sich Werte in den Registern.

Andere Tipps

Wenn Sie nur die C-Bibliothek Anrufe in C ++ Klassenfunktionen Verpackung (in anderen Worten, die C ++ Funktionen tun nichts anderes als Aufruf C-Funktionen), dann wird der Compiler diese Anrufe optimieren, so dass es keine Leistungseinbuße ist.

Wie bei jeder Frage über die Leistung, werden Sie gesagt werden, zu messen, um Ihre Antwort zu bekommen (und das ist die streng richtige Antwort).

Aber als Faustregel für einfache Inline-Methoden, die tatsächlich inlined werden können, werden Sie keine Leistungseinbuße sehen. Im Allgemeinen ist eine Inline-Methode, die nichts tut, aber den Anruf auf eine andere Funktion übergeben ist ein großer Kandidat für inlining.

Aber auch wenn Ihre Wrapper-Methoden nicht inlined wurden, ich vermute, dass Sie keine Leistungseinbuße bemerken würden - nicht einmal eine messbare ein - es sei denn, die Wrapper-Methode in einer kritischen Schleife aufgerufen wurde. Selbst dann wäre es wahrscheinlich nur messbar, wenn die verpackte Funktion selbst nicht viel Arbeit zu tun hat.

Diese Art der Sache ist, über die letzte Sache besorgt zu sein. Erste Sorge über Ihren Code korrekt, wartbar zu machen, und dass Sie geeignete Algorithmen verwenden.

Wie bei allem üblich Optimierung im Zusammenhang, die Antwort ist, dass Sie die Leistung messen müssen sich, bevor Sie wissen, ob die Optimierung lohnt.

  • Benchmark zwei verschiedene Funktionen, ruft man die C-Stil-Funktionen direkt und ein weiterer Aufruf durch den Wrapper. Sehen Sie, welche läuft man schneller, oder wenn die Differenz innerhalb der Fehlermarge Ihrer Messung ist (was bedeuten würde, gibt es keinen Unterschied man messen kann).
  • Schauen Sie sich den Assembler-Code durch die beiden Funktionen im vorherigen Schritt erzeugt (auf gcc, Verwendung -S oder -save-temps). Prüfen Sie, ob der Compiler etwas Dummes getan hat, oder wenn die Wrapper jede Leistung Fehler haben.

Es sei denn, der Leistungsunterschied zu groß ist für nicht-Wrapper verwenden, Neuimplementierung ist keine gute Idee, da man die Einführung Bugs riskieren (was auch Ergebnisse, die gesund aussehen dazu führen könnten, sind aber falsch). Auch wenn der Unterschied groß ist, wäre es einfacher und weniger riskant sein, nur daran denken, C ++ mit C sehr kompatibel ist und die Bibliothek im C-Stil verwenden, auch in C ++ Code.

Ich glaube nicht, Sie werden viele perf Unterschied bemerken. Angenommen, Ihre Zielplattform unterstützen alle Ihre Datentypen

Ich bin Codierung für den DS und ein paar anderen ARM-Geräte und Gleitpunkte sind böse ... Ich musste typedef float FixedPoint <16,8>

Wenn Sie besorgt sind, dass die Overhead-Funktionen des Anrufers Sie verlangsamt, warum nicht testen inlining den C-Code oder in Makros drehen?

Auch, warum nicht die const Korrektheit des C-Codes verbessern, während Sie es sind - const_cast wirklich sparsam verwendet werden sollte, vor allem an Schnittstellen, die Sie steuern

.
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top