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.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5554
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Telfs (Tirol)
Kontaktdaten:

Optimieren von Programmen mit C/C++

Beitragvon gerold » Freitag 2. November 2007, 09:42

Hallo!

Ich habe mal wieder eine ganz spezielle Frage, und ich hoffe, dass ihr mir dabei helfen könnt. Es handelt sich um eine rein theoretische Frage. Es geht um die Optimierung von Python, in dem ein Teil der Arbeit an C oder C++ übergeben wird. Wir schreiben ja immer, wenn jemand nach der Geschwindigkeit von Python fragt, dass man Python mit C oder C++ erweitern kann. Jetzt möchte ich wissen, wie das in der Praxis aussehen könnte. Mir ist dabei aber wichtig, dass man kein C-Könner sein muss. Jemand der mit Müh und Not eine Funktion in C zum Laufen bekommt, sollte damit auch zurecht kommen. Deshalb dachte ich zuerst mal an ctypes.

Wie macht man so etwas mit ctypes?

Wie man in diesem Thread http://www.python-forum.de/topic-7814.html sieht, hatte ich mich schon mal kurz damit befasst. Allerdings ist es ja nur bei der Übergabe von einfachem Text oder einfachen Zahlen so einfach wie im Beispiel.

Hier noch einmal das Beispiel aus oben genanntem Thread:
fib.c:

Code: Alles auswählen

// Das C-Programm
int fib(int n) {
  if(n > 2)
    return fib(n - 1) + fib(n - 2);
  else
    return 1;
}


fibfast.py:

Code: Alles auswählen

#!/usr/bin/env python
import ctypes
f = ctypes.cdll.LoadLibrary("fib.so")
print f.fib(35)


startfib.sh:
[code=]#!/bin/bash
cc -c fib.c
cc -shared fib.o -o fib.so
LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:. python fibfast.py[/code]

Nehmen wir mal an, ich habe einen langen Unicode-Text mit vielen Wörtern. Und ich möchte aus diesem Text alle Wörter, ohne Punkte, Beistriche und Klammern, extrahieren und in eine Liste schreiben.

In Python könnte das z.B. so aussehen:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

UNICODE_TEXT = u"""Hallo Welt. Ich bin gekommen um
zu bleiben. Brennspiritus, Isopropanol und Aceton sind
Stoffe, die man öfter bei Elektronikern anfindet.
Wickie ist der Sohn von Halvar (= Chef der Wickinger)."""


def get_words(text):
    words = []
    for line in text.splitlines():
        for word in line.split():
            word = word.replace("=", "")
            word = word.strip("\n\r().,")
            words.append(word)
    return words


def main():
    words = get_words(UNICODE_TEXT)
    print repr(words)


if __name__ == "__main__":
    main()

# Ausgabe:
# [u'Hallo', u'Welt', u'Ich', u'bin', u'gekommen', u'um', u'zu', u'bleiben',
# u'Brennspiritus', u'Isopropanol', u'und', u'Aceton', u'sind', u'Stoffe',
# u'die', u'man', u'\xf6fter', u'bei', u'Elektronikern', u'anfindet', u'Wickie',
#u'ist', u'der', u'Sohn', u'von', u'Halvar', u'', u'Chef', u'der', u'Wickinger']


Und nehmen wir mal an, dass ich raus gefunden habe, dass die Funktion ``get_words()`` sehr lange braucht um die Wörter zu extrahieren und zurück zu geben. Nehmen wir auch mal an, dass sich die Funktion ``get_words()`` mit Python-Mitteln nicht mehr optimieren ließe. (reine Annahme -- bitte nicht darauf herum reiten)

Jetzt möchte ich diese Funktion in eine C- oder C++-Funktion auslagern. Am liebsten so, dass ich die Funktion über ctypes aufrufen kann, um von der eingesetzten Python-Version unabhängig zu bleiben. Die Schwierigkeiten sind: Es muss ein Unicode-Text an die C-Funktion übergeben werden (wie lange darf dieser sein?) und die C-Funktion sollte (wenn möglich) eine Python-Liste mit Unicode-Strings zurück liefern (Länge unbekannt). Wenn die Rückgabe einer Python-Liste nicht möglich ist, dann würde ich gerne wissen, welche Datenstruktur von der C-/C++-Funktion zurück gegeben werden muss, dass diese *ressourcenschonend* in Python in eine Python-Liste umgewandelt werden kann. Ist so etwas überhaupt möglich, ohne dass man in C gegen Python linkt?

Das ist etwas, was die wenigsten wissen und können. Deshalb bitte ich auch um funktionierende Beispiele in C/C++ und Python und um die entsprechenden Befehle um den Code zu kompilieren.

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 » Freitag 2. November 2007, 09:55

...ist Cython http://pypi.python.org/pypi/Cython/ die bessere Wahl für solche Optimierungen? Wie würde der Cython-Code dafür aussehen und wie müsste man diesen kompilieren? Muss dafür unter Windows ein C-Compiler installiert sein? Beispielcode, bitte. :oops:

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

Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Re: Optimieren von Programmen mit C/C++

Beitragvon Leonidas » Freitag 2. November 2007, 10:08

gerold hat geschrieben:Jetzt möchte ich diese Funktion in eine C- oder C++-Funktion auslagern. Am liebsten so, dass ich die Funktion über ctypes aufrufen kann, um von der eingesetzten Python-Version unabhängig zu bleiben. Die Schwierigkeiten sind: Es muss ein Unicode-Text an die C-Funktion übergeben werden (wie lange darf dieser sein?) und die C-Funktion sollte (wenn möglich) eine Python-Liste mit Unicode-Strings zurück liefern (Länge unbekannt). Wenn die Rückgabe einer Python-Liste nicht möglich ist, dann würde ich gerne wissen, welche Datenstruktur von der C-/C++-Funktion zurück gegeben werden muss, dass diese *ressourcenschonend* in Python in eine Python-Liste umgewandelt werden kann. Ist so etwas überhaupt möglich, ohne dass man in C gegen Python linkt?

Nein, du kannst aus einleuchtenden Gründen nicht die Py_*-Funktionalität nutzen ohne gegen die libpython zu linken. `ctypes` operiert nur auf simplen C-Typen. Du würdest an dieser Stelle wohl am besten ein Null-Terminiertes Array aus wchar_t verwenden.
Mal sehen ob ich so etwas zusammenbekomme. Allerdings ist mein C bisher noch nicht so gut, wie ich es mir wünschen würde :/
My god, it's full of CARs! | Leonidasvoice vs Modvoice
BlackJack

Beitragvon BlackJack » Freitag 2. November 2007, 13:30

Ich denke ein Haupthindernis hier ist Unicode. Das kann man nicht so einfach übergeben, man muss es irgendwie kodieren, weil es ja ein Implementierungdetail ist, wie das intern in Python gehandhabt wird.

Bei C ist dann das Problem, dass `wchar_t` im Grunde im Standard nicht weiter definiert ist als ebenfalls implementierungsabhängig. Das kann jeder C-Compiler handhaben wie er will. Und Unterstützung für Konvertierungen von Kodierungen zu `wchar_t` und zurück ist noch nicht so toll, dass man nicht doch bei manchen Compilern auf externe C-Bibliotheken für Unicode-Unterstützung zugreifen muss.

Die Aufgabenstellung ist so gestellt, dass man auf C-Seite mit UTF-8-kodierten Zeichenketten arbeiten kann. Simpelste Schnittstelle wäre wohl so etwas wie ``char** extract(char *s);``, also UTF-8 kodierte Zeichenkette übergeben und ein Array mit Pointern auf UTF-8 kodierte Worte zurück, wobei das Array mit einem NULL-Pointer beendet wird, damit man das Ende erkennen kann. Die Zeichenkette kann man "inplace" verändern, so dass die Zeiger in die ursprüngliche Zeichenkette zeigen, in die die Funktion an den entsprechenden Stellen Nullbytes einfügt. Das heisst man muss das Ganze auf der Python-Seite so verpacken, das `s` solange erhalten bleibt wie auch das Ergebnis noch da ist. Wenn man mit C arbeitet, muss man halt auch immer ein wenig die Speicherverwaltung im Auge behalten.

Da fällt man dann irgendwann über Deine Vorgabe, kein C-Könner sein zu müssen. Ganz ohne Verständnis für manuelle Speicherverwaltung und Zeiger und Zeiger auf Zeiger auf… geht's einfach nicht.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5554
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Telfs (Tirol)
Kontaktdaten:

Beitragvon gerold » Freitag 2. November 2007, 13:58

BlackJack hat geschrieben:Ich denke ein Haupthindernis hier ist Unicode.
[...]
Die Aufgabenstellung ist so gestellt, dass man auf C-Seite mit UTF-8-kodierten Zeichenketten arbeiten kann.
[...]
Da fällt man dann irgendwann über Deine Vorgabe, kein C-Könner sein zu müssen.

Hallo BlackJack!

Um das Ganze zu vereinfachen:

Die Python-Funktion get_words() wird mit UNICODE gefüttert und unten muss eine Python-Liste mit UNICODE-Texten raus. Die Original-Zeichenkette darf/soll nicht verändert werden. UTF-8 klingt gut für mich. Und ohne (kompliziert) mit Pointern auf Teile der Zeichenkette zu zeigen. -- Wie würde so ein Beispiel (Python und C) aussehen? Es muss ja nicht bis ins Letzte optimiert sein. Es geht mir jetzt erst mal um die Schnittstelle. Es geht mir darum, zu verstehen und eine Basis zu haben, auf die man gedanklich aufbauen kann.

Ohne Beispiel(e) bin ich aufgeschmissen. :roll:

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

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

Beitragvon BlackJack » Freitag 2. November 2007, 14:39

Vergiss Unicode und (Standard-)C, das willst Du nicht, egal in welcher Kodierung, ausser Du hast eine C-Bibliothek die Dir die Arbeit mit Unicode abnimmt. Zum Beispiel wird das 'ö' in Deinem Text als zwei Bytes kodiert und Du müsstest in C herausbekommen, ob diese zwei Bytes nun ein Buchstabe, eine Ziffer, ein Interpunktionszeichen, whitespace oder sonstwas ist.

Und Du *musst* mit Speicherverwaltung und Pointern umgehen können, `ctypes` ist eine Schnittstelle zu C die auf der Python-Seite operiert. Das heisst Du kannst weder Unicode nach aussen übergeben, noch eine Python-Liste von aussen bekommen. C kennt keine Unicode-Objekte und keine Python-Listen als Datentypen, diese Umwandlungen müssen auf Python-Seite passieren. Wenn Du das in C machen willst, musst Du gegen die `libpython` linken und kannst `ctypes` gleich ganz weglassen und das Tutorial durchlesen, wie man Python in C erweitert. Da muss man sich letztendlich aber auch mit Speicherverwaltung und Zeigern auskennen und mit der Python/C-API und wie Python intern Speicher verwaltet.

Das schöne an `cpython` ist ja gerade das man C-Bibliotheken verwenden kann, die von Python keine Ahnung haben müssen und die man auch von C aus ohne Python verwendet könnte.

Python ist einfach in C erweiterbar heisst nicht, dass man kein C können muss, sondern das, wenn man beide Sprachen kann, einfach eine Brücke zu schlagen ist.
Benutzeravatar
BlackVivi
User
Beiträge: 762
Registriert: Samstag 9. Dezember 2006, 14:29
Kontaktdaten:

Beitragvon BlackVivi » Freitag 2. November 2007, 14:47

Könntest du dem ganzen nicht mit Pyrex ein wenig auf die Sprünge helfen?
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5554
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Telfs (Tirol)
Kontaktdaten:

Beitragvon gerold » Freitag 2. November 2007, 15:27

Hallo!

Kann mir jemand bitte diese Pseudo-Funktion bitte erweitern und bitte die dazu gehörende C- oder C++-Funktion schreiben? Bitte!

Code: Alles auswählen

def get_words(text):
    import ctypes
    words_array = ctypes.mydll.get_words(text.encode("utf-8"))
    words = []
    for word in words_array:
        word.decode("utf-8")
    return words

Ich will doch nicht jedem erklären müssen, dass man Python zwar mit C- oder C++-Funktionen erweitern kann -- es sogar ein Modul (ctypes) dafür gibt, aber niemand konnte mir zeigen wie das funktioniert. Jeder wollte mich davon abhalten. Jeder wollte mir einreden, ich wisse nicht was ich will.

Alles was ich will, ist ein kleines Beispiel das aufzeigt wie es funktioniert. Es wird doch wohl in C oder C++ eine Erweiterung geben, mit der man UTF-8-Strings verarbeiten kann...

Ich will danach kein C-Spezialist sein. Mir genügte die Einführung, die ich vor ein paar Jahren gelesen habe. Ich will nur wissen wie es funktionieren könnte. Praktisch -- nicht nur theoretisch. Ein einziges funktionierendes Beispiel, das man ausprobieren kann, würde genügen. Ich will nur auf das Beispiel zurück greifen können um anderen zu beweisen, dass es möglich ist -- auch ohne Python einbinden zu müssen und die Bibliothek für jedes Python neu kompilieren zu müssen.

Ein Beispiel für Integer haben wir ja schon. Jetzt noch ein Beispiel mit Strings (mit Umlauten) und einer Rückgabe eines Arrays und schon ist mehr als die Hälfte aller Fälle abgedeckt.

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

Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Beitragvon CM » Freitag 2. November 2007, 15:38

Hoi Gerold,

eine andere Fragestellung (andere geforderte Datentypen) und es wäre womöglich gar nicht so schwer, aber Unicode ist - wie BJ schon sagte - nicht ohne weiteres in C umzusetzen. ( C++ ginge zwar etwas einfacher, aber ich müßte mich da ehrlich gesagt wieder einlesen - solche Probleme kenne ich nur von Übungsaufgaben und nicht aus dem richtigen Leben ;-) .)

In Pyrex, wie BlackVivi andeutet, wäre es auch zu machen. Das wäre womöglich ein Zwischenschritt: C ohne, daß man selber mit der Python-API rummachen muß, weil das Pyrex übernimmt. Allerdings ist mir jetzt nicht klar, wie man die aktuelle Anforderung in Pyrex übersetzt (ctypes.mydll.get_words() = ???).

Gruß,
Christian
BlackJack

Beitragvon BlackJack » Freitag 2. November 2007, 16:49

Selbst ohne Unicode, also die Aufgabe nur mit Text ist zwar in C machbar, aber ich habe nach ca. 10 Zeilen entnervt aufgegeben, weil es das eine irrsinnige Fummelarbeit in C ist, auf die ich einfach keinen Bock habe.
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Beitragvon rayo » Freitag 2. November 2007, 16:51

Hi Gerold

Eins Vorweg, ich bin kein C-Programmierer, ich kanns nur ein wenig aus der Schule, also nimm das Beispiel nicht als Referenz, ist nur ein Gebastel was ich einfach zum laufen gebracht habe.

Also hier das Beispiel, es achtet jedoch gar nicht auf das Unicodezeugs, es wird einfach ein Unicode-Pythonstring übergeben und eine Linkedlist von Worten zurückgegeben, so wie ich es gsehen habe bleiben die Werte erhalten.

Code: Alles auswählen

import ctypes

class word_list(ctypes.Structure):
    pass
word_list_p = ctypes.POINTER(word_list)
word_list._fields_ = [
        ('word', ctypes.c_wchar_p),
        ('next', word_list_p),
        ('prev', word_list_p),
    ]

f = ctypes.cdll.LoadLibrary("sdf.dll")
f.get_words.argtypes = [ctypes.c_wchar_p]
f.get_words.restype = word_list_p

python_str = u"abc \uFF00\u3321cdf efg aäb lala. te\nst (= ach wirklich)"

tmp = f.get_words(python_str)

while tmp.contents.next:
    tmp = tmp.contents.next
    print repr(tmp.contents.word)


C-Code

Gruss
BlackJack

Beitragvon BlackJack » Freitag 2. November 2007, 19:06

Die Felder der Struktur hast Du etwas gewöhnungsbedürftig definiert. Das würde man normalerweise anstelle des ``pass`` in der Klassendefinition machen und nicht hinterher.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5554
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Telfs (Tirol)
Kontaktdaten:

Beitragvon gerold » Freitag 2. November 2007, 19:54

Hallo rayo!

Vielen Dank! Jetzt habe ich einen Anhaltspunkt. Jetzt kann ich mir etwas darunter vorstellen. Ich werde morgen dein Beispiel durchprobieren und mich ein wenig damit spielen. Mal sehen, wie das Zusammenspiel zwischen C und Python mit ctypes läuft.

Aber ich muss auch zugeben, dass du in deinem Code Dinge verwendest, über die ich noch nicht bescheid weiß. ``extern`` und ``__declspec`` sind mir komplett unbekannt. Da werde ich mich noch ein wenig mit Lesestoff eindecken müssen. ;-)

Außerdem bin ich noch ein wenig irritiert, dass du im C-Code nicht besonders auf Unicode eingehst. Übernimmt das ctypes? Egal, das finde ich morgen schon raus.

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

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

Beitragvon BlackJack » Freitag 2. November 2007, 20:48

Diese ``extern`` und ``__declspec``-Geschichte ist nicht besonders portabel. Das ist Windows/MS C(++)-Zeuch. Beim GCC braucht man das nicht. Ausserdem ist das kein C-Code sondern C++. In C gibt's kein ``new``. Ein Speicherleck ist die Funktion auch, weil ich keine Möglichkeit sehe die Wörterliste wieder freizugeben.
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Beitragvon rayo » Freitag 2. November 2007, 21:11

Ich sag ja, nur lauffähig für mehr nicht zu gebrauchen. Das mit dem Speicherleck war mir auch bewusst, aber so auf die schnelle fiel mir nichts ein um das nicht mit einer linked list sondern mit "wchar_t* list[]" zu lösen, ohne vorher eine max-grösse anzugeben.

Das extern und co hab ich im Internet schnell nachgeschlagen wie ich mit Visual C++ halt ne dll mache und das hat er ausgespuckt. Hab noch nie eine dll/so geschrieben.

BlackJack hat geschrieben:Die Felder der Struktur hast Du etwas gewöhnungsbedürftig definiert. Das würde man normalerweise anstelle des ``pass`` in der Klassendefinition machen und nicht hinterher.


Das ist weil next und prev auf die Struktur selber verweisen (oder halt auf einen Pointer der Struktur) und das wäre direkt innerhalb der Klassendefinition noch nicht bekannt.

Also nochmals: Das Beispiel soll nur gerold ein wenig auf die Sprünge helfen, es ist kein guten Beispiel und auch kein fehlerfreies, wollte nur zeigen dass man einfach an einen wchar_t kommt von Python aus (ohne auf das encoding zu schauen).


Gruss

Wer ist online?

Mitglieder in diesem Forum: Bing [Bot]