<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Blog on chriscomputing.de</title><link>https://blog.chriscomputing.de/de/blog/</link><description>Recent content in Blog on chriscomputing.de</description><generator>Hugo -- gohugo.io</generator><language>de-DE</language><copyright>chriscomputing (CC BY-NC-SA 4.0)</copyright><lastBuildDate>Sat, 15 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.chriscomputing.de/de/blog/index.xml" rel="self" type="application/rss+xml"/><item><title>Mein günstiges Homelab: Self-Hosting mit Debian, ZFS, und Docker</title><link>https://blog.chriscomputing.de/de/blog/budget-homelab-debian-zfs-docker/</link><pubDate>Sat, 15 Aug 2026 00:00:00 +0000</pubDate><guid>https://blog.chriscomputing.de/de/blog/budget-homelab-debian-zfs-docker/</guid><description>&lt;p&gt;Eine fortlaufende aktualisierte Übersicht was ich in meinem Homelab selbst hoste. Weil ich meine digitale Unabhängigkeit schätze fokussiere ich mich auf Anwendungen um meine Abhängigkeit von außer-europäischen Technologiekonzernen zu reduzieren. Dank Immich sind meine Bilder unter meiner Kontrolle (ein großartiger Step um mit de-googling anzufangen), und dank Paperless-ngx sind meine Dokumente hervorragend organisiert, und das ebenfalls vollständig unter meiner Kontrolle. Diese und weitere Anwendungen laufen auf meinem Debian 13 server welcher ZFS und Docker nutzt. Ziel meines Techstacks ist es, ein zuverlässiges System mit geringem Wartungsaufwand zu erschaffen.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Eine fortlaufende aktualisierte Übersicht was ich in meinem Homelab selbst hoste. Weil ich meine digitale Unabhängigkeit schätze fokussiere ich mich auf Anwendungen um meine Abhängigkeit von außer-europäischen Technologiekonzernen zu reduzieren. Dank Immich sind meine Bilder unter meiner Kontrolle (ein großartiger Step um mit de-googling anzufangen), und dank Paperless-ngx sind meine Dokumente hervorragend organisiert, und das ebenfalls vollständig unter meiner Kontrolle. Diese und weitere Anwendungen laufen auf meinem Debian 13 server welcher ZFS und Docker nutzt. Ziel meines Techstacks ist es, ein zuverlässiges System mit geringem Wartungsaufwand zu erschaffen.</p>
<h2 id="pontus-mein-günstiger-lenovo-thinkcentre-server">Pontus: Mein günstiger Lenovo Thinkcentre server</h2>
<p>Vor ein paar Jahren habe ich ein gebrauchtes Lenovo Thinkcentre für nur 80€ gekauft, Baujahr 2016. Seitdem habe ich die interne SSD ausgetauscht, zwei 2 TB HDDs hinzugefügt und den RAM auf 16 GB erweitert. Letzter war vorher in meinem Desktop PC verbaut, bis ich ihn auf 32 GB aufgerüstet habe. Die technischen Daten sind:</p>
<ul>
<li>CPU: Intel Core i5-6400</li>
<li>RAM: 16 GB DDR4</li>
<li>Interner Speicher: 125 GB SSD für das Betriebsystem</li>
<li>Massenspeicher: Zwei 2 TB HDDs, eine für Production und eine für Backups</li>
<li>Betriebssystem: Debian 13</li>
</ul>
<p>
















  
  
  
      
      
  <figure class="figure d-block text-center" >
  <picture  >
  <img class="figure-img img-fluid" src="https://blog.chriscomputing.de/images/posts/homelab-state/pontus.webp?width=300&v=f263e8d5151879ff92ea5be1a2294b4c#center" alt="Kostengünstiger Lenovo Thinkcentre homelab server" title="Pontus, mein Server. Der DVD reader fehlt, zum Glück brauche ich ihn nicht." loading="lazy" height="816" width="459" style="width: 300px" />
</picture>

  <figcaption class="figure-caption">Pontus, mein Server. Der DVD reader fehlt, zum Glück brauche ich ihn nicht.</figcaption>
</figure>
</p>
<p>Als kleine Office-Workstation braucht das Thinkcentre wirklich wenig Strom. Trotz 2 aktiver HDDs verbraucht er im Idle nur 24 Watt. Energiekosten sind ein wichtiger Faktor für mich, weil Stromkosten in Deutschland ziemlich hoch sind. Trotzdem schalte ich meine Festplatten nicht ab wenn sie nicht gebraucht werden, da ich der Meinung bin, dass es sich durch den höheren Verschleiß finanziell nicht lohnt. Insbesondere bei den aktuellen (August 2026) Preisen.</p>
<h3 id="zuverlässige-datenspeicherung-mit-zfs-unter-linux">Zuverlässige Datenspeicherung mit ZFS unter Linux</h3>
<p>ZFS ist der Goldstandard für Dateisysteme. Es hat fortschrittlichen Schutz gegen Datenkorruption, hohe Performance und großartige Unterstützung für Snapshots, welche ich in meinen Backups verwende.<br>
Für ZFS habe ich mich aus zwei Gründen entschieden: Erstens brauche ich ein zuverlässiges und schnelles Dateisystem für mein Homelab. Ich hoste nicht nur für mich, sondern auch für meine Familie, was ich mich verantwortlich macht, ihre wertvollen Bilder nicht zu verlieren. Zweitens wird ZFS häufig in Unternehmen eingesetzt, sodass ich schon jetzt mit meinem Homelab wichtige Fähigkeiten für potentielle zukünftige Jobs erlernen kann.</p>
<p>Da die ZFS Lizenz nicht mit dem Linux Kernel kompatibel ist, ist ZFS gewöhnlicherweise nicht standardmäßig in Distributionen enthalten. Das OpenZFS Project entwickelt <a href="https://zfsonlinux.org/">OpenZFS on Linux</a>, welches OpenZFS als so genanntes out-of-tree module mittels DKMS installieren kann. Es wird nur mit stabilen, langsam(er) updatenden Betriebssystemen wie Debian empfohlen, weil ZFS und der Kernel unabhängig voneinander entwickelt werden. Daher sind sie nicht immer aufeinander abgestimmt, und der neuste Kernel muss nicht immer mit OpenZFS kompatibel sein. Außerdem muss ZFS nach der Installation des Betriebssystems installiert werden, was den Aufwand ein neues System zu installieren leicht erhöht. In meinen Augen ist ZFS aber ein sehr guter Kompromiss, und die leicht höhere Arbeit nehme ich gerne in Kauf.</p>
<h3 id="docker-um-meine-homelab-dienste-laufen-zu-lassen">Docker um meine Homelab Dienste laufen zu lassen</h3>
<p>All meine Dienste laufen in <a href="https://www.docker.com/">Docker</a>. Es ist verlässlich und hatte genug Zeit, um seine Qualität unter Beweis zu stellen. Außerdem ist Docker der de-facto Standard für Homelab Anwendungen, sodass fast alle Software die ich hosten möchte als Container Image mit Anleitung für Docker verfügbar ist. Ich benutze docker-compose, um meine Container deklarativ zu verwalten. Dies erlaubt es mir, auf einem neuen System schnell wieder einsatzbereit zu sein.</p>
<h3 id="was-ich-in-meinem-homelab-hoste">Was ich in meinem Homelab hoste</h3>
<p>Ein Homelab ist nur so gut, wie die Software die es hostet. Ich habe meine Mission, so viele Big-Tech Dienste wie möglich zu ersetzen mit denen begonnen, die die größte Masse und die wertvollsten Daten haben. Um dabei, auch für meine Familie, die beste Bedienbarkeit zu gewährleisten, habe ich die Voraussetzung, dass alle Dienste Single-Sign-On (SSO) unterstützen. Dank SSO brauchen alle Nutzende sich nur ein mal auf jedem Gerät einloggen, und sind dann sofort für alle Homelab Dienste authentifiziert. Das erspart es ihnen, mit mehreren Nutzernamen und Passwörten hantieren zu müssen.<br>
Außerdem möchte ich meine Konfiguration so deklarativ wie möglich gestalten. Das bezieht sich auch auf die Konfiguration eines jeden Dienstes. Im Falle eines Systemausfalls möchte ich meine Dienste schnellstmöglich vollumfänglich online bringen können.</p>
<h4 id="authelia-für-single-sign-on-sso">Authelia für Single Sign On (SSO)</h4>
<p>Ich nutze <a href="https://www.authelia.com/">Authelia</a> um SSO mittels OpenID Connect (OIDC) zu implementieren. Authelia ist eine leichtgewichtige Alternative zu Authentik. Im Gegensatz zu letzterem wird Authelia ausschließlich über deklarative Konfigurationsdateien konfiguriert, inklusive der registrierten Nutzenden. Authelia ist ein zertifizierter OIDC 1.0 Provider, was es kompatibel zu allen Diensten die ebenfalls OIDC 1.0 implementieren macht.</p>
<h4 id="traefik-als-reverse-proxy">Traefik als reverse proxy</h4>
<p>Als einziger Server laufen alle Dienste auf Pontus, was einen reverse proxy unabdingbar macht. Es existieren viele gute. Durch meine Arbeit bin ich mit Nginx und Apache httpd vertraut. Für mein Homelab habe ich mich allerdings für Traefik entschieden, weil es nativ mit Docker interagiert, und ich nur ein paar Zeilen pro Dienst schreiben muss, um ihn durch Traefik zugänglich zu machen. Weil die Konfiguration direkt in der <code>docker-compose.yml</code> geschieht, muss ich mich außerdem nicht um weitere Konfigurationsdateien kümmern. Traefiks automatische Erneuerung von SSL Zertifikaten ist ebenfalls praktisch. Insbesondere meine Eltern wären verunsicher, wenn ihr Browser auf einmal eine große Warnung anzeigt, wenn sie auf ihre Fotos oder Dokumente zugreifen wollen.</p>
<h4 id="mesh-vpn-mit-tailscale">Mesh-VPN mit Tailscale</h4>
<p>Mein Homelab soll aus der ganzen Welt erreichbar sein, aber aus Sicherheitsgründen nur für ausgewählte Menschen. <a href="https://tailscale.com/">Tailscale</a> ist ein moderner Mesh-VPN, welcher Geräte durch Firewalls und NATs miteinander verbinden kann, ohne ein Port in einer Firewall geöffnet werden muss. Tailscale läuft bare-metal auf Pontus. So kann ich Pontus direkt als Tailscale Knoten registrieren, und alle Dienste auf Pontus sind sofort im Tailnet erreichbar. Das nimmt mir die Möglichkeit für Nutzendespezifische Zugangskontrolle, allerdings hat eh nur meine Familie Zugriff und sie können eh alle Dienste nutzen, also brauche ich so etwas momentan nicht.</p>
<p>Leider ist Tailscale ein closed-source, kommerzieller Dienst. Sie haben mindestens Zugriff auf Metadaten, wann welches Gerät sich mit welchen anderen verbindet. In Zukunft würde ich Tailscale gerne durch eine selbst-gehostete Option ersetzen, zum Beispiel <a href="https://github.com/slackhq/nebula">Nebula</a>.</p>
<h4 id="immich-als-google-fotos-ersatz">Immich als Google Fotos Ersatz</h4>
<p><a href="https://immich.app/">Immich</a> ist der wichtigste und erste Service in meinem Homelab. Und es ist großartig. Meine Familie und ich lieben es, wir alle speichern unsere Bilder und Videos in Immich. Die eigenen Bilder und Fotos unter der eigenen Kontrolle zu haben ist einer der wichtigsten Schritte um zu de-Googlen, und das macht Immich sehr einfach. Bevor ich Immich hatte, habe ich meine Bilder in unregelmäßigen Abständen auf meinen PC verschoben, und von da aus auf eine externe Festplatte dupliziert. Meine Familie hat einfach Google Fotos benutzt. Dank Immich kann ich alle meine Fotos immer im Browser oder der Handyapp anschauen, während sie sicher auf meinem Server verwahrt sind. Außerdem hat Immich lokale Gesichtserkennung, dank der man schnell nach Bildern mit bestimmten Personen suchen kann. Zusätzlich bietet Immich Objekterkennung, OCR und GPS tags. All das 100% lokal auf meinem alten Server, der dafür überraschend wenig Ressourcen aufwenden muss.</p>
<p>Ich weiß, dieser Abschnitt liest sich wie ein Werbetext für Immich. Aber Immich ist halt einfach großartig.</p>
<h4 id="paperless-ngx-zur-dokumentenverwaltung">Paperless-ngx zur Dokumentenverwaltung</h4>
<p>Ich hoste auch <a href="https://docs.paperless-ngx.com/">Paperless-ngx</a> als Dokumentenverwaltung. Ehrlicherweise nutze ich es noch nicht viel, da ich erst kürzlich OIDC in <a href="https://play.google.com/store/apps/details?id=de.astubenbord.paperless_mobile">Paperless mobile</a>, der Android-App, zum Laufen bekommen habe. Sie ermöglicht es, Bilder von Dokumenten als PDFs hochzuladen. Da ich keinen Scanner besitze, sind Bilder die einzige Möglichkeit für mich, analoge Dokumente zu digitalisieren. Wenn ich Zeit habe, muss ich mich mal hinsetzen und einen Haufen digitaler und analoger Dokumente in Paperless einpflegen.</p>
<h4 id="beszel-für-docker-natives-monitoring">Beszel für Docker-natives Monitoring</h4>
<p><a href="https://www.beszel.dev/">Beszel</a> ist eine sehr schöne und sehr praktische Anwendung für Monitoring mit eingebautem Support für Docker Container. Beszel selbst wird ebenfalls mit Docker deployed. Zur Zeit gilt meine Hauptsorge der Gesundheit meiner beiden Festplatten. Es ist kein Problem für meine Familie und mich, wenn ein Dienst eine Zeit lang ausfällt, und der Server ist weit entfernt davon überlastet zu sein, sodass wir uns darum auch nicht sorgen müssen. Deutlich problematischer ist der Ausfall einer Festplatte. Ich habe zwar sowohl onsite als auch offside Backups, aber kein Raid, alle Dienste laufen auf einer Festplatte. Daher wüsste ich gerne frühzeitig, wenn eine den Geist aufgibt, sodass ich sie austauschen kann, ohne dass das gesamte Homelab tagelang nicht verfügbar ist, bis eine neue Festplatte geliefert wurde und ich sie eingebaut habe. Docker ermöglicht es, die Festplatten an den Beszel Container weiter zu geben, sodass dieser fortlaufend die SMART Daten aller Festplatten auslesen kann.</p>
<p>
















  
  
  
      
      
  <figure class="figure " >
  <picture  >
  <img class="figure-img img-fluid" src="https://blog.chriscomputing.de/images/posts/homelab-state/beszel_smart.png?v=f263e8d5151879ff92ea5be1a2294b4c" alt="Festplatten SMART Daten Dashboard in Beszel" title="Festplatten SMART Daten in Beszel" loading="lazy" height="493" width="1831" />
</picture>

  <figcaption class="figure-caption">Festplatten SMART Daten in Beszel</figcaption>
</figure>
</p>
<p>Beszel ist weniger detailliert und flexibel wie Prometheus + Grafana (welches ich von meiner Arbeit kenne), die beiden wären für mein aktuelles Homelab aber auch übertrieben. Beszel ist eine einfache, schöne Lösung. Genau das, was ich brauche.</p>
<h4 id="datensicherung-und-backups">Datensicherung und Backups</h4>
<p>Wie bereits erwähnt, ist die Sicherheit meiner Daten extrem wichtig. Durch mein enges Budget habe ich keinen zweiten onsite Backupserver. Das onsite Backup ist schlicht die zweite 2 TB Festplatte, welche in einem anderen ZFS Pool lebt:</p>
<ul>
<li><code>data-pool</code> für Production. Hier sind alle Docker Volumes gespeichert.</li>
<li><code>backup-pool</code> für Backups. Jede Nacht führt ein Skript einen inkrementellen ZFS Snapshot von <code>data-pool</code> aus und sendet ihn zu <code>backup-pool</code>.</li>
</ul>
<p>Sollten während einem Backup Probleme auftreten, werde ich mittels <a href="https://ntfy.sh/">ntfy</a> informiert.</p>
<p>Natürlich ist diese Situation nicht ideal. Sie ist nicht fehlertolerant. Falls einer der beiden Festplatten ausfällt, sollte Pontus sofort abgeschaltet werden. Ich <em>könnte</em> ihn temporär ohne lokales Backup weiterbetreiben und mich auf mein offsite Backup verlassen, aber ideal wäre das bei weitem nicht.</p>
<p>Mein offsite Backup ist eine 5 TB <a href="https://www.hetzner.com/de/storage/storage-box/">Hetzner Storagebox</a>. Getreu der 3-2-1 Regel werden die Backups hier mit <a href="https://www.borgbackup.org/">Borg Backup</a> durchgeführt.</p>
<h2 id="media-diy-satelliten-tv">Media: DIY Satelliten TV</h2>
<p>Meine Eltern schauen gerne Satelliten TV, selbst in Zeiten von Netflix und Co. Kommerzielle Receiver sind teuer und voller kommerzieller Software welcher man nur bedingt trauen kann. Insbesondere wenn sie ans Internet angeschlossen werden will, so wie alles heutzutage. Als der Alte meiner Eltern den Geist aufgegeben hat, habe ich einen gebrauchten <a href="https://www.minisforum.com/">Minisforum</a> Mini-PC und einen TV Stick von <a href="https://www.minisforum.com/">Sundtek</a> gekauft. Der Mini-PC, welchen ich schlicht <em>media</em> nenne, ist an ihren Fernseher über HDMI angeschlossen. Zum Glück ist der Fernseher zu alt, um ein internet-abhängiger Smart-TV zu sein.</p>
<p>Das Betriebssystem von <em>media</em> ist ebenfalls Debian 13. Überall das gleiche Betriebssystem zu verwenden, verringert den Wartungsaufwand. Nach einem Start loggt der PC sich automatisch in einen Account ohne sudo und root Rechte ein und startet <a href="https://kodi.tv/">Kodi</a>, welches meine Eltern mit <a href="https://play.google.com/store/apps/details?id=org.xbmc.kore">Kore</a> übers Smartphone vom Sofa aus steuern können. Falls meine Eltern einen Streaming Service nutzen wollen, können sie die dauerhaft angeschlossene Tastatur und Maus verwenden.</p>
<p>Der Sundtek TV Stick ist via USB angeschlossen und bekommt sein DVB-S signal von der Satellitenschüssel. Sundtek bietet aktuelle Treiber für Linux, welche auch einen Systemd service installieren, der den Stick automatisch nach dem Bootvorgang startet.</p>
<h2 id="offsite-backups-für-den-ernstfall">Offsite Backups für den Ernstfall</h2>
<p>Als Offsite Backups nutze ich eine <a href="https://www.hetzner.com/de/storage/storage-box/">Hetzner Storagebox</a> mit 5 TB mit <a href="https://www.borgbackup.org/">Borg Backup</a>. Nicht nur <em>pontus</em> sichert seine Daten dorthin, sondern auch die Laptops meiner Eltern, sowie mein Laptop und PC. Die 5 TB sollten dafür locker ausreichen. Jedes Gerät hat einen eigenen Subaccount mit eigenem SSH Schlüssel auf der Storage Box und nur Zugriff auf sein Borg Repository. Die Repositories sind selbstverständlich verschlüsselt, inkrementelle Backups geschehen täglich.</p>
<p>Da Borg eine andere Technologie als ZFS ist, erfüllt das System auch die &ldquo;1&rdquo; der 3-2-1 Regel, welche besagt, dass mindestens ein Backup mit einer anderen Technologie gemacht werden sollen.</p>
]]></content:encoded></item><item><title>Syllogistisches Denken in LLMs verbessern</title><link>https://blog.chriscomputing.de/de/blog/improving_syllogistic_reasoning/</link><pubDate>Tue, 11 Aug 2026 00:00:00 +0000</pubDate><guid>https://blog.chriscomputing.de/de/blog/improving_syllogistic_reasoning/</guid><description>&lt;h2 id="syllogistisches-denken-von-llms-verbessern"&gt;Syllogistisches Denken von LLMs verbessern&lt;/h2&gt;
&lt;p&gt;Large Language Models (LLMs) wissen viel über die Welt, was häufig ihrer Fähigkeit strikte logische Folgerungen zu machen in die Quere kommt, insbesondere wenn diese nicht mit dem Weltwissen kompatibel sind. Ein hervorragendes Beispiel ist &lt;strong&gt;syllogistisches denken&lt;/strong&gt;, bei dem LLMs Probleme mit dem so genannten &lt;em&gt;content effect&lt;/em&gt; haben. Content effect wird das oben beschriebene Phänomen genannt, nämlich wenn eine logisch valide Schlussfolgerung verworfen wird da sie inhaltlich nicht mit dem Weltwissen des LLMs übereinstimmt.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="syllogistisches-denken-von-llms-verbessern">Syllogistisches Denken von LLMs verbessern</h2>
<p>Large Language Models (LLMs) wissen viel über die Welt, was häufig ihrer Fähigkeit strikte logische Folgerungen zu machen in die Quere kommt, insbesondere wenn diese nicht mit dem Weltwissen kompatibel sind. Ein hervorragendes Beispiel ist <strong>syllogistisches denken</strong>, bei dem LLMs Probleme mit dem so genannten <em>content effect</em> haben. Content effect wird das oben beschriebene Phänomen genannt, nämlich wenn eine logisch valide Schlussfolgerung verworfen wird da sie inhaltlich nicht mit dem Weltwissen des LLMs übereinstimmt.</p>
<p>In diesem Post erläutere ich zwei Methoden, um content effect zu verringern: Activation Steering und neuro-symbolosch logische Löser. Dieser Post basiert auf einem Gruppenprojekt, welches ich zusammen mit 2 Freunden für einen Universitätskurs in Natural Language Processing gemacht habe. Leider hatten wir keine Zeit mehr, unser Projekt für <a href="https://sites.google.com/view/semeval-2026-task-11">Aufgabe 11</a> der SemEval 2026 einzureichen, auf der unser Projekt basiert hat. Selbstverständlich habe ich vor der Veröffentlichung dieses Posts das Einverständnis meiner Co-Autoren eingeholt. Ein paar Absätze sind direkte Zitate aus unserem Report.</p>
<h3 id="was-ist-syllogistisches-denken">Was ist syllogistisches Denken?</h3>
<p>Eine Schlussfolgerung wird aus mindestens einer Premisse gezogen. Dabei geht es ausschließlich um die logische Validität, unabhängig davon, ob Prämissen oder die Schlussfolgerung in der Realität wahr ist. LLMs vermischen oft faktische und logische Validität, was zu falschen Schlussfolgerungen führt.</p>
<div class="alert alert-info" role="region" aria-label="info">
  <div class="alert-title">Beispiel Syllogismus</div>
  <div class="alert-body">Ein Tier welches Milch produziert, ist eine Kuh. Eine Ziege produziert Milch. Daher ist eine Ziege eine Kuh.</div>
</div>
<p>Das obrige Beispiel ist logisch korrekt, aber faktisch nicht, und stimmt nicht mit dem Weltwissen eines LLMs überein. Daher tendieren LLMs dazu, diesen Syllogismus als falsch zu bewerten. Dieses als content effect bekanntes Phänomen ist das schwerwiegenste Problem, welches unsere SemEval Aufgabe lösen sollte. .<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>Wir haben uns früh dazu entschlossen, mit kleinen open-weight LLMs zu arbeiten. Zwar haben große state-of-the-art (SOTA) Modelle eine deutlich höhere Grundfähigkeit, aber kleine Modelle sind wichtig für edge-deployments, resourcenbeschränkte Umgebungen und low-latency inferenz. Wir haben <em>Llama-3.2-1B-Instruct</em> und <em>Llama-3.1-8B-Instruct (8 bit)</em> verwendet.</p>
<h2 id="bestehende-literatur-zum-content-effect-in-llms">Bestehende Literatur zum content effect in LLMs</h2>
<p>Das Benchmarking formaler logischer Deduktion und logischer Konsistenz in LLMs hat sich zu einem aktiven Forschungsgebiet der NLP-Forschung entwickelt. In der bisherigen Literatur wird häufig die Anfälligkeit für „Belief Bias“ (z. B. content effect) hervorgehoben, bei denen Modelle semantische Plausibilität gegenüber deduktiver Gültigkeit priorisieren. Darüber hinaus hat sich gezeigt, dass LLMs menschenähnliche Denkfehler produzieren, indem sie beispielsweise <em>figurativen Effekten</em> zum Opfer fallen, wie etwa der Sensitivität gegenüber der Reihenfolge von Argumenten und der syntaktischen Anordnung von Variablen. <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>Es wurden auch Anstrengungen in verschiedene Strategien zur Minderung dieser Probleme investiert: <em>activation steering</em>, eine einfache Maßnahme, die das Schlussfolgern durch die Manipulation interner Aktivierungen beeinflusst <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, kann die Abhängigkeit von Weltwissen verringern, was wiederum den Inhaltseffekt reduziert. Ein weiterer Forschungsansatz befasst sich mit neuro-symbolischer KI, bei der ein LLM als semantischer Parser mit einer nachgeschalteten Engine für formales Schlussfolgern kombiniert wird. Das LLM liest die Argumente im Klartext und wandelt sie in die Eingabe um, die ein logischer Löser erwartet. Der Löser berechnet dann zuverlässig die logische Korrektheit <sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>. Außerdem wurden Anstrengungen unternommen, das syllogistische Schlussfolgern ausschließlich durch Prompt-Engineering zu verbessern <sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>.</p>
<h2 id="der-semeval-2026-datensatz">Der SemEval 2026 Datensatz</h2>
<p>Der SemEval 2026 Aufgabe 11 Datensatz besteht aus binären Syllogismen mit je zwei Prämissen und einer Schlussfolgerung. Das LLM muss entscheiden, ob die Schlussfolgerung logisch valide ist. Zusätzlich hat jeder Syllogismus ein <em>plausability label</em>, welches angibt, ob die korrekte Schlussfolgerung mit dem Weltwissen eines LLMs übereinstimmt. Dieses Label wurde natürlich nur während dem Training verwendet, nicht während der Evaluation. Die Antwort des LLMs kann mit einem <em>validity label</em> überprüft werden.</p>
<p>Der Datensatz ist nur auf Englisch verfügbar. Da die offizielle Evaluation auch weitere Sprachen beinhaltet, haben wir den Datensatz mit Hilfe von Meta&rsquo;s <em>facebook/nllb-200-3.3B</em> in alle Zielsprachen übersetzt. Weitere Zielsprachen waren unter anderem Deutsch und Italienisch, aber auch weniger in Trainingsdaten repräsentierte Sprachen wie Swahili oder Bengali. Insgesamt wird das System auf 12 verschiedenen Sprachen evaluiert.</p>
<h2 id="auswertung-der-ergebnisse">Auswertung der Ergebnisse</h2>
<p>Die Organisatoren von Aufgabe 11 haben ein Evaluationsskript zur Auswertung bereitgestellt. Es kombiniert eine gewöhnliche Korrektheitsmetrik mit weiteren Metriken, um content effect zu erkennen.</p>
<p><strong>Genauigkeit:</strong><br>
Genauigkeit ist der Anteil der Antworten bei denen das System die Validität des Syllogismus richtig eingeschätzt hat. Desto höher die Genauigkeit, desto besser.</p>
<p><strong>Content Effect Metriken:</strong><br>
Außerdem gibt das Skript mehrere Metriken im Zusammenhang mit dem content effect zurück:</p>
<ul>
<li><em>plausible-valid</em>: Syllogismen die sowohl plausibel als auch korrekt sind</li>
<li><em>impausible-valid</em>: Syllogismen die zwar unplausibel, aber korrekt sind</li>
<li><em>plausible-invald</em>: Syllogismen, die zwar plausibel, aber falsch sind</li>
<li><em>implausible-valid</em>: Syllogismen die sowohl unplausibel als auch falsch sind</li>
</ul>
<p>Ein niedriger content effect ist besser.</p>
<p>Der <em>Intra-plausability content effect</em> misst den Bias in Richtung eines spezifischen Labels innerhalb der gleichen Plausibilität, also beispielsweise ob ein System bei plausiblen Syllogismen eher dazu tendiert diese als korrekt zu klassifizieren, auch wenn sie das nicht sind. Er wird berechnet als:</p>
$$
\begin{equation}
\text{Bias}_{\text{intra}} = \frac{1}{2} \left( \left| \text{ACC}_{\text{plausible,valid}} - \text{ACC}_{\text{implausible,valid}} \right| + \left| \text{ACC}_{\text{plausible,invalid}} - \text{ACC}_{\text{implausible,invalid}} \right| \right)
\end{equation}
$$<p>Der <em>Cross-plausibility content effect</em> misst, wie sehr das Modell sich auf die Plausibilität innerhalb eines korrektheit-Labels verlässt:</p>
$$
\begin{equation}
\text{Bias}_{\text{inter}} = \frac{1}{2} \left( \left| \text{ACC}_{\text{plausible,valid}} - \text{ACC}_{\text{plausible,invalid}} \right| + \left| \text{ACC}_{\text{implausible,valid}} - \text{ACC}_{\text{implausible,invalid}} \right| \right)
\end{equation}
$$<p>Der <em>Total content effect</em> wird aus dem Durchschnitt der beiden Plausibilitäten berechnet:</p>
$$
\begin{equation}
\text{TCE} = \frac{1}{2} \left( \text{Bias}_{\text{intra}} + \text{Bias}_{\text{inter}} \right)
\end{equation}
$$<p>Von wo ausgehend der <em>finale Score</em> berechnet werden kann:</p>
$$
Score = \frac{\text{accuracy}}{1 + \log(1 + \text{TCE})}
$$<h2 id="ansätze-um-syllogistisches-denken-von-llama-3-zu-verbessern">Ansätze um syllogistisches Denken von Llama 3 zu verbessern</h2>
<p>Wir haben zwei Ansätze versucht, deren Resultate im späteren Verlauf miteinander verglichen werden. Der Erste ist Activation Steering, der Zweite einen logischen Löser zusammen mit dem LLM zu nutzen.</p>
<h3 id="activation-steering">Activation Steering</h3>
<p>Die Grundeidee von Activation Steering ist es, die internen Berechnungen eines LLMs zu manipulieren, indem interne Aktivierungen linear in die gewünschte Richtung verschoben werden. Um diese Richtung zu identifizieren, wird ein Satz gelableter Trainingsdaten $D = \{(x_i, y_i)\}_{i=1}^{N}$ benötigt, wobei positive Label logisch korrekte Schlussfolgerungen markieren, und negative Label durch belief bias entstandene negative Schlussfolgerungen.</p>
<p>In meiner Erfahrung ist die folgende Erklärung für Activation Steering einfach zu verstehen: Stellen Sie sich ein Gehirn vor. Dieses Gehirn wird Medikamenten ausgesetzt, welche Gehirnaktivitäten in bestimmten Arealen verstärken oder verringern. Beispiele für solche Medikamente sind solche gegen Narkolepsie. Das ist was Activation Steering mit LLMs macht. Es beeinflusst die Aktivierungen des LLMs, um dessen Verhalten zu beeinflussen. Activation Steering ist vergleichbar zu dem was Anthropic in ihrem Experiment getan hat, welches <a href="https://www.anthropic.com/news/golden-gate-claude">Golden Gate Claude</a> hervorgebracht hat.</p>
<p>Der größte Vorteil von Activation Steering liegt in seiner geringen Rechenintensität. Es bedarf nur weniger Ressourcen um die korrekten Aktivierungen zu identifizieren, deutlich weniger als traditionelles Finetuning. Des Weiteren erhöht es nicht den Rechenaufwand während der Inferenz, und kann auf einer pro-Anfragen Basis angewandt werden.</p>
<h4 id="contrastive-activation-addition-caa">Contrastive Activation Addition (CAA)</h4>
<p>Contrastive Activation Addition (CAA) ist eine recht einfache Form des Activation Steerings. Alle Aktivierungen für gewollte und ungewollte Ausgaben werden getrennt voneinander gesammelt. Wenn $\mu^+$ der Durchschnitt der gewollten (positiven) Aktivierungen und $\mu^-$ der Durchschnitt der ungewollten (negativen) Aktivierungen ist, kann ihre Differenz als $\Delta \phi = \mu^{+} - \mu^{-}$ berechnet werden. Während der Inferenz können die Aktivierungen des Modells modifiziert werden, indem eine skalierte Version des Kontrastvektors angewandt wird: $\tilde{\phi}(x) = \phi(x) + \alpha \cdot \Delta \phi$. $\phi$ ist die unmanipulierte Aktivierung und $\tilde{\phi}$ die finale Aktivierung mit angewandtem Steering. Diese Intervention schiebt die Repräsentationen der hidden States in Richtung des Subspaces welcher mit ungebiasten logischen Schlussfolgerungen assoziiert wird.<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup></p>
<h4 id="conditional-activation-steering-cast">Conditional Activation Steering (CAST)</h4>
<p>Conditional Activation Steering (CAST) stellt eine Verbesserung über CAA dar. Im Gegensatz zu CAA subtrahiert CAST auch vom unmanipuliertem Kontrollvektor $\phi$ <sup id="fnref1:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>. Wir haben Bedingungsvektoren verwendet, um zu bestimmen, ob $\alpha$ positiv oder negativ sein sollte, wobei letzteres die Richtung des Kontrollvektors im Vergleich zu CAA invertiert.</p>
<p>Wir haben mit zwei Bedingungsvektoren $\psi_{c}^{+}$ und $\psi_{c}^{-}$ gearbeitet, welche die durchschnittliche Summe valider und invalider Aktivierungen sind. Das Vorzeichen von $\alpha$ wird dadurch bestimmt, ob die Aktivierung ähnlicher zum durchschnitt der validen oder invaliden Aktivierungen während dem Training ist.</p>
$$
f \left(\alpha, \phi(x), \psi_c^{+}, \psi_c^{-}\right) =
\begin{cases}
    - \alpha, & \text{ if } \operatorname{sim}(\phi(x), \operatorname{proj}_{\psi_c^{+}} \phi(x)) > \operatorname{sim} (\phi(x), \operatorname{proj}_{\psi_c^{-}} \phi(x)) \\
    \alpha, & \text{otherwise.}
\end{cases}
$$<p><strong>k-cast:</strong> Ein Problem mit gewöhnlichen CAST Methoden ist Informationsverlust welcher dadurch entsteht, dass alle validen und invaliden Aktivierungen während dem Training zusammen gerechnet werden. Dadurch gehen Details in der Aktivierung verloren. k-CAST verwendet k-nearest-neighbor clustering, um während der Inferenz vergleichbare Aktivierungen aus dem Training zu finden. Das Vorzeichen von $\alpha$ wird anhand der $k = 5$ nächsten Nachbarn mit den ähnlichsten Aktivierungen bestimmt.</p>
<h3 id="neuro-symbolic-logischer-löser">Neuro-symbolic logischer Löser</h3>
<p>Der zweite Ansatz ist es, das LLM mit einem logischen Löser zu verknüpfen. Der Löser überprüft die Logik des Syllogismus, was korrekte Resultate garantiert, wenn die Eingaben korrekt waren. Das LLM muss den Syllogismus in syntaktisch und semantisch korrekte Eingaben für den Löser parsen.</p>
<p>Wir haben jeden Satz auf exakt einen Qualifier aus <code>{all, none, some, some_not}</code> und zwei Prediktoren <code>(a,b)</code> gemapped, was zu einer kompakten symbolischen Repräsentation unabhängig von der Sprache des Syllogismus führt.</p>
<p>
















  
  
  
      
      
  <figure class="figure " >
  <picture  >
  <img class="figure-img img-fluid" src="https://blog.chriscomputing.de/images/posts/improving_syllogistic_reasoning/ns-solver_extraction.png?width=600&v=f263e8d5151879ff92ea5be1a2294b4c" alt="Extrahierung von Eingaben durch das LLM" title="Extrahierung von Eingaben durch das LLM" loading="lazy" height="741" width="1471" style="width: 600px" />
</picture>

  <figcaption class="figure-caption">Extrahierung von Eingaben durch das LLM</figcaption>
</figure>
</p>
<h4 id="dreischrittige-pipeline-zur-datenextraktion">Dreischrittige Pipeline zur Datenextraktion</h4>
<p>Für jeden Syllogismus sollte das LLM einen <em>quantifier</em> und zwei <em>terms</em> von jedem der drei Sätze extrahieren. Aus allen drei Sätzen gleichzeitig zu extrahieren erhöhte Formatierungsfehler und vermischte gelegentlich Informationen aus mehreren Sätzen, weswegen Extrahierung Satz-für-Satz ausgeführt wurde. Um konsistente Benennung zu fördern, haben wir eine kurze <code>used_predicates</code> Liste geführt, welche im Prompt integriert wurde und Wiederverwendung früherer Prädikatennamen gefördert hat.</p>
<p>Weil der logische Löser unterschiedliche Strings als unterschiedliche Prädikate behandelt, mussten extrahierte Begriffe normalisiert werden. Des Weiteren haben wir den vollständigen Syllogismus in jedem Prompt inkludiert, ebenfalls um stabile Benennung über alle Sätze hinweg zu fördern.</p>
<p>Schlussendlich validierten wir Ausgaben und gaben sie im 3-tuple Format <code>(quantifier=..., a=..., b=...)</code> an den Löser weiter. Syntaktisch inkorrekte extrahierungen waren selten, aber wenn sie auftraten, sind wir auf zufälliges raten zurückgefallen.</p>
<h4 id="schlussfolgerungen-mit-dem-z3-löser-validieren">Schlussfolgerungen mit dem Z3 Löser validieren</h4>
<p>Wir haben den <a href="https://www.microsoft.com/en-us/research/project/z3-3/">Z3</a> Löser für SMT (Satisfiability Modulo Theories) von Microsoft verwendet, um zu bestimmen, ob die Schlussfolgerung korrekterweise aus den Prämissen folgt. Gegeben korrekt geparster Eingaben, gibt Z3 ein binäres Urteil (<code>valid</code> oder <code>invalid</code>) zurück, welches wir als die finale Ausgabe unserer Pipline verwendeten.</p>
<h4 id="umgang-mit-mehrsprachigen-syllogistischen-daten">Umgang mit mehrsprachigen syllogistischen Daten</h4>
<p>Um die durch unsere Aufgabe geforderte Unterstützung verschiedenster Sprachen sicherzustellen, haben wir sprach-abhängiges Preprocessing vom sprach-unabhängigen extrahieren getrennt. Wir detektieren die verwendete Sprache automatisch und trennen die 3 Sätze mittels ICU sentence boundary detection, bevor die Eingaben in die extraction Pipeline gegeben werden. Die besten Ergebnisse wurden mit einem englischen Prompt erzielt, unabhängig von der Sprache des Syllogismus.</p>
<p>Nach der Extrahierung wurden die Prädikate mit sprachspezifischen Filtern normalisiert (beispielsweise Artikel entfernt). Diese Schritte ergaben in den allermeisten Fällen brauchbare Eingaben für den Solver.</p>
<h4 id="auswahl-des-richtigen-llms">Auswahl des richtigen LLMs</h4>
<p>Wir haben Modelle der Llama Familie verwendet. Llama-3.2-1B-Instruct stellte sich als unverlässlich für die strukturierte Extraktion heraus (bspw. gibt es häufig <code>some</code> statt <code>some_not</code> aus), weswegen wir zusätzlich Llama-3.1-8B-Instruct quantisiert auf 8-bit verwendet haben. Beide Modelle sind schnell und brauchen wenige Ressourcen, welches niedriglatente Anwendung in Ressourcen-beschränkten Umgebungen ermöglicht.</p>
<h2 id="ergebnisse-activation-steering-vs-logische-löser">Ergebnisse: Activation Steering vs. Logische Löser</h2>
<p>Die Experimente wurden mit 5 verschiedenen Seeds (10, 20, 30, 40, 42) ausgeführt. Für Activation Steering wurde der Datensatz 90/10 zwischen Training und Evaluation aufgeteilt. Da das Training nur aus der Berechnung der Kontrollvektoren besteht, geschieht es schnell. Auf einer einzelnen Nvidia A100 benötigte Training und Evaluation für alle Sprachen sowie nur für englischsprachige Eingaben insgesamt nur wenige Minuten, inklusive der Evaluation des logischen Lösers. Letzterer benötigt kein Training.</p>
<p>Die Evaluation beinhaltet als Referenz außerdem eine Baseline, in der das LLM nur gepromptet wurde.</p>
<p>
















  
  
  
      
      
  <figure class="figure " >
  <picture  >
  <img class="figure-img img-fluid" src="https://blog.chriscomputing.de/images/posts/improving_syllogistic_reasoning/results_english.png?width=600&v=f263e8d5151879ff92ea5be1a2294b4c" alt="Diagramm mit den Evaluierungsergebnissen von Activation Steering im Vergleich zu neuro-symbolischen Solvern auf englischen Trainingsdaten." title="Ergebnisse für rein englische Daten. Für Accuracy und Score gilt: Höher ist besser. Für TCE gilt: Niedriger ist besser. Die wichtigste Metrik ist der Score." loading="lazy" height="484" width="1096" style="width: 600px" />
</picture>

  <figcaption class="figure-caption">Ergebnisse für rein englische Daten. Für Accuracy und Score gilt: Höher ist besser. Für TCE gilt: Niedriger ist besser. Die wichtigste Metrik ist der Score.</figcaption>
</figure>
</p>
<p>
















  
  
  
      
      
  <figure class="figure " >
  <picture  >
  <img class="figure-img img-fluid" src="https://blog.chriscomputing.de/images/posts/improving_syllogistic_reasoning/results_multi.png?width=600&v=f263e8d5151879ff92ea5be1a2294b4c" alt="Diagramm mit den Evaluierungsergebnissen von Activation Steering im Vergleich zu neuro-symbolischen Solvern auf mehrsprachigen Trainingsdaten." title="Ergebnisse für mehrsprachige Daten. Für Accuracy und Score gilt: Höher ist besser. Für TCE gilt: Niedriger ist besser. Die wichtigste Metrik ist der Score." loading="lazy" height="484" width="1096" style="width: 600px" />
</picture>

  <figcaption class="figure-caption">Ergebnisse für mehrsprachige Daten. Für Accuracy und Score gilt: Höher ist besser. Für TCE gilt: Niedriger ist besser. Die wichtigste Metrik ist der Score.</figcaption>
</figure>
</p>
<h3 id="activation-steering-1">Activation Steering</h3>
<p>Activation Steering ist <strong>dauerhaft besser als die Baseline und neuro-symbolischen Ansätze</strong>. Im ausschließend englischsprachigen Test wurde der beste Score von CAST erreicht, obwohl k-CAST eine höhere <em>accuracy</em> erreichte. Wir vermuten, dass es daran liegt, dass k-CAST nicht ausreichend viele Trainingsdaten zur Verfügung hat, um seine das Vorzeichen anhand der 5 nächsten Nachbarn <strong>korrekt</strong> zu bestimmen.</p>
<p>Im <strong>mehrsprachigen Test ist k-CAST besser als CAST</strong>, vermutlich weil Aktivierung anhand des durchschnittlichen Vektors erheblich ungenauer wird, wenn die Aktivierungen für alle Sprachen summiert werden. Allerdings hat k-CAST mehr Rechenaufwand, da alle Aktivierungen der Trainingsdaten geladen werden müssen, um die k-nearest Nachbarn zu identifizieren, welche das Vorzeichen von $\alpha$ bestimmen.</p>
<h3 id="neuro-symbolisch-logische-pipeline">Neuro-symbolisch logische Pipeline</h3>
<p>Mit Llama 1B konnten wir nur eine geringe Verbesserung gegenüber der Baseline beobachten. Wir vermuten, dass dies hauptsächlich an Fehlern in der Extrahierungspipeline liegt. Llama 8b (quantisiert auf 8-bit) produziert deutlich bessere Ergebnisse, was zeigt, dass die Fähigkeit des LLMs ein wichtiger Faktor für diesen Ansatz darstellt. Noch fähigere LLMs würden vermutlich noch bessere Ergebnisse erzielen.</p>
<ul>
<li>
<p><strong>Llama 1B (Unter den Erwartungen geblieben):</strong> Zeigte kaum Verbesserungen gegenüber der Baseline. Wir führen dies auf Fehler bei der Extraktion und Generierung der Eingaben für den Z3-Solver zurück, da das Modell mit 1B Parametern Schwierigkeiten hatte, Prämissen zuverlässig zuzuordnen.</p>
</li>
<li>
<p><strong>Llama 8B (Verbesserte Genauigkeit):</strong> Verbesserte die Genauigkeit (Accuracy) in beiden Dateneinstellungen erheblich, mit einem deutlichen Anstieg bei den englischen Daten.</p>
</li>
<li>
<p><strong>Total Content Effect (TCE):</strong> Interessanterweise erzielte Llama 1B unter den neuro-symbolischen Constraints einen besseren TCE als das 8B-Modell, obwohl es eine schlechtere Baseline bezüglich des TCE aufwies. Dies deutet darauf hin, dass die logischen Entscheidungen des kleineren Modells weniger von semantischer Plausibilität beeinflusst werden.</p>
</li>
</ul>
<h2 id="schlussfolgerung">Schlussfolgerung</h2>
<p>In unserem Projekt haben wir gezeigt, dass wesentliche Verbesserungen der logischen Inferenz von LLMs mithilfe verschiedener Techniken möglich sind. Für mich persönlich stach Activation Steering als sehr vielseitiges Werkzeug mit geringem Overhead hervor, um ein Modell zu manipulieren. Im Gegensatz zu früheren Experimenten im Bereich Representation Engineering (wie Anthropics Golden Gate Claude), zeigt unsere Arbeit, wie Activation Steering kognitive Schlussfolgerungsfähigkeiten direkt steuern kann. Dies ist eine wichtige Demonstration auf dem Weg zur Spezialisierung von Modellen. In Zukunft würde ich gerne die Erklärbarkeit von KI (AI Explainability) weiter erforschen und untersuchen, wie ein besseres Verständnis genutzt werden könnte, um Modelle sicherer und besser zu machen.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>See e.g. Lampinen et al., <a href="http://arxiv.org/abs/2207.07051">Language models, like
humans, show content effects on reasoning tasks</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>See e.g. Bertolazzi et al., <a href="https://doi.org/10.18653/v1/2024.emnlp-main.769">A Systematic Analysis of Large Language Models as Soft Reasoners: The Case of Syllogistic Inferences</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>See e.g. Valentino et al., <a href="https://doi.org/10.48550/arXiv.2505.12189">Mitigating Content Effects on Reasoning in Language Models through Fine-Grained Activation Steering</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>See e.g. Pan et al., <a href="https://aclanthology.org/2023.findings-emnlp.248/">Logic-Lm: Empowering Large Language Models with Symbolic Solvers for Faithful Logical Reasoning</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>See e.g. Ranaldi et al., <a href="https://aclanthology.org/2025.acl-long.843/">Improving Chain-of-Thought Reasoning via Quasi-Symbolic Abstractions</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>See e.g. Rimsky et al., <a href="https://doi.org/10.18653/v1/2024.acl-long.828">Steering llama 2 via contrastive activation addition</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Übertragbarkeit von Benchmarkergebnissen auf reale wissenschaftliche Anwendungen</title><link>https://blog.chriscomputing.de/de/blog/wie-gut-sind-benchmarks/</link><pubDate>Sat, 04 Oct 2025 00:00:00 +0000</pubDate><guid>https://blog.chriscomputing.de/de/blog/wie-gut-sind-benchmarks/</guid><description>&lt;div class="alert alert-info" role="region" aria-label="info"&gt;
 
 &lt;div class="alert-body"&gt;Diese Bachelorarbeit von meiner Universität als eine der besten des Jahrganges 2025 ausgezeichnet.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Vor kurzem habe ich meine Bachelorarbeit mit dem Titel „Übertragbarkeit von Benchmarkergebnissen auf reale wissenschaftliche Anwendungen” beendet. In diesem Beitrag möchte ich die wichtigsten Ergebnisse teilen. Ich hatte das Privileg, für meine Arbeit nicht nur auf die Ressourcen der Universität, sondern auch auf die meines Arbeitgebers zuzugreifen. Das brachte verschiedene Vorteile mit sich. Beispielsweise konnte ich Expertise von sowohl der Universität als auch meines Arbeitgebers nutzen. Außerdem hatte ich drei verschiedene HPC-Cluster zur Verfügung, auf denen ich meine Tests ausführen konnte. Zugriff auf 3 HPC-Cluster ist selten, insbesondere im Rahmen einer Bachelorarbeit. Daher bin ich den Beteiligten zu tiefem Dank verpflichtet. Jedoch hat das alles auch einen Nachteil: Ich kann nicht frei Daten direkt aus meiner Bachelorarbeit verwenden. Weder die Universität noch mein Arbeitgeber wären glücklich, wenn ich die genauen Fähigkeiten ihrer Cluster in die Welt posaunen würde. Zusammen mit meinem Wunsch, ein bisschen Anonymität zu bewahren, führt dies dazu, dass ich in diesem Blogpost nur die wichtigsten Lehren zusammenfassen und meine Bachelorarbeit paraphrasieren werde.&lt;/p&gt;</description><content:encoded><![CDATA[<div class="alert alert-info" role="region" aria-label="info">
  
  <div class="alert-body">Diese Bachelorarbeit von meiner Universität als eine der besten des Jahrganges 2025 ausgezeichnet.</div>
</div>
<p>Vor kurzem habe ich meine Bachelorarbeit mit dem Titel „Übertragbarkeit von Benchmarkergebnissen auf reale wissenschaftliche Anwendungen” beendet. In diesem Beitrag möchte ich die wichtigsten Ergebnisse teilen. Ich hatte das Privileg, für meine Arbeit nicht nur auf die Ressourcen der Universität, sondern auch auf die meines Arbeitgebers zuzugreifen. Das brachte verschiedene Vorteile mit sich. Beispielsweise konnte ich Expertise von sowohl der Universität als auch meines Arbeitgebers nutzen. Außerdem hatte ich drei verschiedene HPC-Cluster zur Verfügung, auf denen ich meine Tests ausführen konnte. Zugriff auf 3 HPC-Cluster ist selten, insbesondere im Rahmen einer Bachelorarbeit. Daher bin ich den Beteiligten zu tiefem Dank verpflichtet. Jedoch hat das alles auch einen Nachteil: Ich kann nicht frei Daten direkt aus meiner Bachelorarbeit verwenden. Weder die Universität noch mein Arbeitgeber wären glücklich, wenn ich die genauen Fähigkeiten ihrer Cluster in die Welt posaunen würde. Zusammen mit meinem Wunsch, ein bisschen Anonymität zu bewahren, führt dies dazu, dass ich in diesem Blogpost nur die wichtigsten Lehren zusammenfassen und meine Bachelorarbeit paraphrasieren werde.</p>
<h2 id="einleitung">Einleitung</h2>
<p>Benchmarks sind ein weit verbreitetes Werkzeug, um die Leistung von Computern einzuschätzen. Das trifft sowohl auf gebräuchliche Laptops als auch auf große HPC-Cluster zu. Eine genaue Einschätzung der Leistung ist beispielsweise relevant, wenn Hersteller neue Technologien erproben oder potentielle Käufer die Leistung ebendieser abschätzen wollen. Allerdings sind weit verbreitete Benchmarks nicht unumstritten. Insbesondere der <a href="https://www.netlib.org/benchmark/hpl/">HPL Benchmark</a>, welcher die Grundlage für die viel beachtete <a href="http://top500.org/">Top500</a> Liste der stärksten Supercomputer weltweit ist, wird häufig wegen seiner geringen Aussagekraft kritisiert. Der HPL Benchmark versucht mit allen Mitteln, möglichst viele <a href="https://de.wikipedia.org/wiki/Floating_Point_Operations_Per_Second">FLOP/s</a> zu erreichen. Dafür minimiert er die Kommunikation zwischen den Prozessen sowie die Anzahl der Speicherzugriffe, um die CPU so gut wie möglich auszulasten. Beides ist nicht repräsentativ für reale wissenschaftliche Anwendungen (im Nachfolgenden nur noch Anwendungen genannt). Das hat zur Folge, dass HPL deutlich höhere FLOP/s erreicht als Anwendungen und deutlich besser auf vielen Knoten skaliert.<br>
Um die Probleme mit HPL und ähnlichen Benchmarks auszugleichen, wurde eine Reihe von sogenannten anwendungsnahen Benchmarks entwickelt. Im Allgemeinen werden dafür Codes, wie sie in der Wissenschaft verwendet werden, in einen Benchmark verpackt. Damit sollen die Anforderungen realer Anwendungen angemessen dargestellt werden. Ein Beispiel für anwendungsnahe Benchmarks sind die <a href="https://www.nas.nasa.gov/software/npb.html">NAS Parallel Benchmarks</a>, kurz NPB, entwickelt von der NASA Advanced Supercomputing Division. Obwohl sie darauf ausgelegt sind, reale Anwendungen abzubilden, sind die Benchmarks sehr auf ihr spezielles Problem optimiert. Daher ist es fraglich, wie gut sie sich wirklich eignen, um die Rechenleistung von Clustern auf Anwendungen vorherzusagen.</p>
<h2 id="durchführung">Durchführung</h2>
<p>Die Tests wurden mit vier Benchmarks und zwei Anwendungen durchgeführt. Um das Scalingverhalten untersuchen zu können, wurden die Tests in vier Szenarien durchgeführt:</p>
<ol>
<li>Auf einem Knoten mit einer CPU, um nur Kommunikation auf einer CPU zu haben.</li>
<li>Auf einem Knoten mit beiden CPUs, um außerdem Kommunikation auf dem CPU-Interconnect zu haben,</li>
<li>Auf vier Knoten mit insgesamt acht CPUs (2 pro Knoten), um das Knoten-Interconnect außerdem zu verwenden.</li>
<li>Auf 64 Knoten mit 128 CPUs, um das Verhalten in massiv parallelen Umgebungen zu testen, in denen Kommunikation und Parallelizierbarkeit eine sehr wichtige Rolle spielen.</li>
</ol>
<p>Die Ergebnisse der vier Benchmarks wurden verwendet, um Vorhersagen für die Rechenleistung auf den beiden Anwendungen zu machen. Außerdem offenbarten die Benchmarkergebnisse selbst interessante Details über die verwendete Hardware.</p>
<h2 id="benchmarks-und-anwendungen">Benchmarks und Anwendungen</h2>
<p>Es wurden drei synthetische Benchmarks verwendet: <a href="https://www.netlib.org/benchmark/hpl/">HPL</a>, <a href="https://www.cs.virginia.edu/stream/ref.html">STREAM</a> und vier Benchmarks aus der <a href="https://mvapich.cse.ohio-state.edu/benchmarks/">OSU-Microbenchmark Suite</a>: allreduce, alltoall, bibw und latency. Allreduce und Alltoall sind <a href="https://www.mpi-forum.org/">MPI</a> Routinen. Die Benchmarks messen die benötigte Zeit, um sie auszuführen. Bibw und Latency nutzen MPI-Routinen, um die bidirektionale Bandbreite beziehungsweise die Latenz zwischen zwei Knoten zu messen. STREAM ist ein Benchmark, welcher die Bandbreite des RAMs testet. Auf modernen Clustern ist der entscheidende Faktor für die Laufzeit eines Programms nicht mehr die Leistung der CPU, sondern wie schnell Daten zu der CPU gelangen können. Caches sind zwar eine sehr wichtige Technologie, aber letztendlich passen die wenigsten Probleme in den Cache einer Anwendung. Daher ist die Bandbreite des RAMs von zunehmender Bedeutung. Der vierte verwendete Benchmark ist die <a href="https://www.nas.nasa.gov/software/npb.html">NPB Suite</a>. Wie auch bei den OSU-Microbenchmarks wurden mehrere Benchmarks aus der Sammlung verwendet, und zwar die Conjugate Gradient (CG), Multigrid (MG) und Fourier Transformation (FT) Benchmarks. Conjugate Gradient und Multigrid sind beides Methoden zur Lösung linearer Gleichungssysteme, wobei Multigrid-Methoden häufig eine Conjugate-Gradient Methode anwenden.<br>
Für die Anwendungen wurde NWChem ausgewählt, ein OpenSource Chemie-Projekt. Die andere Anwendung ist intern von meinem Arbeitgeber, sodass ich hier kein weiteres Wort über sie verlieren kann. Mit NWChem wurde eine DFT-Analyse des C28 Moleküls durchgeführt. DFT berechnet die Verteilung der Elektronen in einem Molekül. C28 ist das Molekül, welches entsteht, wenn 28 Kohlenstoffatome eine Bindung eingehen. Eine Einschränkung von NWChem ist, dass die DFT Analyse mithilfe von Gaußschen Basissätzen berechnet wird, welche schlechter parallelisierbar sind als andere.</p>
<h2 id="ergebnisse">Ergebnisse</h2>
<p>Die Tests haben gezeigt, dass die Rechenleistung in Anwendungen nur eingeschränkt durch Benchmarkergebnisse vorherzusagen ist. Insbesondere reicht ein Benchmark alleine nicht aus, es müssen mehrere verwendet werden. Welche Kombination der Benchmarks die aussagekräftigsten Ergebnisse erzielt, hängt von der Anwendung ab, deren Rechenleistung vorhergesagt werden soll. Für CPU-intensive Anwendungen ist das Ergebnis in STREAM beispielsweise weniger relevant als für Anwendungen, welche große Mengen Daten verarbeiten müssen. Außerdem zeigte sich, dass insbesondere der HPL-Benchmark, trotz seiner immensen Verbreitung, wenig geeignet ist, um die Rechenleistung von Clustern unter realen Bedingungen abzuschätzen. HPL erreicht um ein Vielfaches höhere FLOP/s und skaliert insbesondere auf vielen Knoten viel besser als Anwendungen. Außerdem spiegeln die relativen Abstände zwischen Clustern bei HPL nicht unbedingt die relativen Abstände bei realen Anwendungen wieder, die Abstände bei HPL sind mitunter deutlich geringer. Dies ist darauf zurückzuführen, dass der meiste technologische Fortschritt bei CPUs nicht in der rohen Rechenleistung ihrer Kerne, sondern in der Größe und Geschwindigkeit des Caches liegt, welche von HPL kaum in Betracht gezogen werden.<br>
Des Weiteren zeigen die Tests, dass selbst die anwendungsnahen Benchmarks der NBP Suite deutlich besser optimiert sind als die zur Verfügung stehenden Anwendungen. Auf einem Cluster konnte auf 8 CPUs sogar ein 22-facher <a href="https://hpc-wiki.info/hpc/Scaling">Speedup</a> erzielt werden. Die Anwendungen waren weit davon entfernt, wobei unter gewissen Umständen auch bei ihnen ein kleiner, superlinearer Speedup beobachtet werden konnte. Somit ist klar, dass Benchmarkergebnisse nur eingeschränkt übertragbar auf reale wissenschaftliche Anwendungen sind.</p>
]]></content:encoded></item><item><title>Fibonacci in einer Sekunde</title><link>https://blog.chriscomputing.de/de/blog/fibonacci_in_1_sekunde/</link><pubDate>Fri, 28 Feb 2025 00:00:00 +0000</pubDate><guid>https://blog.chriscomputing.de/de/blog/fibonacci_in_1_sekunde/</guid><description>&lt;p&gt;Vor einiger Zeit schlug YouTube mir ein &lt;a href="https://youtu.be/LXm6ygZ3h7A?si=2RG1Io6w2f44w2tR"&gt;Video&lt;/a&gt; vor, indem der Creator &lt;a href="https://www.youtube.com/@SheafificationOfG"&gt;Sheafification of G&lt;/a&gt; versuchte, die größtmögliche Fibonacci Zahl innerhalb einer Sekunde zu berechnen. Das Video ist Teil einer Serie, in der verschiedene Ansätze und verschiedene Programmiersprachen behandelt wurden. Ich empfehle sehr, die unterhaltenden und spannenden Videos zu schauen. Davon inspiriert beschloss ich, das ebenfalls zu versuchen, allerdings in Java. Nicht weil Java so eine weit verbreitete Sprache für wissenschaftliches Rechnen ist, sondern einfach weil ich länger kein Java mehr geschrieben habe. Meine Implementation befindet sich auf &lt;a href="https://github.com/chriscomputing/fib-java"&gt;Github&lt;/a&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Vor einiger Zeit schlug YouTube mir ein <a href="https://youtu.be/LXm6ygZ3h7A?si=2RG1Io6w2f44w2tR">Video</a> vor, indem der Creator <a href="https://www.youtube.com/@SheafificationOfG">Sheafification of G</a> versuchte, die größtmögliche Fibonacci Zahl innerhalb einer Sekunde zu berechnen. Das Video ist Teil einer Serie, in der verschiedene Ansätze und verschiedene Programmiersprachen behandelt wurden. Ich empfehle sehr, die unterhaltenden und spannenden Videos zu schauen. Davon inspiriert beschloss ich, das ebenfalls zu versuchen, allerdings in Java. Nicht weil Java so eine weit verbreitete Sprache für wissenschaftliches Rechnen ist, sondern einfach weil ich länger kein Java mehr geschrieben habe. Meine Implementation befindet sich auf <a href="https://github.com/chriscomputing/fib-java">Github</a>.</p>
<h2 id="der-naive-ansatz">Der naive Ansatz</h2>
<p>Die Fibonacci Reihe ist definiert als $fib(n) = fib(n-1) + fib(n-2)$, und $fib(0)= 0, fib(1) = 1$. Daher ist es naheliegend, Fibonacci rekursiv zu implementieren. Daraus ergibt sich der Pseudocode:</p>





<pre tabindex="0"><code>fib(n):
  if n == 0:
    return 0
  if n == 1:
    return 1
  return fib(n-2) + fib(n-1)</code></pre><p>Es ist einfach zu sehen, dass dieser Algorithmus nicht effizient ist. Für jede Berechnung müssen alle Fibonacci Zahlen von 0 aus ermittelt werden, sodass dieser Algorithmus eine Laufzeit von $O(exp(n))$ hat.</p>
<h2 id="dynamische-programmierung">Dynamische Programmierung</h2>
<p>Die offensichtliche Verbesserung der rekursiven Variante ist, $fib(n-2)$ und $fib(n-1)$ zu speichern um sich viele Rechenschritte sparen zu können. Das ist ein Trick, den viele intuitiv anwenden, wenn sie eine Fibonacci Zahl von Hand errechnen sollen. Im allgemeinen wird die Technik, Ergebnisse vorheriger Schritte wieder zu verwenden, dynamische Programmierung genannt.</p>





<pre tabindex="0"><code>fib(n):
  a = 0
  b = 1
  for i from 1 to n:
    tmp = a + b
    b = a
    a = tmp
  return a</code></pre><p>Man könnte meinen, dieser Algorithmus würde Fibonacci in $O(n)$ berechnen, da die Addition in einem einfachen For-Loop stattfindet. Das ignoriert aber, dass die Operation der Addition auf der Bitebene ebenfalls lineare Zeit in Anspruch nimmt. Daher hat dieser Algorithmus (auf Computer angewandt) die Laufzeit $O(n²)$.</p>
<h2 id="fast-squaring">Fast Squaring</h2>
<p>Der vorherige Ansatz bietet bereits eine bedeutende Verbesserung, und erschöpft das Verbesserungspotential asympthotischer Laufzeiten in diesem Beitrag. Das bedeutet aber nicht, dass wir kein besseres Ergebnis mehr erzielen können. Zum Glück gilt diese Aussage:</p>
$$
\begin{bmatrix}
    0 & 1 \\ 1 & 1
\end{bmatrix}^n =
\begin{bmatrix}
    F_{n-1} & F_n \\ F_n & F_{n+1}
\end{bmatrix}
$$<p>Wenn wir also die Matrix $\begin{bmatrix}0 & 1 \\ 1 & 1\end{bmatrix}$ mit $n$ potenzieren, erhalten wir oben rechts und unten links die n-te Fibonacci Zahl. Ein Beweis für diese Aussage findet sich <a href="https://usaco.guide/plat/matrix-expo?lang=cpp">hier</a>. Insbesondere gepaart mit <a href="https://en.wikipedia.org/wiki/Exponentiation_by_squaring">Exponentiation by Squaring</a> ist es möglich, Fibonacci noch schneller zu berechnen.</p>





<pre tabindex="0"><code>fib(n):
  M = {{0,1}, {1,1}}
  M = exp_by_squaring(M, n)
  return M[0][1]</code></pre><p>Exponentiation by Squaring hat den Vorteil, dass die Anzahl der benötigten Rechenoperationen von $O(n)$ im dynamischen Algorithmus auf $O(log(n))$ reduziert wird. Da für die letzte Multiplikation dennoch $O(n^2)$ Bitopertionen benötigt werden, bleibt die asympthotische Laufzeit (für Computer) dennoch gleich.</p>
<h2 id="umsetzung-in-java">Umsetzung in Java</h2>
<p>In bester OOP Tradition habe ich ein BaseScheduler und BaseCalculator Interface geschrieben, sodass die konkreten Implementationen problemlos ausgetauscht werden können. Scheduler haben die Aufgabe, in möglichst wenig Schritten ein $n$ zu finden, welches das Programm innerhalb eines Threshholds von 0,002 Sekunden an eine Laufzeit von eine Sekunde bringt. Das ist nötig, weil ich nicht tagelang warten möchte, bis mein Laptop jede Zahl von $1$ bis $n$ ausprobiert hat. Calculator implementieren die bereits vorgestellten Algorithmen, um Fibonacci zu berechnen.</p>
<h3 id="die-scheduler">Die Scheduler</h3>
<p>Ich habe 3 Scheduler implementiert. Der Erste, genannt &ldquo;Graph Scheduler&rdquo; inkrementiert n um eine vorgegebene STEP_SIZE, bis die 1-Sekunden-Marke erreicht ist. Der Zweite, genannt &ldquo;Binary Scheduler&rdquo;, führt eine Art binäre Suche aus, um möglichst schnell das korrekte n zu finden. Das funktioniert schon überraschend gut, aber kann noch verbessert werden. Dafür ist der &ldquo;Ratio Scheduler&rdquo; da. Dies ist sein Code:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">RatioScheduler</span><span class="w"> </span><span class="kd">implements</span><span class="w"> </span><span class="n">BaseScheduler</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="kt">double</span><span class="w"> </span><span class="n">THRESHOLD</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="nf">RatioScheduler</span><span class="p">(</span><span class="kt">double</span><span class="w"> </span><span class="n">threshold</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">        </span><span class="k">this</span><span class="p">.</span><span class="na">THRESHOLD</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">threshold</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kt">long</span><span class="w"> </span><span class="nf">schedule</span><span class="p">(</span><span class="kt">double</span><span class="w"> </span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="kt">long</span><span class="w"> </span><span class="n">previous</span><span class="p">,</span><span class="w"> </span><span class="kt">double</span><span class="w"> </span><span class="n">target</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">THRESHOLD</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">Math</span><span class="p">.</span><span class="na">abs</span><span class="p">(</span><span class="n">time</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">target</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">            </span><span class="k">return</span><span class="w"> </span><span class="o">-</span><span class="n">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">        </span><span class="kt">double</span><span class="w"> </span><span class="n">dampener</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">1</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">        </span><span class="kt">double</span><span class="w"> </span><span class="n">factor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Math</span><span class="p">.</span><span class="na">max</span><span class="p">(</span><span class="n">Math</span><span class="p">.</span><span class="na">min</span><span class="p">(</span><span class="n">target</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">2</span><span class="p">.</span><span class="na">5</span><span class="p">),</span><span class="w"> </span><span class="n">0</span><span class="p">.</span><span class="na">5</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">factor</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">            </span><span class="n">dampener</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">1</span><span class="p">.</span><span class="na">02</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">            </span><span class="n">dampener</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">0</span><span class="p">.</span><span class="na">98</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="p">(</span><span class="kt">long</span><span class="p">)</span><span class="w"> </span><span class="n">Math</span><span class="p">.</span><span class="na">ceil</span><span class="p">(</span><span class="n">factor</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">previous</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">dampener</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>Ratio Scheduler berechnet das Verhältnis zwischen gemessener Zeit und Zielzeit und leitet daraus ein Verhältnis, mit dem n multipliziert werden müsste ab. Außerdem bezieht Ratio Scheduler dabei einen Dämpfer mit ein, da es ansonsten für Laufzeiten nahe an einer Sekunde zu zu starken Oszillationen kam.</p>
<h3 id="die-calculator">Die Calculator</h3>
<p>Der Code für die Calculator ist entweder zu langweilig oder zu lang, und die Algorithmen bereits diskutiert. Daher verweise ich an dieser Stelle nur erneut auf das <a href="https://github.com/chriscomputing/fib-java">Github Repository</a>.<br>
Für die Implementation musste ich BigInteger verwenden, da Javas größter Datentyp, der long, bereits nach der 93sten Fibonacci Zahl überläuft. Ich habe zuerst den rekursiven und den dynamischen Algorithmus implementiert, bevor ich mich Fast Squaring zugewandt habe. Für Letzteres habe ich die <a href="https://commons.apache.org/proper/commons-math/">Apache Commons Math Library</a> verwendet, welche zahlreiche Methoden für Matrizen bietet. Leider können die Matrizen der Library nicht mit BigIntegern umgehen, sodass ich für diese Implementation BigFraction aus der Commons Math Library verwenden musste, da der Rest meines Codes BigInteger erwartet. Daher beinhaltet der Code für Fast Squaring auch eine eher unschöne Umwandlug in BigInteger.</p>
<h2 id="ergebnisse">Ergebnisse</h2>
<p>Damit zum spannenden Teil. Wie schlägt sich Java im Vergleich zu C? Für C verwende ich die <a href="https://github.com/SheafificationOfG/Fibsonisheaf">Implementationen von Sheafification of G</a>. Die folgenden Werte wurden ermittelt, indem ich mein Testprogramm auf meinem Laptop mit den jeweils verschiedenen Calculatorn ausgewählt ausgeführt habe. Das daraus gewonnene n habe ich dann einzeln erneut berechnen lassen und die dabei ermittelte Laufzeit für Java und C festgehalten. Dazu später mehr. Hier sind die Ergebnisse:</p>
<table>
	<thead>
			<tr>
					<th>Algorithmus</th>
					<th>n</th>
					<th>Java</th>
					<th>C</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Rekursiv</td>
					<td>39</td>
					<td>1.096616593s</td>
					<td>0.161663483s</td>
			</tr>
			<tr>
					<td>Dynamisch</td>
					<td>302.463</td>
					<td>1.630596675s</td>
					<td>0.507222650s</td>
			</tr>
			<tr>
					<td>Fast Squaring</td>
					<td>2.743.397</td>
					<td>1.816605545s</td>
					<td>1.511263398s</td>
			</tr>
	</tbody>
</table>
<p>Es ist überraschend, wie groß der Unterschied zwischen dem dynamischen Algorithmus und Fast Squaring ist, obwohl beide Asympthotisch die gleiche Laufzeit haben. Das zeigt erneut, dass Laufzeitklassen lange nicht alles über einen Algorithmus aussagen. Weniger überraschend ist, dass C durchweg besser abschneidet als Java. Auffällig ist, dass keine Zeit für Java unter einer Sekunde ist. Wie kommt das? Wie oben bereits erwähnt, wurde $n$ mit Hilfe eines Schedulers in einer Serie von vielen (ca. 20 - 30) Fibonacci Berechnungen ermittelt. In dieser Serie hat mein Laptop $n$ tatsächlich innerhalb einer Sekunde berechnen können. Die hier aufgeführten Zeiten für Java und C wurden ermittelt, indem die Berechnung ein mal mit dem gegebenen $n$ ausgeführt wurde. Den Zeitunterschied erkläre ich vor allem durch predictive branching der CPU. Cache könnte auch eine Rolle gespielt haben. Ausgiebig recherchiert habe ich zu dem Phänomen allerdings nicht.</p>
<h2 id="verbesserungsmöglichkeiten">Verbesserungsmöglichkeiten</h2>
<p>Natürlich gibt es sowohl im Versuchsaufbau, als auch in der Implementation der Algorithmen Verbesserungsmöglichkeiten. Zum Beispiel habe ich die Werte durch einmaliges Ausführen auf meinem Laptop ermittelt, ein hochgradig unwissenschaftliches vorgehen. Es ist auch nicht ideal, dass zwei Implementationen BigInteger und eine BigFraction verwendet. Ich habe nicht getestet ob Apaches BigFraction Implementation effizienter addiert als BigInteger, trotzdem wäre es sicherlich präziser, einen einheitlichen Datentyp zu verwenden. Streng genommen sind weder BigInteger noch BigFraction optimale Datentypen. Beide sind immutable, sodass für jede Berechnung neuer Speicherplatz für das Ergebnis allokiert werden muss. Das ist ein signifikanter Overhead, ohne den mit Sicherheit größere $n$ möglich wären. Da es möglich ist, für ein gegebenes $n$ zu berechnen, wie viel Speicherplatz $fib(n)$ maximal benötigen würde, wäre es am besten, zu Beginn des Algorithmus einmal die nötigen Variablen zu allokieren und dann in-place zu modifizieren. Ich habe Versuche genommen, das umzusetzen. Allerdings ist Java nicht für Manipulationen auf Byte-Level gemacht (zum Beispiel sind Bytes in Java signed, weil sie trotz ihres Namens als kleine Dezimalzahlen betrachtet werden), weswegen ich die Idee aufgrund von Zeitdruck durch Klausuren verworfen habe.</p>
]]></content:encoded></item></channel></rss>