Optimieren von Programmen mit C/C++

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
BlackJack

Beitragvon BlackJack » Samstag 3. November 2007, 03:11

:oops: Auf die Selbstreferenzierung bei der Struktur hatte ich nicht geachtet.

Wenn hier ein Beispiel gesucht wird, wie man Anfängern zeigen kann wie C in Python eingebunden wird, dann sollte das auch "wasserdicht" sein, also kein Speicherleck enthalten.

Und das es sich hier um MS C++ handelt, sollte stärker herausgestellt werden. Bei der Beschreibung und dem "falschen" Include in der ersten Zeile kann man leicht übersehen, dass es kein C ist. Wobei es vielleicht schon ausreicht aus dem ``new`` ein `malloc()` zu machen.
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Beitragvon rayo » Samstag 3. November 2007, 10:32

Hi BlackJack

Ich denke Gerold kann auf dieser Basis ein sauberes Beispiel machen, eins für Zahlen hat er ja und er möchte eines für Unicodestring.

Irgendwo ist der Anfang, darum habe ich auch extra beim Beispiel geschrieben dass es nicht so brauchbar ist.

Gruss
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5554
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Telfs (Tirol)
Kontaktdaten:

Beitragvon gerold » Samstag 3. November 2007, 11:35

Hallo BlackJack!
Hallo Rayo!

Bitte nicht streiten. :? Das Beispiel von Rayo hat mich schon um einiges weiter gebracht. Auch das Wissen, dass es noch nicht ideal ist und man sich noch überlegen muss, wie man einfach so ein Array, ohne Speicherleck an Python übergeben kann, bringt mich weiter. Ich bin zwar nicht der Hellste, aber wenn ich mich wieder ein wenig in C++ einlese, dann bekomme ich daraus sicher ein Beispiel für den GCC gebacken. Und mehr als ein funktionierendes Beispiel wollte ich ja auch nicht. Das mit dem GCC ist wichtig, weil ich das demnächst bei einem der nächsten LUGT-Stammtische (Linux User Group Tirol) vortragen möchte. Es ist mir natürlich auch wichtig, dass das Beispiel mindestens unter Linux und Windows funktioniert.

Vielleicht sollte ich auch mal erklären, warum ich mich so um ein einfaches, funktionierendes Beispiel bemühe:

Viele Firmen setzen C++ ein und weigern sich auch nur daran zu denken, das Team mit einem Python-Progammierer zu erweitern. Die anderen Programmierer können ja nicht Python. Wie sollen die C++-Programmierer denn mit dem Python-Programmierer zusammenarbeiten?
Da hat man einfach einen besseren Stand als Python-Programmierer, wenn man aufzeigen kann, dass geschwindigkeitskritische Teile von Python auf C++ ausgelagert werden können. Und dass jeder der bereits angestellten C++-Programmierer ohne weiteres dazu fähig ist -- auch ohne sich mit Python befassen zu müssen.

So profitiert die Firma zukünftig von der schnellen Entwicklung von Anwendungen mit Python und hat gleichzeitig die Sicherheit, dass eventuell auftretende Geschwindigkeitsengpässe sehr leicht mit C oder C++ entschäft werden können.

Und um diesen Argumenten mehr Gewicht zu verleihen, braucht man ein paar funktionierende, leicht zu durchschauende Beispiele, die man C-Programmierern zeigen kann. So einfach ist das. Da auch der Python-Programmierer nicht dumm sterben soll, ist es wichtig, dass auch der Python-Programmierer das C-Beispiel durchschauen kann.

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Beitragvon HWK » Samstag 3. November 2007, 12:20

gerold hat geschrieben:Auch das Wissen, dass es noch nicht ideal ist und man sich noch überlegen muss, wie man einfach so ein Array, ohne Speicherleck an Python übergeben kann, bringt mich weiter.
Am einfachsten könnte man den Speicher für das Array ja in einer zweiten C++-Funktion zurückgeben, z.B. so (ungetestet):

Code: Alles auswählen

__declspec(dllexport) void delete_nodes(word_node_p root) {
   word_node_p temp;
   do {
      temp = root->next;
      delete root;
      root = temp;
   } while (root);
}
Die Funktion wird einfach aus Python mit dem Array als Argument aufgerufen, wenn man das Original-Array nicht mehr braucht.
MfG
HWK
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5554
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Telfs (Tirol)
Kontaktdaten:

Beitragvon gerold » Samstag 3. November 2007, 12:51

Hallo!

Blöde Frage: Da wir ja schon bei C++ sind. Könnte man nicht die Wörter in ein vector-Objekt schreiben und einfach diesen vector an Python zurück geben? Gibt es so etwas auch in C?

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs

Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
BlackJack

Beitragvon BlackJack » Samstag 3. November 2007, 13:05

@gerold: Bei der Motivation würde ich gänzlich von `ctypes` abraten. Um C++ mit Python zu verbinden ist das Modul nahezu komplett ungeeignet. Auf der C++-Seite müsste man immer noch zusätzliche eine C-API zur Verfügung stellen, die man dann über `ctypes` ansprechen kann. Damit überzeugst Du bestimmt keine C++-Programmierer. Die dürften eher schreiend davon rennen.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5554
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Telfs (Tirol)
Kontaktdaten:

Beitragvon gerold » Samstag 3. November 2007, 17:56

BlackJack hat geschrieben:@gerold: Bei der Motivation würde ich gänzlich von `ctypes` abraten. Um C++ mit Python zu verbinden ist das Modul nahezu komplett ungeeignet. Auf der C++-Seite müsste man immer noch zusätzliche eine C-API zur Verfügung stellen, die man dann über `ctypes` ansprechen kann. Damit überzeugst Du bestimmt keine C++-Programmierer. Die dürften eher schreiend davon rennen.

Hallo BlackJack!

Das ist deprimierend. :cry: Ich muss noch einmal darüber nachdenken.

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs

Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5554
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Telfs (Tirol)
Kontaktdaten:

Beitragvon gerold » Montag 5. November 2007, 09:59

BlackJack hat geschrieben:Bei der Motivation würde ich gänzlich von `ctypes` abraten. Um C++ mit Python zu verbinden ist das Modul nahezu komplett ungeeignet.

Hallo!

Für C++. Besser bzw. empfehlenswert? http://www.boost.org/libs/python/doc/tu ... osing.html

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs

Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Panke
User
Beiträge: 185
Registriert: Sonntag 18. März 2007, 19:26

Beitragvon Panke » Dienstag 6. November 2007, 16:47

Frag doch mal hier im Forum nach. Vielleicht hast du ja Glück.
windner
User
Beiträge: 76
Registriert: Freitag 19. Oktober 2007, 11:25

Beitragvon windner » Donnerstag 8. November 2007, 20:14

Auf c++.de kennt sich leider kaum jemand mit Python aus.

Was das Problem angeht: get_word ist ein denkbar schlechtes Beispiel für eine Effizienzsteigerung durch Neufassen in C, weil die meiste Rechenzeit in jedem Fall darauf verwendet wird, die Python-Liste zu erzeugen (oder den Iterator zu bedienen).

Den Speicher in dem C-Modul selbst zu allozieren, ist nicht nötig. Man kann array.array verwenden, und dann mit array.array.buffer_info die adresse UND die Länge zu erfahren. Folglich muss man sich um den Terminator keine Sorge machen, zumal ja Unicode-Strings viele Nullbytes enthalten.

Falls man sich fürchtet, auf den Speicher keinen Lesezugriff zu erhalten, macht man ein beherztes su (auf Windows python.exe als SYSTEM ausführen).

Dann kann man ein C-Modul nach meinem Schema erzeugen (siehe unten), mit distutils.core.setup übersetzen und mit import oder imp.load_module einbinden (ich mache das schon länger on-the-fly).

Ich gehe der Einfachheit davon aus, dass die Daten als 2-Byte-Unicode vorliegen, mit UTF-8 hätte man da noch ein bißchen Arbeit. Natürlich sollte man eine Bibliothek für den jeweiligen Zeichensatz einsetzen. Das wollte ich mir ersparen, deshalb geht das Beispiel nur mit Zeichen C, für die gilt, dass ord(c)<128. Diese bestehen in 2 Byte-Unicode immer aus dem ASCII-Wert und einem Nullbyte.

Unter diesen Einschränkungen ist alles theoretisch ganz einfach:

Code: Alles auswählen

#include <Python.h>
#include <ctype.h>

// unser Zeiger soll möglichst nicht überlaufen können
typedef LONGEST long;

// das sollte immer stimmen:
typedef TYPE_1_BYTE char;

// short wird schon 2 Bytes haben
typedef TYPE_2_BYTES short;

// ich muss wissen, wie viele Bytes ein Zeichen hat
// gehe hier davon aus, dass ich die Daten als Unicode Little Endian bekomme,
// mit genau 2 Bytes, also zuerst das Zeichen-Byte, dann das Nullbyte
#define CHAR_SIZE (2)

// dann habe ich einen Zeiger auf den Speicher von array.array
// den behandle ich als Ganzzahl, muss also meine eigene Speicherarithmetik
// definieren:
#define INC_PT(pointer) (pointer += CHAR_SIZE)
#define DEC_PT(pointer) (pointer -= CHAR_SIZE)

// STAR ahmt den *-Operator nach
#define STAR(pointer) ((TYPE_2_BYTES)((TYPE_2_BYTES)((*pointer)*256)+(TYPE_2_BYTES)(*(pointer+1))))

// dann noch ein Makro, um zu die Zeichen zu erkennen
// ich verwende isalpha aus ctype.h
#define IS_CHAR(pointer) (isalpha(STAR(pointer)>>8))

static PyObject * mod_function(PyObject *self, PyObject *args) {
   
    LONGEST pointer, bufsiz;
    TYPE_2_BYTES c;
   
    // 2 Parameter: Adresse und Größe in [2 Bytes] erwartet
    PyArg_ParseTuple(args, "ll", &pointer, &bufsiz);
   
    // hier eine Python-Liste erzeugen
   
    // jetzt kann ich über die Zeichen iterieren
    while (--bufsiz) {
        // das nächste Zeichen holen
        c = STAR(pointer)
       
        // hier mit IS_CHAR prüfen, ob wir ein Wort bekommen und dieses
        // zusammensetzen (ich klammere das aus)
       
        // dann c in die Liste eintragen
       
        // dann den Pointer inkrementieren
        INC_PT(pointer);
    }
   
    return /*Referenz auf die Liste*/;

}

// der Rest ist gottgegeben
static PyMethodDef modMethods[] = {
    {"get_word",  mod_function, METH_VARARGS, ""},
    {NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC initmod(void) {
    Py_InitModule("mod", modMethods);
}
int main(int argc, char *argv[]) {
    Py_SetProgramName(argv[0]);
    Py_Initialize();
    inittempmod();
    return 0;
}


Imho ist die beste Variante, einem C-Programmierer einen neuen Weg zu zeigen, ihn diesen zu Fuß entlang zu schicken. Ein erfahrener C-Programierer würde wahrscheinlich als nächstes Makros schreiben, die Zeiger auf die richtige Größe erzeugen, damit er die herkömliche Zeigerarithmetik verwenden kann.
Zuletzt geändert von windner am Donnerstag 8. November 2007, 20:40, insgesamt 2-mal geändert.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5554
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Telfs (Tirol)
Kontaktdaten:

Beitragvon gerold » Donnerstag 8. November 2007, 20:34

Hallo windner!

Vielen Dank für dein Beispiel!

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs

Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
windner
User
Beiträge: 76
Registriert: Freitag 19. Oktober 2007, 11:25

Beitragvon windner » Donnerstag 8. November 2007, 20:48

Das ist genau das, was mich die letzten Wochen beschäftigt hat.

Möchte betonen, daß es kein Problem ist, den Übersetzungsvorgang in Python zu automatisieren. Ich habe am Wochenende ein Modul geschrieben, das eine Funktion cf implementiert, die (analog zu lambda) eine anonyme Funktion aus C-Code erzeugt, kompiliert und zurückgibt. Dazu muss ich ein tempöres C-Modul erzeugen und sinnvollerweise irgendwann wieder löschen. Bis auf das Löschen läuft schon alles. (Ideen dazu? Mein Thread hat 0 Antworten... http://www.python-forum.de/topic-12437.html?highlight=)
windner
User
Beiträge: 76
Registriert: Freitag 19. Oktober 2007, 11:25

Beitragvon windner » Donnerstag 8. November 2007, 21:04

Hab mir das Ganze vor dem Schlafengehen noch einmal angeschaut. Laufen wird das eher nicht. Vergiss lieber, was ich da oben gepostet hab.

Besser geht's schon so:

Code: Alles auswählen

#include <Python.h>
#include <ctype.h>

Py_UNICODE *__p = NULL;
long __bufsiz = 0, __index = 0;

#define WORDBUFSIZ (1024)
// scheinbar doch Big-Endian
#define IS_CHAR(value) (isalpha(value))
#define agetc() ((long)(__index < __bufsiz ? *(__p + __index++) : EOF))
#define aspyc() ((long)(__index < __bufsiz ? *(__p + __index) : EOF))

Py_UNICODE *agetword() {
    static Py_UNICODE word[WORDBUFSIZ];
    int i = 0;
   
    if (aspyc() == EOF) return NULL;
    if (IS_CHAR(aspyc()))
        while (IS_CHAR(aspyc()) && aspyc() != EOF)
            word[i++] = agetc();
    else word[i++] = agetc();
    word[i] = 0;
   
    return word;
}

static PyObject * mod_function(PyObject *self, PyObject *args) {
    long lpointer, bufsiz;
    PyArg_ParseTuple(args, "ll", &lpointer, &bufsiz);
    __p = (Py_UNICODE*)lpointer; __bufsiz = bufsiz;
   
    PyObject *list = PyList_New((Py_ssize_t)0);
    Py_UNICODE *word;
    while((word = agetword()) != NULL)
        PyList_Append(list, Py_BuildValue("u", word));
       
    return list;
}

static PyMethodDef modMethods[] = {
    {"get_word",  mod_function, METH_VARARGS, ""},
    {NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC initmod(void) {
    Py_InitModule("mod", modMethods);
}
int main(int argc, char *argv[]) {
    Py_SetProgramName(argv[0]);
    Py_Initialize();
    initmod();
    return 0;
}


Hab ich (auf Windows mit Python 2.5 und mingw32) ohne Probleme übersetzen können:

Code: Alles auswählen

>>> import mod
>>> import array
>>> a = array.array('u')
>>> a.fromunicode(u'eins zwei drei\nvier fuenf sechs')
>>> print mod.get_word(*a.buffer_info())
[u'eins', u' ', u'zwei', u' ', u'drei', u'\n', u'vier', u' ', u'fuenf', u' ', u'
sechs']
>>>
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5554
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Telfs (Tirol)
Kontaktdaten:

Beitragvon gerold » Freitag 9. November 2007, 15:30

Umgeleitet von http://www.python-forum.de/post-81834.html#81834

windner hat geschrieben:Wie gehts btw den C-Extensions?

Hallo windner!

Nicht gut! Ich dachte nicht, dass C solche extreme Einschränkungen hat. Ich dachte, C sei C++ ziemlich ähnlich, aber dass solche Kleinigkeiten (ein paar Wörter auseinander nehmen) so schwierig sein würden, hatte ich nicht gedacht.

Ich werde mich in ein paar Wochen wieder ein wenig in C++ einlesen (vorher muss ich mir aber, berufsbedingt, nochmal JavaScript rein ziehen), und dann gehe ich den Thread (Optimieren mit C) noch einmal an. Diesmal werde ich mich aber nicht mehr auf C einlassen, sondern sofort auf C++ los gehen. Dein Beispiel hat mir ein paar Schwierigkeiten aufgezeigt, die es zu umgehen gilt. Aber darauf werde ich in ein paar Wochen zurück kommen.

Mein Ziel ist es weiterhin, eingefleischte C/C++-Programmierer davon zu überzeugen, dass reine Python-Programmierer eine gute Ergänzung zum C/C++-Team sind und einen Gewinn für die Firmen darstellen. Das Haupt-Gegenargument ist immer noch, dass viele glauben, Python-Programme wären Schnecken gegenüber C-Programmen. Sind sie teilweise ja auch, aber wenn ich beweisen kann, dass evt. auftretende Schwachstellen sehr einfach durch C/C++-Code ergänzt werden können, dann haben Python-Programmierer einen besseren Stand. Denn, dass man mit Python sehr viel schneller eine Anwendung schreiben kann, ist scheinbar allen klar.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs

Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
BlackJack

Beitragvon BlackJack » Freitag 9. November 2007, 16:48

Ich weiss nicht ob man eingefleischte C++ Programmierer wirklich überzeugen kann. Also umgekehrt ist das bei mir zumindest so überhaupt nicht möglich ─ ich finde C++ und die Einstellung der Programmierer grauenhaft. Unter anderem wegen diesem C++ ist das schnellste überhaupt, alles andere ist total langsam weil soweit von der Maschine weg. Die wollen die totale Kontrolle und wollen oft auf biegen und brechen ihr heiliges RAII und Destruktoren in Python umsetzen.

Wer C++ und Templates mag und damit freiwillig programmiert, hat ganz offensichtlich keinen Bedarf an einer einfachen, verständlichen Programmiersprache, also ziehen die üblichen Argumente für Python nicht. ;-)

Wer ist online?

Mitglieder in diesem Forum: Google [Bot], schurmak, snafu