GIL removal in Python 1.4

Alles, was nicht direkt mit Python-Problemen zu tun hat. Dies ist auch der perfekte Platz für Jobangebote.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Dave Beazley hat einen interessanten Artikel geschrieben, der einen 15 Jahre alten Path für Python 1.4 erklärt, mit dem man damals den Global Interpreter Lock (GIL) entfernen hätte können. Der Patch verlangsamt unglücklicherweise (wie auch alle anderen Versuche danach) Python, wenn es nur auf einem Prozessor läuft, sodass er letztlich nicht benutzt wurde.

Interessant finde ich die Erkenntnis, dass fast vollständig das Reference Counting (das gegen racing conditions bei mehreren Threads gesichert werden muss)für die Verlangsamung verantwortlich ist. Würde man dieses ausbauen, wäre viel gewonnen. Leider würden dann die meisten C-Extensions nicht mehr funktionieren. Sie müssten alle im Hinblick auf einen "richtigen" Garbage-Collection-Algorithmus neu geschrieben werden.

Das Problem ist aber tiefergehend. Eigentlich müssten alle Datenstrukturen, die Python intern nutzt, thread-sicher sein. Am einfachsten wäre dies gegeben, indem man persistente, unveränderliche Datenstrukturen nutzt, wie sie Clojure populär gemacht hat. Leider basiert Python aber in seiner Grundfeste auf veränderbaren dictionaries, eigentlich einem Implementationsdetail, welches aber durch Eigenschaften wie die __dict__-Attribute Teil der Sprachsemantik geworden ist.

Ich denke daher, dass man den GIL nur dann effektiv loswerden könnte, wenn man quasi eine neue Sprache schafft, die auf den ersten Blick zwar die Syntax mit Python teilt, aber ihr inneres Verhalten nicht mehr kompatibel zu Python ist. Das fängt damit an, dass man sich nicht mehr darauf verlassen darf, dass Objekte zeitig gelöscht und dabei z.B. offene Betriebssystem-Ressourcen wie Dateien geschlossen werden und muss damit enden, dass Exemplare von Klasen nicht mehr beliebige Attribute haben können. So würde ich fordern, dass man jedes Attribut in der Klasse deklarieren muss, was dann dazu führt, dass man keine dynamischen Dictionaries mehr braucht, um Speicherplatz für die Attributwerte zu schaffen. Da Klassen Exemplare von Metaklassen sind, gilt das selbe für Methoden. Um die Flexibilität zu erhalten, nachträglich noch Methoden (oder Attribute) zu ergänzen, würde ich vorschlagen, eine Syntax zu schaffen, die klar macht, dass dies eine aufwendige Operation ist. Self zeigt, wie man das technisch löst, darauf will ich hier gar nicht eingehen.

Man verliert dadurch zwar Flexibilität in der Meta-Programmierung, aber 80% aller Anwender werden das wahrscheinlich gar nicht merken. Man würde im Gegenzug effizientes Multithreading gewinnen. Denn eigentlich finde ich die Ideen von Clojure (oder auch Erlang) genau richtig, doch eine etwas einfachere Syntax (zu dem Preis im Vergleich bei Clojure, die Homoikonizität - oder wie das heißen mag - also die Selbstreflexivität - zu verlieren, den ich aber gerne zahlen würde).

Stefan
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Da selbst Python3 sich bis heute schwer tut, wäre ein solches Python, wie Du es vorschlägst, sicherlich mit noch weniger Akzeptanz gesegnet, oder?

Kurze Nachfrage zu den "persistenten, unveränderlichen Datenstrukturen": Meinst Du damit "nur" Attribute von Klassen? Wären denn dann Decoratoren noch möglich?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@Hyperion: Ein Dekorator ist doch eigentlich nur syntaktischer Zucker, der quasi eine vorgelagerte Funktion darstellt. Diese Funktion betreibt dann zwar häufig ein bißchen Introspektion hinsichtlich der Funktionsargumente und solche Spässe, aber speziell mit einem persistenten Wörterbuch (resp. "Attributspeicher") dürfte das doch nichts zu tun haben, da in dem Moment ja nicht automatisch etwas an irgendwelchen Attributen verändert wird. Oder meintest du etwas anderes?
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

@snafu: Eben doch. Das Attribut das die Methode (oder eben das Modulattribut und die Funktion) wird mit der dekorierten Methode ueberschrieben. Mit persistenten Datenstrukturen wird dann eine Kopie erzeugt und nur die veraendert. Das heisst man hat erstmal das Problem, dass alle das neue Dictionary (oder sonst was) benutzen.

AFAIK ist das aber kein Problem, weil immer neu gesucht wird, einzig JITs koennten hier ein Problem werden.

Mit persistenten Datenstrukturen kann man dann zwar Mehrkerner effektiv auslasten, aber benoetigt dafuer wahrscheinlich mehr Speicher,. An sich kein Problem, aber damit macht man sich den Cache kaputt und muss da dann erstmal Leistungseinbussen hinnehmen. Wobei es da durchaus spannende Datenstrukturen gibt, allerdings weiss ich nicht, in wie weit die das Cache-Problem loesen. Ich denke das wird spannend :)
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

cofi hat geschrieben:@snafu: Eben doch. Das Attribut das die Methode (oder eben das Modulattribut und die Funktion) wird mit der dekorierten Methode ueberschrieben. Mit persistenten Datenstrukturen wird dann eine Kopie erzeugt und nur die veraendert. Das heisst man hat erstmal das Problem, dass alle das neue Dictionary (oder sonst was) benutzen.
Allerdings ist das ja schon zur Compilezeit bekannt und muss daher zur Laufzeit nicht mehr beachtet werden. Ich dachte, dies sei der Casus Knaxus.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

CPython hat quasi keine Chance den GIL los zu werden. PyPy hat dort wesentlich bessere Chancen, da man da nur noch die Daten Strukturen die vom Interpreter genutzt werden absichern muss, einen richtigen GC haben die schon.

Das sofortige schliessen von Resourcen ist ein Implementationsdetail von CPython auf dass sich guter Python Code nicht verlässt, tatsächlich kann man dass auch nicht wenn eine Anwendung auf PyPy, Jython oder IronPython laufen soll.

Die Existenz von __dict__ setzt auch nicht zwangsläufig voraus dass der Interpreter ebenfalls veränderliche Datenstrukturen nutzt, PyPy erzeugt die nur auf Bedarf und verwendet normalerweise Maps, welche wesentlich effizienter sind.

Ich denke nicht dass die Lösung dieses Problems zwangsläufig eine neue Sprache erfordert, die PyPy Leute überlegen momentan das Problem mit Software Transactional Memory zu lösen. Es ist zwar fraglich ob es Erfolg haben wird und selbst wenn bleiben die Geschwindigkeitsprobleme aber die Lösung scheint wesentlich realistischer als eine neue Sprache zu entwickeln die Python ersetzt.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Idealerweise hätte ein Python-Interpreter keinen globalen Zustand bzw. würden sich verschiedene Threads keine Objekte teilen. Beides ist in Python nicht gegeben und wahrscheinlich unmöglich, wenn man die definierte Semantik erhalten will. Die Programmiersprache Erlang schafft es hingegen. Alle Datentypen sind standardmäßig unveränderlich. Threads (bzw. Prozesse, wie sie dort heißen) können sich so gefahrlos Objekte teilen. Clojure macht globale veränderbare Variablen automatisch thread-lokal, wenn man nicht explizit etwas anderes haben will. Und auch hier sind alle Datenstrukturen standardmäßig unveränderlich. Zumindest kann man im Gegensatz zu Erlang aber die Werte lokaler Variablen ändern.

Ob eine Python-artige Sprache, die grundsätzlich multithreaded wäre, eine Chance hat, weiß ich nicht. Aber ich würde einmal behaupten, dass ein Problem von Python 3 ist, dass es nicht anders genug zu Python 2.x ist und letzteres einfach in der Regel gut genug ist. Von mehreren Prozessorkernen profitieren zu können wäre hingegen eine völlig neue Eigenschaft und daher gelten hier andere Regeln.

Python fordert, dass Module, Klassen und Exemplare ein veränderliches dictionary haben und um auf der sicheren Seite zu sein, müsste man sicherstellen, dass verschiedene Threads dieses ändern dürfen. Weil man prinzipiell jedes dict-Exemplar dem Attribut `__dict__` zuweisen kann - insbesondere auch eigene Unterklassen mit beliebigen Implementierungen - muss jedes dieses Objekte threadsafe sein und das kostet Performance.

Was ich meinte ist, dass man das `__dict__`-Attribut aus der Sprachbeschreibung tilgt und stattdessen für Exemplare definieren muss, welche Attribute sie haben sollen. Für Klassen und Module ergibt sich dies aus dem Code. Bislang werden Klassen und Module in Python ja nicht definiert, sondern bei der Ausführung konstruiert. Würde man (wie z.B. bei Java und C++) von einer Deklaration ausgehen, wüsste der Compiler, welche Attribute und Methoden eine Klasse hat und welche globalen Objekte ein Modul und könnte immer passende Datenstrukturen zur Verfügung stellen, die sich nach dem Laden eines Moduls nie mehr ändern.

Oder man benutzt sog. persistente Datenstrukturen, die die Eigenschaft haben, dass Änderungen jeweils neue Exemplare von einem Objekt erzeugen. Will man wie bei `a['b'] = 2` ein Dictionary `a` ändern, muss das als `a1 = setitem(a, 'b', 2)` ausgedrückt werden, sprich, das ursprüngliche Objekt bleibt wie es ist und es entsteht ein neues Dictionary `a1`, bei dem unter dem Schlüssel `'b'` die zwei abgelegt ist. Damit man nicht immer naiv kopieren muss, sind Dictionaries (aber auch Vektoren aka Listen) trickreicher aufgebaut. Sie auch STM.

Dekoratoren haben eigentlich damit wenig zu tun, denn sie sind ja nur syntaktischer Zucker.

Wie man den Methoden-Lookup schnell hinbekommt, ist noch einmal ein anderes Thema und IMHO eigentlich ein gelöstes Problem, wenn man sog. polymorphic inline caches benutzt. Kann sich die Menge der Methoden zudem nie ändern, ist deren Implementierung deutlich einfacher. Wurde alles so zwischen 1990 und 1995 im Self-Projekt erforscht.

Und ja, das ganze braucht mehr Speicher, aber das fände ich total egal. Zudem würde ich viel Speicher einsparen können, wenn ich nicht für jedes Exemplar einer Klasse ein dict bräuchte, sondern ähnlich wie (Self und wie) V8 es macht und wie es bei Smalltalk schon immer der Fall war: Die Klassen geben für jedes Attribut den Index in eine Liste vor.

Syntaktisch könnte das so aussehen:

Code: Alles auswählen

    module foo: # könnte man auch implizit durch die Datei machen
        def a = 0   # eine module-globale Variable (automatisch thread-lokal)
        
        def b():    # eine module-globale Funktion
            return a
        
        class C:
            slots c, d, e   # mögliche Attribute
            
            def a = 0       # ein Klassen-Attribut
            
            def b(self):    # eine Methode
                return self.a
Theoretisch könnte man auch durch statische Analyse alle Attribute erkennen, wenn die Absprache funktioniert, dass man irgendwo und irgendwie alle Attribute zuweist, aber ich denke, explizit ist besser als implizit.

Alle Funktionen, die Attribute von Modulen, Klassen und Exemplaren zu finden, zu untersuchen oder zu ändern, würde ich (wie bei Self oder Newspeak) in "Mirror"-Objekte ziehen und so die Meta-Ebene klar abtrennen. Das war schon Mitte der 80er ein Hinweis von Kent Beck, wenn ich mich recht an einen Artikel aus Guide to Better Smalltalk erinnere.

Und ansonsten: Ja, PyPy hat bessere Chancen als CPython, den GIL los zu werden, gleichzeitig denke ich aber, dass dieses Projekt dermaßen von der Kompatibilität zu Python gebremst wird, dass es extrem schwer und aufwendig sein wird, das zu erreichen.

Ob eine neue Sprache unbedingt Python ersetzen muss, weiß ich gar nicht. Ergänzen würde ja schon mal reichen.

An Python finde ich gut, dass es sehr einsteigerfreundlich ist. Die Syntax ist klar, die Semantik einfach. Es gibt andererseits genug Syntax, um sich nicht zu verlieren. Dennoch ist die Sprache vergleichsweise ausdrucksstark. Mir persönlich sind die ganzen C-Extensions relativ egal, auch wenn ich sehe, dass viele Python nutzen, weil es numpy und ähnliches Zeug gibt. Das "batteries included"-Argument zählt heutzutage IMHO kaum noch, denn inzwischen kommt jede Sprache mit einer mindestens genauso umfangreichen Bibliothek, häufig, weil sie auf einer anderen aufbaut.

Tatsächlich würde ich für das erwähnte Python-artige Ding auf Java 7 setzen - noch nicht einmal wegen der Klassenbibliothek von Java, denn von der JVM als hervorragende Ausführungsumgebung, die JIT und GC gelöst hat. Dann würde ich hoffen, dass irgendwer, der schlauer als ich ist, das auf LLVM o.ä. umbaut. Ich weiß, dass einige Clojure-Leute davon träumen, den Java-Unterbau loszuwerden.

Und ich glaube, Clojure wäre ein guter Unterbau für so eine python-artige Sprache.

Stefan
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@sma: Also ich finde ja, der Komfort an Python ist, dass man so etwas nerviges, wie das Definieren von Namen, bevor man ihnen etwas zuweisen darf, gerade nicht hat. Du hast schon recht: Wenn das tatsächlich jemand so umsetzen will, dann sollte er sozusagen die Sprachbeschreibung forken. Ich glaube, dies in ein Standard-Python einzubauen, wäre ein weitaus größerer Einschnitt als das, was mit Python 3 gemacht wurde. Das wäre also kein mögliches Python 4 oder ähnliches, sondern tatsächlich eine neue Sprache für mich.

In der Hinsicht ist es aber IMHO auch äußerst fraglich, ob die Welt wirklich so etwas braucht. Wenn man tatsächlich etwas bequemes in der Art von Python haben will, was idealerweise auf Java aufbaut und auch ähnlich zügig bei der Ausführung ist, dann möchte man sich vielleicht mal Groovy mit der Erweiterung Groovy++ ansehen. Interessant daran finde ich, dass man, wie hier beschrieben, explizit bestimmen kann, ob der Quelltext statisch oder dynamisch typisiert kompiliert werden soll. Als wie praktikabel sich das Ganze tatsächlich erweist, kann ich allerdings schlecht abschätzen. Am ehesten erinnert dieses Vorgehen wohl an Cython. Ich persönlich würde aber wohl Groovy eher nicht einsetzen wollen, weil mir die syntaktischen Möglichkeiten schon wieder too much sind.

Eine Idee für Python hätte ich dann allerdings doch noch. Wie wäre es, wenn man, ähnlich wie man das für new-style Klassen mittels `object` gemacht hat, nochmal einen neuen Grundtyp für Klassen einführt, welcher zumindest keine nachträgliche Attributzuweisung mehr erlaubt und damit auch intern anders vorgehen kann? Ich könnte mir durchaus vorstellen, dass solche Zuweisungen dann nur noch innerhalb von `__init__()` (also für das Exemplar) und auf der übergeordneten Ebene (eben als Klassenattribut) möglich sind. Schon jetzt erweist es sich ja meist als guter Programmierstil, wenn man alle später in der Klasse benötigten Attribute gleich zu Beginn "definiert", zwecks Übersichtlichkeit (analog zu Import-Statements, welche möglichst *vor* jeglichem Programmcode stehen). Der einzige wirklich ins Gewicht fallende Nachteil wäre dann natürlich, dass ein späteres Monkey-Patching von solchen Klassen nicht mehr möglich wäre.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

snafu, noch eine zusätzliche Variante finde ich ist keine gute Option, denn sie macht eine eigentlich einfache Sprache unnötig kompliziert. IMHO sollte Python noch einfacher werden, nicht noch komplizierter. Schon jetzt kann man ja mittels __slots__ eine effizientere Repräsentation von Attributen wählen.

Ein Zwang, alle Attribute zu deklarieren finde ich eigentlich gut, denn das ist eine Form der Dokumentation. Ansonsten muss man die komplette Implementierung UND alle Stellen im restlichen Code, wo die Klasse benutzt wird, prüfen, um zu erkennen, welche Attribute die Klasse eigentlich hat. Mir fehlt, wenn ich etwas beschreiben will, immer ein formaler Weg, wie ich die Attribute einer Klasse beschreiben kann. Ich würde auch gleich noch eine Typannotationen zulassen:

Code: Alles auswählen

class Planet:
    slot name: str
    slot owner: Player
Ehrlich gesagt, aber dann weiche ich schon recht stark von Python ab, würde ich vielleicht komplett auf Attribute verzichten und dem Weg folgen, dass Objekte einzig Methoden haben. Slots, die Werte aufnehmen können, sind dann ein Implementationsdetail und nach außen nur durch Accessor-Methoden sichtbar.

Der Ausdruck "self.a = self.a + 1" ruft damit zwei Methoden auf, nennen wir sie "a" und "set_a", d.h. dort steht eigentlich self.set_a(self.a() + 1). Damit kann ich Methodenaufrufe aber nicht mehr an den Klammern erkennen bzw. anders ausgedrückt, ein Ausdruck "self.m" ist nicht mehr das Methodenobjekt, sondern ruft diese Methode sofort auf. Ich brauche für diesen Fall eine spezielle Syntax, z.B. "&self.m" oder self.&m" oder aber Meta-Programmierung, z.B. getattr(self, "m"). Gleichzeitig kann ich dann aber auch nicht mehr mit "ding()" ein Ding aufrufen. Auch das ist auf einmal eine Meta-Programmierung, z.B. call(ding, ...). Selbstverständlich könnte ich mich dazu entscheiden, dass jedes Objekt für "get" oder "call" Methoden hat, vielleicht schreibe ich ja auch zwei "_" davor und dahinter, um diese Meta-Methoden von normalen zu unterscheiden :) Damit bin ich wieder am Anfang, habe aber das Konzept von Attributen nicht mehr und brauche auch kein "dict", um sie zu speichern. Stattdessen muss ich einen Mechanismus haben, wie ich das Paar "a" und "set_a" erzeugen kann, z.B. über eine Deklaration, wie oben zu sehen.

Intern zählt der Compiler dann die Slots und weist jedem einen Index zu, der benutzt wird, um in einem Array von Werten, das jedes Exemplar einer Klasse dann hat, den passenden Wert zu finden oder zu setzen. Alternativ könnten Slots automatisch eine verkettete Liste von Werten aufbauen. Und wenn "self.a = ..." nur syntaktischer Zucker für "self.set_a(...)" ist, kann man sich auch selbst für Slots beliebige Implementierungen stricken, braucht also hier keine speziellen Meta-Methoden mehr.

Stefan
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Interessantes Thema. Auch wenn ich nicht viel zu beisteuern kann.

Ich denke aber auch, das Python 3 zu wenig neues im Vergleich mit v2.x bietet. Gerade weil Änderungen an den C-Extensions erforderlich sind, wäre es gut gewesen, wenn v3 deutlichere Vorteile hätte.

Anscheinend werden noch eine ganze Zeit lang CPUs überwiegend über die Anzahl der Kerne skalieren. Von daher ist die Unterstützung von Mehrprozessorsystemen immer wichtiger. Aber gut, die Programmierung ist halt nicht so ganz trivial. Von daher wäre es wichtig, wenn man Nebenläufigkeit so einfach programmieren könnte, wie das Wort suggeriert ;)

In der aktuellen c't ist eine Einführung zu Go drin. Schon der erste Satz von Wikipedia hört sich interessant an:
Go ist eine kompilierbare, nebenläufige Programmiersprache mit automatischer Speicherbereinigung.
Die ersten Abschnitte aus dem c't Artikel machen einem die Sprache auch sehr schmackhaft. Doch nach dem lesen des gesamten Artikels bleibt bei mir davon nicht mehr all zu viel übrig. Aber vielleicht ist das bei Leuten aus der C, C++, Java Ecke anders ;)


Wie könnte die Zukunft von Python wohl aussehen? PyPy sieht schon länger recht viel versprechend aus. PyPy ist schon seit einiger Zeit schneller als CPython. Der aktuelle PyPy trunk soll laut http://speed.pypy.org/ 4,3x schneller sein.
Aber das scheint nicht zu reichen. Es krankt offensichtlich an der Unterstützung externe Module, obwohl mittlerweile doch so einiges geht. (Passend dazu: http://2011.djangocon.eu/media/slides/pypy-talk.pdf und http://www.python-forum.de/viewtopic.php?f=1&t=24225 )

EDIT:
Und auch wenn PyPy 5x oder 10x schneller ist... Ohne Nebenläufigkeiten fehlt auch die Unterstützung von Mehrprozessorsystemen, oder nicht?

Auch interessant: http://ep2011.europython.eu/media/confe ... uction.pdf

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

@sma / Stefan: Verständnisfrage eines Nicht-Informatikers: Wenn eine Klasse Attribute haben darf, im Kontrast zu einer Klasse, die keine aufweisen darf, sondern nur Metoden, ist diese "attributhaltige" Klasse nicht (zumindest prinzipiell) effizienter zu implementieren bei einer dynamischen Sprache, da +/- direkt auf Speicher zugeriffen werden kann, im Gegensatz zu Funktionsaufrufen wie "self.a()"? (Die gegenwärtige CPythonimplementierung gibt das wohl nicht her, aber ich versuche nur Dein Argument zu verstehen - nicht es zu kritisieren.)

Ein guter JIT würde natürliche diesen theoretischen Overhead durch Methodenaufrüfe wieder aus dem Weg schaffen, ähnlich der Optimierung von inline-Funktionen bei C++.

Gruß,
Christian

edit: "attributfreie" durch "attributhaltige" ersetzt - jetzt verstehe ich auch deets-Anmerkung. Meine Fragestellung hatte sich verholpert ... Edit eingebracht nach meinem zweiten Kommentar in diesem Thread.
Zuletzt geändert von CM am Montag 15. August 2011, 08:57, insgesamt 1-mal geändert.
deets

@CM

Ich verstehe dein Argument nicht - kann es sein, dass du statisch schreiben wolltest, statt dynamisch? So oder so ist deine Vermutung aber eh falsch, das eine hat mit dem anderen nichts zu tun. Wenn eine Klasse keine Attribute haben duerfte, musst du immer noch die Frage beantworten, wie Methodenzugriffe funktionieren. Von der Semantik haengt die Optimierbarkeit ab - siehe C++ und normale vs. virtuelle Methoden.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Was ist denn eigentlich gegen das multiprocessing-Modul einzuwenden hinsichtlich der Programmierung gegen mehrkernige Systeme? Ist das Interface zu kompliziert, um es wirklich benutzen zu können? Ist es der Umstand, dass Prozesse statt Threads verwendet werden? Funktioniert es vom Effekt her nicht wie gewünscht? Ich habe keine Ahnung, da ich es bisher nicht verwendet habe. Wäre schön, wenn mich jemand aufklären könnte. :)
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

deets, ich habe kein Argument, sondern eine Frage - und das auch auf die Gefahr hin, dass es eine dumme Frage ist. Und in meinen Augen hat statisch vs. dynamisch nichts mit meiner Frage zu tun, oder liege ich falsch? Wenn ja, warum?

edit: Ich merkte gerade, daß ich meine Frage mißverständlich gestellt hatte. Sie wurde editiert.
deets

@CM

Also so richtig klar ist sie immer noch nicht formuliert ;) Aber ich glaube ich habe jetzt verstanden, was du meinst. Im Grunde geht es dir doch darum, ob Attributzugriffe effizienter sind als Funktionsaufrufe. Und die Antwort ist: ja. Aber das ist irrelevant, es wird niemals eine Objektimplementierung geben, die *keine* Attributzugriffe kennt - denn irgendwo muessen die Daten ja nunmal sein. Man kann natuerlich hingehen & kuenstlich durch Kapselung dafuer sorgen, dass die effizienten Zugriffe nur einer Klasse selbst zur Verfuegung stehen. Oder es per Konvention machen, wie in Java.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

@deets: Merci. Also war es eine *dumme* Frage. Und die Antwort lautet: Ich habe sma's Einwand überhaupt nicht verstanden :oops:
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Bei Go gefällt mir das Konzept der Nebenläufigkeit mit "channels" nicht. Das mag aber persönlicher Geschmack sein. Ich finde Actors als Konzept besser. Nett gemacht (für eine statisch getypte Programmiersprache) sind bei Go die impliziten Interfaces.

Prozessoren werden nicht mehr schneller. Es werden nur mehr pro Chip. Daher wird in Zukunft ein Rechner dutzende wenn nicht hunderte CPU-Kerne haben. AFAIK ist das technisch kaum kein Problem. Problem ist die Software, denn traditionelle Programmiersprachen (und Architekturmodelle der Hardware) sorgen dafür, dass sich die Kerne gegenseitig ausbremsen.

Das Multiprocessing-Modul ist IMHO viel zu schwergewichtig. Nebenläufigkeit eines Programms heißt nicht, dass ich da mal einen Prozess abspalten will, sondern dass ich möglichst jede Form von Parallelität in meinem Programm nutzen kann. So sollte z.B. jede for-Schleife standardmäßig parallel und nicht sequentiell bearbeitet werden. Fortress hat diesen Ansatz.

Traditionell wird Python so programmiert, dass das bei for-Schleifen nicht geht, da sie meist einen Seiteneffekt haben, aber abstraktionen wie map() oder filter() und die passende Syntax dazu mit list comprehensions machen das schon eher erreichbar.

Wenn ich jedenfalls in Zukunft einen Rechner mit 16 CPU-Kernen habe, dann muss ich in meinen Programmen auf genug zu tun haben, das 16-fach parallel ausgeführt werden kann. Das ist eine ganz andere Ebene als Multiprocessing-Modul es bietet.

Eine Sprache wie Erlang, wo es normal ist, dass man 100.000 Prozesse hat, ist da besser gerüstet. Erlang ist nur leider von der Syntax archaisch und auch ausschließlich für die Server-Programmierung geeignet.

Fortress, so das Ziel von Guy Steele, soll irgendwann einmal Fortran ablösen und effizienter rechnen können, weil es einfacher ist, mathematische Formeln in Fortress auszudrücken und diese zu parallelisieren.

Und in Python quälen wir uns immer noch mit einem Programmiermodell, dass davon ausgeht, dass der Computer einen Befehl brav nach dem anderen ausführt. Das ist (mir) nicht genug.

Was die Attributzugriffe angeht: Das kann nicht so einfach sagen.

Erst einmal muss definiert sein, was ein Attribut ist. Der Begriff sollte keine Implementierung implizieren. Wenn du aber annimmst, dass letztlich ein Attribut dadurch implementiert wird, das ein Objekt durch ein einen Zeiger auf ein Stück Speicher repräsentiert wird und jedes Attribut eine Zelle in diesem Speicher ist und ich das also durch Zeiger plus Offset ansprechen kann, dann ist ein solcher Zugriff effizienter, als wenn ich auf ähnliche Weise erst einmal ein Methodenobjekt laden muss, um es dann auszuführen, auf das es dann sich den Wert irgendwie besorgt und auf den Stack oder in ein Register schreibt und dann zurückkehrt - schon weil Aufrufe von Unterprogrammen auf den allgegenwärtigen Intel-Prozessoren (SPARC war das AFAIK besser mit seinen Registerfenstern) vergeichsweise teuer ist.

Bei Python sind Attribute aber standardmäßig durch ein Dictionary implementiert und ein Zugriff auf ein Dictionary ist im Vergleich zu dem Zeiger auf ein Stück Speicher um Größenordnungen aufwendiger. Er ist sogar aufwendiger als ein Methodenaufruf, wenn diese (z.B. von einer VM wie der Java-VM) speziell optimiert werden.

Verstecke ich also den Zugriff per Zeiger und Offset hinter einer Methode (damit ich den Offset später noch ändern kann, ohne jede Programmstelle, wo ich zugreife, anpassen zu müssen), kann das immer noch effizienter sein, als der Dictionary-Zugriff.

Tatsächlich könnte ich mir vorstellen, dass CPython intern nicht immer Dictionaries benutzt, sondern falls man nur 1-5 Attribute hat, z.B. eine verkettete Liste oder ein Array, in dem linear gesucht wird. So etwas geht in der Regel schneller, wobei das Array wieder effizienter ist (weil man den Cache einer CPU nicht so quält) wie eine verkettete Liste, die früher mal die beste Lösung gewesen wäre, weil das einen Garbage-Collection-Algorithmus nicht so stark stresst, weil man alle Knoten einer Liste dank gleicher Größe effizienter verwalten kann.

Als ich da über Attribute dozierte, dachte ich in erste Linie an eine Implementierung auf der Basis der JVM und da hatte ich gerade gelesen, dass in Java 7 dank dem Offenlegen der "polymorphic inline caches" dank MethodHandles es in JRuby effizienter ist, für Konstanten Zugriffsmethoden zu generieren, die dann von der JVM effizient kompiliert, gecacht, ge-inlined und so weiter werden, anstatt gemäß Ruby recht komplizierter Regeln danach selbst zu suchen.

Wenn ich also eine effizienten Mechanismus für (dynamische) Methoden habe, dann sollte ich alles - eben auch Attribute - als Methoden implementieren. Oder vielleicht auch nicht, denn als ich gerade versucht habe, den Vorteil dieses Ansatzes beim nebenläufigen Ändern der Klassenstruktur darzulegen, stolperte ich genau über diesen Punkt ;)

Stefan
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Super Beschreibung. Danke.
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

snafu: Neue Prozesse erstellen ist unter Linux kein Problem, aber unter Windows, weil es wohl sehr viel "teurer" ist als einfach ein neuer Thread.
deets

sma hat geschrieben: Verstecke ich also den Zugriff per Zeiger und Offset hinter einer Methode (damit ich den Offset später noch ändern kann, ohne jede Programmstelle, wo ich zugreife, anpassen zu müssen), kann das immer noch effizienter sein, als der Dictionary-Zugriff.
Nunja, theoretisch ist das natuerlich moeglich. Ich kann ja auch beliebig waitstates einfuehren vor irgendwelchen Operation, um sie zu verlangsamen. Aber praktisch wird es wohl kaum eine Sprache geben, die aus Effizienz- & Flexibilitaetsgruenden den Methoden-Dispatch via einer vtable erledigt, aber dann Attribute ueber Namen und Dictionaries aufloest. Wenn ist es eher umgekehrt, siehe Objective-C.
Antworten