20% Leistungseinbußen für ein schönes Softwaredesign


17

Ich schreibe eine kleine Bibliothek für spärliche Matrixberechnungen, um mir beizubringen, die objektorientierte Programmierung bestmöglich zu nutzen. Ich habe wirklich hart daran gearbeitet, ein schönes Objektmodell zu haben, bei dem die Teile (dünne Matrizen und die Graphen, die ihre Konnektivitätsstruktur beschreiben) sehr locker gekoppelt sind. Meiner Meinung nach ist der Code viel erweiterbarer und für ihn wartbarer.

Es ist jedoch auch etwas langsamer als wenn ich einen stumpfen Ansatz gewählt hätte. Um die Nachteile dieses Objektmodells zu testen, habe ich einen neuen Matrixtyp mit geringer Dichte geschrieben, bei dem die Kapselung des zugrunde liegenden Diagramms aufgehoben wurde, um festzustellen, wie viel schneller das ausgeführt werden würde.

Anfangs sah es ziemlich trostlos aus; Der Code, auf den ich einst stolz war, lief 60% langsamer als eine Version ohne elegantes Softwaredesign. Aber ich konnte ein paar Optimierungen auf niedriger Ebene vornehmen - eine Funktion inlinieren und eine Schleife ein wenig ändern -, ohne die API überhaupt zu ändern. Mit diesen Änderungen ist es nur noch 20% langsamer als die Konkurrenz.

Was mich zu meiner Frage bringt: Wie viel Performanceverlust sollte ich in Kauf nehmen, wenn es bedeutet, dass ich ein schönes Objektmodell habe?


Welche spärliche Matrixoperation haben Sie gemessen?
Bill Barth

Matrix-Vektor multiplizieren. Die Matrizen reichten in der Größe von . Ich habe sie zu Laplace-Graphen für Erdos-Renyi-Zufallsgraphen mit einem Durchschnittsgrad von d = log 2 n gemacht . Außerdem verschlechtern sich die 20% auf einigen Maschinen, so dass ich jetzt eher geneigt bin, das Ganze rauszuwerfen. Tiefer Seufzern=1024,...,16384d=Log2n
Daniel Shapero

3
Welche Programmiersprache verwenden Sie? In der Regel können Sie mit etwas wie C ++ mit eleganten Designs zu geringen (oder nicht vorhandenen) Kosten durchkommen. In anderen Sprachen ohne Metaprogrammierung (Java, Fortran usw.) erscheinen 20% der Kosten angemessen.
LKlevin

Können Sie uns Ihren Code zeigen? Welche Sprache hast du benutzt? Welche Compiler- und Compiler-Flags? Hast du genau herausgefunden, woher der Performance-Hit kommt? Wie haben Sie sichergestellt, dass Sie den richtigen Grund gefunden haben? Welchen Profiler haben Sie verwendet und wie haben Sie ihn verwendet? Sind Sie sicher, dass das schöne Objektmodell nicht ineffizient implementiert ist? 20% sind klein genug, um eine Menge Daten zu sammeln und eine detaillierte Analyse durchzuführen, bevor Sie feststellen, dass dies definitiv auf das Design und nicht auf eine minderwertige Implementierung oder andere Codierungsprobleme zurückzuführen ist.
Kirill

Kurze Randbemerkung: Jeder scheint öffentlich gutes Design über reine Leistung zu loben (natürlich mit sehr triftigen Gründen). Aber warum ist so viel Code aus der realen Welt wirklich, wirklich unerreichbar? Fühlen sich alle Code-Slobs schuldig und schweigen sie öffentlich?
AlexE

Antworten:


9

Nur sehr wenige wissenschaftliche Softwareentwickler verstehen gute Designprinzipien, daher entschuldige ich mich, wenn diese Antwort etwas langwierig ist. Aus Sicht der Softwareentwicklung ist es das Ziel des wissenschaftlichen Softwareentwicklers, eine Lösung zu entwerfen, die eine Reihe von Einschränkungen erfüllt, die häufig in Konflikt stehen .

Im Folgenden sind einige typische Beispiele für diese Einschränkungen aufgeführt, die möglicherweise auf den Entwurf Ihrer Bibliothek mit spärlicher Matrix angewendet werden:

  • Fertig in einem Monat
  • Läuft korrekt auf Ihrem Laptop und mehreren Workstations
  • Läuft effizient

Die Wissenschaftler konzentrieren sich zunehmend auf einige andere allgemeine Anforderungen aus dem Software-Engineering:

  • Dokumentation (Benutzerhandbuch, Tutorial, Codekommentar)
  • Wartbarkeit (Versionskontrolle, Testen, modularer Aufbau)
  • Wiederverwendbarkeit (modularer Aufbau, "Flexibilität")

Möglicherweise benötigen Sie mehr oder weniger einer dieser Anforderungen. Wenn Sie versuchen, einen Gordon-Bell-Preis für Leistung zu gewinnen, sind auch Bruchteile eines Prozents relevant, und nur wenige der Juroren bewerten die Qualität Ihres Codes (solange Sie sie davon überzeugen können, ist es richtig). Wenn Sie versuchen, die Ausführung dieses Codes auf einer gemeinsam genutzten Ressource wie einem Cluster oder einem Supercomputer zu rechtfertigen, müssen Sie häufig Ansprüche bezüglich der Leistung Ihres Codes verteidigen, diese sind jedoch selten sehr streng. Wenn Sie versuchen, eine Veröffentlichung in einem Journal zu veröffentlichen, in der die Leistungssteigerungen Ihres Ansatzes beschrieben werden, müssen Sie zu Recht schneller als Ihre Konkurrenten sein, und 20% Leistung sind ein Kompromiss, den ich gerne für eine bessere Wartbarkeit und Wiederverwendbarkeit eingehen würde.

Wenn Sie auf Ihre Frage zurückkommen, sollte "gutes Design", das genügend Entwicklungszeit hat, niemals die Leistung beeinträchtigen. Wenn das Ziel darin besteht, Code so schnell wie möglich auszuführen, sollte der Code unter Berücksichtigung dieser Einschränkungen entworfen werden. Sie können Techniken wie Codegenerierung und Inline-Assemblierung verwenden oder hoch optimierte Bibliotheken verwenden, um Ihr Problem zu lösen.

Aber was ist, wenn Sie nicht genug Entwicklungszeit haben? Was ist gut genug? Nun, es kommt darauf an, und niemand wird in der Lage sein, Ihnen eine gute Antwort auf diese Frage zu geben, ohne mehr Kontext.

FWIW: Wenn Sie wirklich daran interessiert sind, Hochleistungs-Kernel mit spärlicher Matrix zu schreiben, sollten Sie sich mit einer optimierten PETSc-Installation vergleichen und mit ihrem Team zusammenarbeiten, wenn Sie sie schlagen. Sie würden gerne optimierte Kernel in die Bibliothek integrieren.


Ich bin neugierig auf Codegeneratoren - ich denke, sie könnten mir nützlich sein, aber ich mache mir Sorgen, dass sie schwer zu warten sind. Ich weiß, dass Java-Programmierer sie häufig verwenden, aber sie sind oft darauf zugeschnitten, Code für bestimmte Anwendungen zu generieren. Kennen Sie wissenschaftliche Codes, die sie verwenden?
Daniel Shapero

ATLAS, FFTW, Spiral, OSKI, Ignition, stencil_codegen, um nur einige zu nennen. Es wird nicht öffentlich beworben, aber ich wäre nicht überrascht, wenn mehrere der wichtigen Kernel in MKL und ESSL auf diese Weise generiert würden. Das Schreiben von wartbarem Kernel-Generierungscode wäre eine interessante Folgefrage. Ich habe Erfahrung damit, aber ich würde mich nicht als Autorität betrachten.
Aron Ahmadia

12

Es ist eine Frage, wofür du deine Zeit verbringst. Für die meisten von uns verbringen wir 3/4 der Zeit mit Programmieren und 1/4 der Zeit damit, auf Ergebnisse zu warten. (Ihre Zahlen können variieren, aber ich denke, die Zahl ist nicht völlig unbegründet.) Wenn Sie also ein Design haben, mit dem Sie doppelt so schnell programmieren können (3/4 einer Zeiteinheit statt 1,5 Zeiteinheiten), dann Sie can kann einen Leistungseinbruch von 300% verzeichnen (von 1/4 auf 1 Zeiteinheit) und Sie haben immer noch einen Vorsprung in Bezug auf die für die Lösung des Problems aufgewendete Echtzeit.

Auf der anderen Seite sehen Ihre Berechnungen bei Hochleistungsberechnungen möglicherweise anders aus, und Sie möchten möglicherweise mehr Zeit für die Optimierung Ihres Codes aufwenden.

Für mich scheinen 20% ein ziemlich guter Kompromiss zu sein, wenn Sie am Ende produktiver sind.


Gute Antwort, ich würde auch die Wichtigkeit hinzufügen, wo Leistung wichtig ist. Ein bestimmter wissenschaftlicher Code führt nicht nur eine Matrixmultiplikation durch. Wenn 20% Ihrer Laufzeit in der Matrixmultiplikation liegen, ergibt sich bei einem Leistungseinbruch von 20% insgesamt nur ein Unterschied von 4%, den ich gerne gegen eine benutzerfreundlichere Bibliothek eintauschen würde.
Aurelius

1
Und eine besser geschriebene Bibliothek bedeutet weniger Bugs, sodass Sie weniger Zeit darauf verwenden müssen, auf falsche Ergebnisse zu warten.
Davidmh

4

IMHO ist eine Strafe von bis zu 50% (aus welchem ​​Grund auch immer) nicht so schlimm.

Tatsächlich habe ich einen Leistungsunterschied von 0-30% gesehen, der nur auf dem Typ des Compilers basiert. Dies gilt für die sparsame MatMult-Routine von PETSc für Matrizen, die sich aus FE-Diskretisierungen niedriger Ordnung ergeben.


1

Das Softwaredesign wird sich mit der Zeit nicht automatisch verbessern. Die Leistung wird. Sie erhalten die 20% mit Ihrer nächsten CPU zurück. Außerdem wird es durch ein gutes Softwaredesign einfacher, die Bibliothek in Zukunft zu erweitern oder zu verbessern.


Ich denke nicht, dass dies die Frage beantwortet.
nicoguaro

0

Ein allgemeines Prinzip besteht darin, zuerst für ein gutes Design zu sorgen und dann die Leistung nur dann zu optimieren, wenn dies erforderlich ist . Anwendungsfälle, in denen eine Leistungssteigerung von 20% wirklich erforderlich ist, sind eher selten, wenn sie überhaupt auftreten.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.