Boost.Python: Wrap-Funktionen zum Freigeben der GIL

8

Ich arbeite gerade mit Boost.Python und möchte etwas Hilfe, um ein heikles Problem zu lösen.

Kontext

Wenn eine C ++ - Methode / -Funktion mit Python in Verbindung gebracht wird, muss sie die GIL (Global Interpreter Lock) freigeben, damit andere Threads den Interpreter verwenden können. Auf diese Weise kann der Interpreter von anderen Threads verwendet werden, wenn der Python-Code eine C ++ - Funktion aufruft. Fürs Erste sieht jede C ++ - Funktion folgendermaßen aus:

%Vor%

Um es an boost python zu übergeben, mache ich:

%Vor%

Problem

Dieses Schema funktioniert gut, aber es impliziert, dass module.cpp ohne guten Grund von Boost.Python abhängt. Im Idealfall sollte nur python_exposure.cpp von Boost.Python abhängen.

Lösung?

Meine Idee war, mit Boost.Function zu spielen, um die Funktionsaufrufe so zu verpacken:

%Vor%

Hier wäre wrap verantwortlich dafür, die GIL während des Aufrufs von myfunction zu entsperren. Das Problem mit dieser Methode ist, dass wrap dieselbe Signatur wie myfunction haben muss, was ziemlich viel bedeutet, dass Boost.Function ...

erneut implementiert wird

Ich wäre sehr dankbar, wenn jemand einen Vorschlag für dieses Problem hätte.

    
Rippalka 04.09.2013, 15:46
quelle

2 Antworten

12

Das Anzeigen von Funktoren als Methoden wird nicht offiziell unterstützt >. Der unterstützte Ansatz wäre, eine Nicht-Member-Funktion verfügbar zu machen, die an die Member-Funktion delegiert. Dies kann jedoch zu einer großen Anzahl von Codeblöcken führen.

Soweit ich das beurteilen kann, schließt die Implementierung von Boost.Python Funktoren nicht explizit aus, da Instanzen von python::object als Methode verfügbar gemacht werden können. Boost.Python stellt jedoch einige Anforderungen an den Objekttyp, der als Methode verfügbar gemacht wird:

  • Der Funktor ist CopyConstructible.
  • Der Funktor kann aufgerufen werden. I.e. Instanz o kann o(a1, a2, a3) .
  • genannt werden
  • Die Call-Signatur muss zur Laufzeit als Metadaten verfügbar sein. Boost.Python ruft die Funktion boost::python::detail::get_signature() auf, um diese Metadaten zu erhalten. Die Meta-Daten werden intern verwendet, um den korrekten Aufruf einzurichten, sowie um von Python nach C ++ zu senden.

Die letzte Anforderung ist, wo es komplex wird. Aus irgendeinem Grund, der mir nicht sofort klar ist, ruft Boost.Python get_signature() über eine qualifizierte ID auf und verhindert so das argumentabhängige Nachschlagen. Daher müssen alle Kandidaten für get_signature() vor dem Definitionskontext der aufrufenden Vorlage deklariert werden. Die einzigen Überladungen für get_signature() , die berücksichtigt werden, sind beispielsweise diejenigen, die vor der Definition von Vorlagen deklariert wurden, die sie aufrufen, z. B. class_ , def() und make_function() . Um dieses Verhalten zu berücksichtigen, muss beim Aktivieren eines Funktors in Boost.Python eine get_signature() -Überladung angegeben werden, bevor Boost.Python eingeschlossen wird, oder explizit eine Meta-Sequenz bereitgestellt werden, die die Signatur für make_function() darstellt.

Lassen Sie uns einige Beispiele für die Aktivierung der Funktor-Unterstützung durcharbeiten und stellen Funktoren zur Verfügung, die Wächter unterstützen. Ich habe mich entschieden, keine C ++ 11-Funktionen zu verwenden. Daher wird es einen Standardcode geben, der mit variablen Vorlagen reduziert werden könnte. Darüber hinaus verwenden alle Beispiele dasselbe Modell, das zwei Nichtmitgliedsfunktionen und eine spam -Klasse mit zwei Memberfunktionen bereitstellt:

%Vor%

Aktivieren von boost::function

Bei Verwendung der bevorzugten Syntax für Boost.Function Die Zerlegung der Signatur in Meta-Daten, die Boost.Python-Anforderungen erfüllen, kann mit Boost.FunctionTypes . Hier ist ein vollständiges Beispiel, das boost::function funktors als Boost.Python-Methode verfügbar macht:

%Vor%

Und seine Verwendung:

%Vor%

Wenn ein Funktor bereitgestellt wird, der eine Member-Funktion aufruft, muss die angegebene Signatur das Äquivalent der Nicht-Member-Funktion sein. In diesem Fall wird int(spam::*)(int) zu int(spam&, int) .

%Vor%

Auch können Argumente mit boost::bind an die Funktoren gebunden werden . Beispielsweise muss der Aufruf von example.times_two() kein Argument bereitstellen, da 21 bereits an den Funktor gebunden ist.

%Vor%

Benutzerdefinierter Funktor mit Schutzfunktionen

Nach dem obigen Beispiel kann man benutzerdefinierte Funktortypen mit Boost.Python verwenden. Lassen Sie uns einen Funktor namens guarded_function erstellen, der RAII verwendet und nur die Wrapped-Funktion während der Lebensdauer des RAII-Objekts aufruft.

%Vor%

Das guarded_function bietet eine ähnliche Semantik wie die with Anweisung von Python. Um mit den Namen des Boost.Python-API-Namens zu bleiben, bietet eine with() C ++ - Funktion eine Möglichkeit, Funktoren zu erstellen.

%Vor%

Dadurch können Funktionen freigelegt werden, die auf nicht-intrusive Weise mit einem Guard laufen:

%Vor%

Darüber hinaus bietet die with() -Funktion die Möglichkeit, die Funktionssignaturen abzuleiten, sodass die Metadatensignatur Boost.Python explizit zur Verfügung gestellt werden kann, anstatt boost::python::detail::get_signature() zu überladen.

Hier ist das vollständige Beispiel mit zwei RAII-Typen:

  • no_gil : Gibt GIL im Konstruktor frei und ruft GIL in destructor erneut ab.
  • echo_guard : Druckt in Konstruktor und Destruktor.
%Vor%

Und seine Verwendung:

%Vor%

Beachten Sie, wie mehrere Wächter mithilfe eines Containertyps wie boost::tuple :

bereitgestellt werden können %Vor%

Bei Aufruf in Python erzeugt example.times_two(21) die folgende Ausgabe:

%Vor%     
Tanner Sansbury 06.09.2013, 01:14
quelle
2

Wenn jemand interessiert ist, hatte ich ein kleines Problem mit dem Code von Tanner Sansbury , wenn ich sein letztes Arbeitsbeispiel benutze. Aus irgendeinem Grund hatte ich immer noch das Problem, dass er im letzten generierten boost::function eine falsche Signatur hatte:

%Vor%

auch beim Überladen von boost::python::detail::get_signature() . Verantwortlich dafür war boost::function_types::components ; Es hat einen Standard-Template-Parameter ClassTranform = add_reference<_> , der diese Klassenreferenz erstellt. Um dies zu beheben, änderte ich einfach die mpl_signature struct wie folgt:

%Vor%

Und jetzt funktioniert alles wie ein Zauber.

Wenn jemand bestätigen kann, dass dies in der Tat die richtige Lösung ist, wäre ich interessiert:)

    
Rippalka 20.11.2013 18:30
quelle