Stranger Than Usual

Positivprojekt 02: Astronomie

Wie schon angedroht kann nicht jeder Eintrag im Positivprojekt so groß sein wie der über Webcomics. Jetzt wird es also kürzer.

Ich bin ja allgemein an Naturwissenschaften interessiert, und Astronomie gehört definitiv dazu. Außerdem ist es ganz schön, gedanklich mal von der Erde wegzukommen.

Schon der große Philosoph Randall Munroe wusste Astronomie zu nutzen (CC BY NC 2.5 Randall Munroe)

xkcd: Making Friends

Wenn der Mond ein Pixel groß wäre

Space is big. Really big. You just won't believe how vastly, mind-bogglingly huge it is. I mean, you might think it's a long way down the road to the chemist, but that's peanuts to space.

— The Hitchhiker's Guide to the Galaxy

Man macht sich ja in der Regel keine Vorstellungen über die Größe des Universums. Oder auch nur eines sehr kleinen Teils davon, wie dem Sonnensystem. In üblichen Abbildungen der Planeten sieht man sie häufig nebeneinander aufgereiht, wobei die jeweiligen Größen der Planeten zwar maßstabsgerecht wiedergegeben werden, die Abstände zwischen ihnen aber gnadenlos gekürzt werden.

Kein Wunder, denn wenn man die Abstände korrekt darstellen würde, käme etwas dabei raus wie diese Seite. Grundidee: Stelle das Sonnensystem maßstabsgerecht auf einer Website dar, wenn der Mond ein Pixel groß ist. Man muss lange scrollen, bis man durch ist. Es braucht schon eine ganze Weile von der Sonne bis zum Mars, und dann ist es noch drei Mal so weit, um zum Jupiter zu kommen.

Um es mit den Worten des Autors der Seite zu sagen:

Most space charts leave out the most significant part – all the space.

Astrodicticum simplex

Ein Blog über Astronomie und Wissenschaft von Florian Freistetter. Dort werden interessante Themen zur Astronomie behandelt, gelegentlich irreführende Berichterstattung in der Presse kritisiert und Transkripte zu Florians Podcast „Sternengeschichten“ veröffentlicht. Eine gute Quelle, um aktuelle Themen der Astronomie für Laien erklärt zu bekommen. Auf jeden Fall Lesenswert.

Update 2022-03-02

Da Scienceblogs mit der Einstellung droht, hat Florian Freistetter das Blog auf eine neue Adresse umgezogen. Ich habe die URL hier angepasst.

How to survive in spacecraft

Auf der remote chaos experience gab es einen schönen Vortrag darüber, wie Lebenserhaltungssysteme im Weltraum funktionieren. Dabei werden mehrere Methoden vorgestellt, welche Vor- und Nachteile sie haben, in welchen Fällen sie eingesetzt werden und wie man sie kombinieren kann. Sehr interessant. Ich hatte mich vorher nie näher mit dem Thema beschäftigt, hatte aber immer das Gefühl, dass mehr an der Sache dran sein muss als „wir nehmen jede Menge Sauerstoffflaschen mit“.

Überhaupt gab es in den letzten Jahren auf dem Congress immer wieder interessante Themen zu Astronomie und Raumfahrt, so zum Beispiel dieser Vortrag darüber, was man mit Raumschiffen, Satelliten und Sonden eigentlich macht, wenn der Start erfolgreich war, also zum Beispiel wie man sie in den richtigen Orbit kriegt.

Auf dem 33c3 gabe es eine ganze Reihe von Vorträgen mit Leuten von der ESA, zum Beispiel diesen Vortrag hier. Überhaupt findet man immer wieder Vorträge zu Astronomie und Raumfahrt auf dem Kongress. Die meisten sind wirklich interessant.

Stuff in space

Eine 3D-Karte über menschgemachte Objekte im Erdorbit, mit ihrer aktuellen Position in Echtzeit. Da oben wird es langsam echt voll. Auch hier sind sicher eine Menge Arbeit und Daten reingeflossen, aber das Ergebnis kann sich sehen lassen.

Honorable Mentions

Methodisch Inkorrekt hat im Jahresrückblick 2020 gefühlt überdurchschnittlich viele astronomische Themen auf dem Tablett gehabt. Die beiden sind Physiker, haben zwar selber nichts mit Astronomie gemacht, sind aber Astronomie- und Raumfahrtfans (besonders Nicolas). Auch sonst kommen bei Methodisch Inkorrekt immer wieder astronomische Themen vor.

Dort wurde auch immer wieder der Podcast Auf Distanz erwähnt, wobei ich hier bisher nie dazu gekommen bin, ihn anzuhören. Deswegen kann ich ihn auch nicht guten Gewissens empfehlen, auch wenn er gut sein soll.

Positivprojekt 03: Coole Projekte von Kollegen

Diese Woche möchte ich zwei kleine Projekte von Arbeitskollegen vorstellen.

Katzenlichtschalter

Ein Arbeitskollege von mir hat einen katzenbetriebenen Lichtschalter gebaut. Einen lasergesteuerten katzenbetriebenen Lichtschalter. Schaut euch das Video an.

Green walking

Ein anderer Arbeitskollege hat im letzten Sommer eine App namens Green Walking gebaut (Quellcode).

Diese App benutzt frei zugängliche Daten von Wikidata und OpenStreetMap um grüne Ecken in einer Stadt zu finden, wo man Spaziergänge oder ähnliches machen kann.

Die App ist mit Flutter geschrieben, was es theoretisch einfach man, sie für Android uns iOS zu bauen (wobei der letzte Stand, den ich kenne, ist, dass der Kollege kein iPhone zum Testen hat und er die App bisher nur für Android gebaut hat).

Ich finde, diese App ist ein schönes Beispiel, was man mit freien Informationen cooles machen kann.

Positivprojekt 04: Katzen

Wer keine Katzen mag, sollte diesen Blogpost überspringen. Ich bin jetzt in Woche 4 meines Positivprojektes und mir gehen schon fast die Themen aus.

Das Internet ist für Katzen da. Es mag Leute geben, die mit Katzen nichts anfangen können, ich gehöre nicht dazu. Auch eine der beliebtesten Personen der Scheibenwelt weiß Katzen zu schätzen:

KATZEN. JA, KATZEN SIND RECHT NETT

Auch Toady One, der Entwickler von Dwarf Fortress, hat Katzen in seinem Spiel eine besondere Stellung gewährt: Im Gegensatz zu anderen Haustieren adoptieren Katzen Zwerge, nicht umgekehrt. Da Zwerge um ihre Haustiere besorgt sind (man sie also zum Beispiel nicht schlachten kann und aufpassen sollte, dass sie nicht ums Leben kommen, wenn man keine amoklaufenden Zwerge haben möchte), gibt es dort auch das Phänomen der catsplosion, also der explosionsartig steigenden Katzenpopulation.

Heute geht es aber eigentlich um die Cagle Cats, der Twitterkanal der Katzen der Webcomicautorin Mary Cagle (u.a. Autorin von Sleepless Domain, über die ich früher schon einmal berichtet habe.

Bei den Katzen gibt es gerade Nachwuchs. Ich weiß, nicht jeder kann mit Katzen etwas anfangen, aber wer es kann, der sollte sich das auf jeden Fall anschauen.

Keine Ersatzteile

Ärgerlich. Mein gut vier Jahre alter Laptop hat einige Probleme. Einige Tasten funktionieren nicht mehr, und die Bildschirmscharniere sind komplett ausgebrochen.

So ist der Laptop nicht mehr wirklich benutzbar (ich benutze gerade behelfsmäßig einen Pappkarton für die Stabilität). Transportierbar ist er auch nicht. Tippen ist schwierig, weil einige Tastenkürzel nicht mehr gehen.

Der Laptop im Karton ist mit Fäden fixiert, um das Umkippen des Displays zu verhindern.

Der Laptop muss also repariert werden. Es ist ein Tuxedo Laptop, also habe ich zuerst beim Hersteller angefragt. Zwei Mal, per Mail, im Januar, bisher keine Antwort. Als gestern Abend das zweite Scharnier ausgebrochen ist, bin ich heute zu einem ortsansässigen Reparaturunternehmen gefahren.

Die positiven Seiten: Ich habe mal wieder ein bisschen Bewegung gekriegt, und der Reparaturladen hat sich wirklich Mühe mit der Ansteckungsvermeidung gegeben.

Die negative Seite: Es gibt keine Ersatzteile mehr für dieses Gerät.

Das ist extrem ärgerlich. Denn im Gegensatz zu meinem Vorgängerlaptop, der zu dem Zeitpunkt, als die Festplatte den Geist aufgab, schon nicht mehr meinen Leistungsanforderungen entsprach, ist mein aktueller Laptop immer noch topfit. Also quasi das Gegenstück zu einem Sechzigjährigen, der nach einem Unfall nicht mehr laufen kann, aber geistig noch zu Höchstleistungen fähig ist.

Aber als Laptop benutzen kann ich ihn nicht mehr. Und deswegen muss ich mir jetzt einen neuen kaufen. Vielleicht finde ich ja jemanden, der mit dieser Kiste noch etwas anfangen kann.

Positivprojekt 05: Bilder aus Arda

Eine Freundin hat mir letztens einen Link zu der Seite einer Illustratorin namens Jenny Dolfen geschickt. Die malt unter anderem wunderbar stimmungsvolle Bilder über Tolkiens Werke, die meiner Meinung nach vom Stil her super zum Setting passen.

Sie hat auch noch einige andere Bilder, u.a zu Harry Potter, Star Wars, anderen Franchises und historischen Settings. Auf jeden Fall sehenswert!

Positivprojekt 06: PBS Eons

Mir gehen langsam die positiven Sachen aus, die ich hier posten könnte. Bisher waren es auch eigentlich immer irgendwelche Sachen, die man im Netz finden kann. Vermutlich, weil man die am besten verlinken kann.

Dieses Mal möchte ich den Youtube-Kanal PBS Eons vorstellen. Wie der Name schon andeutet, werden dort paläontologische Themen behandelt. Die Videos sind in der Regel um die zehn Minuten lang und behandeln ein Thema, von speziellen Themen wie einer Insel, auf denen sich Tiere ungewöhnlich groß entwickelten (was wohl regelmäßig vorkommt, ebenso wie der umgekehrte Fall) bis hin zu sehr großen Themen wie einer kurzen Geschichte der geologischen Zeit.

Die geologischen, klimatischen, evolutionären, usw. Mechanismen sind anschaulich erklärt, und es gibt einen Riesenhaufen interessanter Dinge, die zum Beispiel ich noch nicht über Paläontologie wusste. Als ich den Channel kennengelernt habe, war mein Wunschthema die Zeit, in der die Antarktis ganzjährig eisfrei war. Irgendwann kam dann tatsächlich ein Video darüber. Diese unvorstellbaren Zeiträume, in denen sich die Welt so vollkommen verändern kann, und die Vergangenheit, in denen die Welt so anders (und doch in vielerlei Hinsicht so gleich) war, finde ich einfach faszinierend.

Gefunden habe ich den Channel übrigens über Bruce Schneiers „Friday Squid Blogging“, mit dem Video „How the Squid Lost Its Shell“.

Positivprojekt 07: Marshubschrauber

In letzter Zeit gab es wieder einige bisher erfolgreiche Marsmissionen. Warum häuft sich das gerade? Meine Vermutung ist, dass es wohl gerade irgendwie einfacher sein muss, zum Mars zu kommen. Würde passen, weil etwa alle zwei Jahre der Abstand zwischen Mars und Erde besonders klein ist. Laut Wikipedia ist der letzte Batzen Marsmissionen 2018 gestartet (der aktuelle 2020), davor 2016, davor 2013. Käme also hin.

Naja, jedenfalls haben es sowohl die Vereinigten Arabischen Emirate als auch China diesen Monat geschafft, einen Orbiter in die Marsumlaufbahn zu bringen (China hat außerdem einen Lander mit Rover, der wohl noch nicht gelandet ist).

Die NASA hat gestern jedenfalls eine Erfolgreiche Landung auf dem Mars hingelegt. Und das Beste: Mit an Bord ist ein Hubschrauber!.

Der hat jetzt noch nicht viele Messinstrumente an Bord, sondern nur das Nötigste, es ist mehr so ein Proof Of Concept, wenn ich das richtig verstehe. Trotzdem: Wir können uns bald auf Luftaufnahmen vom Mars freuen, nicht nur die Bilder von Orbitern und Rovern im Landeanflug.

Positivprojekt 08: Paddeln wieder erlaubt

Gute Neuigkeiten: Seit letztem Montag darf man in Essen wieder paddeln. In letzter Zeit war das aufgrund der Pandemie-Einschränkungen nicht erlaubt.

Paddeln eignet sich als Sport recht gut dazu, unter Pandemiebedingungen betrieben zu werden: Man ist an der frischen Luft und das Abstandhalten ist auch kein Problem.

Natürlich gelten die üblichen Regeln, Abstandhalten, im Bootshaus Maske tragen etc. Keine Frage. Aber paddeln an sich ist wieder erlaubt.

Wichtig ist nur zu sagen: Obwohl es die Tage schon wieder ziemlich warm war, ist das Wasser immer noch verdammt kalt. Wenn man da ohne Schutzkleidung reinfällt, kann das tödlich sein. Also: nicht alleine paddeln, immer eine Schwimmweste und mindestens Neopren tragen.

Positivprojekt 09: Die Sendung mit der Maus wird 50

Die Lieblingsfernsehsendung meiner Kindheit, die Sendung mit der Maus wird heute, am 7.3.2021 fünfzig Jahre alt.

Obwohl ich die Sendung mittlerweile nur noch sporadisch verfolge und viele Gesichter dort nicht mehr kenne, so ist die Sendung mit der Maus doch immer noch etwas, auf das man zählen kann, um komplizierte Sachen einfach zu erklären. Für Kinder, natürlich, aber auch für Erwachsene zu empfehlen.

Die Sendung mit der Maus hat es auch nach tvtropes geschafft. Käpt'n Blaubär war der Held meiner Kindheit (mein Vater hat sich den größten Teil der Sendung nicht angeschaut, aber für Käpt'n Blaubär mussten wir ihn immer rufen. Von Fragen wie das Salz ins Meer kommt über die Herstellung von Büroklammern bis hin zu Radioaktivität und wie Atomkraftwerke funktionieren: Die Sendung hat immer eine Antwort.

Für fünfundzwanzig Jahren habe ich als Kind den 25. Mausgeburtstag gefeiert. Und heute habe ich mir die 50. Jubiläumssendung angeschaut. Natürlich kommt vorher das Intro mit Inhaltangabe immer zwei Mal, einmal auf Deutsch und einmal auf… welche Sprache ist es dieses Mal?

Das war: Klingonisch

Ansonsten war die Sendung ein Blick in die Zukunft. Hauptsächlich Sachgeschichten dieses Mal, mit ein bisschen Rahmenstory um den Mausgeburtstag. Einiges an Spekulationen und Wünschen für die Zukunft, aber auch einiges an zukunftsweisenden Ideen, wie zum Beispiel Anbau und Nutzen von Algen oder Urban Mining.

Die Sendungen müssen schon vor einiger Zeit gedreht worden sein, denn Masken werden keine getragen, auch nicht bei einer Geschichte, die in einer Schule spielt. Dort nimmt ein Mädchen, das aus krankheitsgründen zu Hause bleiben muss, mit einem Roboter am Unterricht Teil. Die Klassenkameraden sitzen im echten Klassenraum, am Platz des Mädchens sitzt ein kleiner Roboter mit Kamera, Mikrofon, Lautsprecher, Wortmeldefunktion und (eingeschränkter) Bewegungsfähigkeit. Ich wusste nicht, dass so etwas schon eingesetzt wird.

Wie auch immer, ich bin froh, dass es die Sendung mit der Maus noch gibt.

Positivprojekt 10: astronomical picture of the day

Ich bin ja ein Fan davon, wenn man öffentliche Daten über öffentliche, maschinenlesbare Schnittstellen herunterladen kann. So zum bietet zum Beispiel die NASA einige APIs an. Insbesondere möchte ich heute das astronomical picture of the day vorstellen.

Da wird jeden Tag ein astronomisches Bild online gestellt. Mal ist es ein Bild einer Galaxie. Mal das eines Kometen. Mal der Aurora. Und manchmal ist es eine Rundumsicht des Perseverance Marsrovers.

Alles Bilder, die sich wunderbar als Hintergrundbild machen würden. Nur ist es ja langweilig, wenn man immer dasselbe Bild hat, und nervig, es jeden Tag selber ändern zu müssen.

API to the rescue

Glücklicherweise kann man sich das ja automatisch herunterladen lassen. Da die URLs der Bilder nicht wirklich erratbar sind, besorgt man sich zuerst die URLs der HD-Bilder und lädt diese dann herunter. Dann benutzt man ein kleines Script, um die Datei als Hintergrundbild zu setzen.

Non könnte man das Herunterladen einfach per shell-Script machen. Wäre vermutlich das einfachste. Doch ich mache das in meiner Freizeit. An Effizienz denken muss ich beruflich genug. Ich will das in rust schreiben. Also habe ich das getan. Es ist für das bisschen was es tut hoffnungslos over-engineered und arbeitet vermutlich langsamer als ein einfaches wget. Aber es funktioniert gut.

Außerdem habe ich mich da ein bisschen mit serde und dem Deserialisieren in rust-enums beschäftiggen können. Also was gelernt. Das Ding hat sogar eine Konfigurationsdatei, in der man den API-Key eintragen kann (optional, wenn man nicht mehr als 60 Anfragen pro Tag machen will).

Da ist noch Luft für jede Menge nutzlose Funktionen, die ich vielleicht irgendwann mal baue. Aber nicht jetzt. Oh und unter Windows funktioniert das mit der Konfigurationsdatei vermutlich nicht, weil ich es mir einfach gemacht und auf die HOME-Variable zugegriffen habe, die es unter Windows (glaube ich) nicht gibt.

Bildschirmhintergrund

Ich benutze gnome, da kann man sich den Hintergrund mit gsettings setzen:

gsettings set org.gnome.desktop.background picture-uri "file://$IMAGE_PATH"

IMAGE_PATH ist hier der Pfad zum Bild. Den gibt mein herunterladen-Programm oben auf stdout aus, weil ich zu faul war, mir etwas anderes einfallen zu lassen (wie gesagt, das Ganze wäre vermutlich mit einem einfachen shellscript einfacher zu lösen gewesen).

Kleiner Wörterbuch-Benchmark

Es heißt ja immer, man soll als Programmierer nicht zu früh zu viel Zeit in Optimierungen stecken. Auf der anderen Seite heißt es, man soll überflüssige Speicherallokationen vermeiden. Aber wie viel bringt das? Und lohnt es sich, den Code weniger lesbar zu machen, wenn man dafür ein paar Millisekunden sparen kann? Was, wenn es viele Millisekunden sind? Was ist, wenn man durch die Optimierungen aufpassen muss, dass man nicht dem Borrow-Checker von Rust auf die Füße tritt?

Ich habe keine Antworten darauf, aber ich habe zumindest ein paar Zahlen erhoben, wie sich die ganzen Allokationen auswirken.

Disclaimer: Das Ganze hier hat keinen Anspruch auf methodisch korrekte Wissenschaftlichkeit. Dazu habe ich hier zu wenig Gedanken hereingesteckt, und reviews gab es auch keine. Insbesondere habe ich auch keine Ahnung, was man bei Benchmarks alles berücksichtigen muss.

Das Beispielproblem

Das Problem, dessen Lösung getestet werden soll:

  • Lese eine Wörterbuch-Datei ein (words, eine Textdatei mit einem Wort pro Zeile)
  • lege die einzelnen Wörter in einer Wörterbuch-Datenstruktur ab (typischerweise Hashtabellen)
  • frage für ein Wort ab, ob es im Wörterbuch ist

Meine Wörterbuchdatei ist bei mir die Datei, die zufällig unter /usr/share/dict/words liegt, 976241 Bytes groß, 102774 Zeilen lang.

Grundlegend möchte ich zwei Ansätze testen:

  1. Lese die gesamte Datei in eine kontinuierliche Datenstruktur (array) ein, splitte sie und lege die Teilstrings (Verweise auf Bereiche der großen Datenstruktur) in einem Wörterbuch ab
  2. Lese die Datei Zeile für Zeile ein, allokiere für jede Zeile einen String und lege diesen String in einem Wörterbuch ab

Ach ja: Für praktische Zwecke ist das natürlich ziemlich dumm, wenn man eh die ganze Datei durchgeht und nur ein Wort abfragt, kann man sich das Ablegen sparen und vergleicht einfach jeden String mit dem gesuchten Wort. Das ist aber nicht das, was ich testen möchte.

Die ersten beiden Programme

Ich benutze natürlich erst einmal rust, weil es meine Lieblingssprache ist. Ansatz 1:

use std::collections::HashSet;
use std::fs::File;
use std::io::{BufReader, Read};
use std::path::Path;

fn with_big_string() -> std::io::Result<bool> {
    let file = File::open(Path::new("/usr/share/dict/words"))?;
    let mut reader = BufReader::new(file);

    let mut content = String::with_capacity(1024 * 1024);
    reader.read_to_string(&mut content)?;

    let dict: HashSet<&str> = content.lines().collect();

    Ok(dict.contains("banana"))
}

fn main() -> std::io::Result<()> {
    if with_big_string()? {
        println!("Oh, Banana!");
    } else {
        println!("No banana!");
    }

    Ok(())
}

Ich habe hier den Buffer für die Datei mal mit einer statisch definierten Kapazität versehen, die ließe sich aber auch ohne großen Aufwand aus den Metadaten der Datei auslesen.

Im Gegensatz dazu Ansatz 2:

use std::collections::HashSet;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;

fn with_small_strings() -> std::io::Result<bool> {
    let file = File::open(Path::new("/usr/share/dict/words"))?;
    let reader = BufReader::new(file);

    let dict: HashSet<String> = reader
        .lines()
        .collect::<std::io::Result<HashSet<String>>>()?;

    Ok(dict.contains("banana"))
}

fn main() -> std::io::Result<()> {
    if with_small_strings()? {
        println!("Oh, Banana!");
    } else {
        println!("No banana!");
    }

    Ok(())
}

In rust hat der erste Ansatz den Vorteil, dass er weniger Allokationen durchführt. Der Zweite hingegen hat den Vorteil, dass man weniger Ärger mit dem borrow-checker hat, wenn man das HashSet herumreicht. Im ersten Fall muss immer garantiert sein, dass content, von dem das HashSet borrowed, noch existiert.

Für sehr große Dateien möchte man vermutlich nicht die ganze Datei am Stück einlesen. Ist hier aber auch irrelevant, denn dann würde man auch nicht den ganzen Inhalt in einem Wörterbuch ablegen wollen.

Benchmarking

So… wie misst man jetzt die zeitliche performance? Einfachster Fall: time

$ time target/release/big_string
Oh, Banana!

real	0m0,016s
user	0m0,012s
sys	0m0,004s

Das Dumme ist nur: aufgrund verschiedener Umstände schwankt dieser Wert ganz schön:

$ time target/release/big_string
Oh, Banana!

real	0m0,045s
user	0m0,038s
sys	0m0,006s

Also was tun? Man müsste das mehrmals ausführen, und einen Mittelwert bilden. Dazu gibt es einen Haufen tools. Ich habe mir das erstbeste (hyperfine) geschnappt, und damit losgelegt. Mit -m wird die Mindestanzahl von Durchläufen angegeben, mit -w wird das Programm ein paar Mal ohne Messung vor dem Benchmark ausgeführt, damit die Caches des Betriebssystems weniger Unterschied machen.

$ hyperfine -w 10 -m 500 target/release/big_string
Benchmark #1: target/release/big_string
  Time (mean ± σ):      10.0 ms ±   0.2 ms    [User: 8.6 ms, System: 1.6 ms]
  Range (min … max):     9.6 ms …  10.9 ms    500 runs

Zehn Millisekunden. Gut. big_string ist Variante 1. Wie sieht es mit small_strings, also Variante 2 aus?

$ hyperfine -w 10 -m 500 target/release/small_strings
Benchmark #1: target/release/small_strings
  Time (mean ± σ):      17.5 ms ±   0.6 ms    [User: 14.9 ms, System: 2.7 ms]
  Range (min … max):    16.8 ms …  20.9 ms    500 runs

17,5 Millisekunden. Das sind über 70% mehr. Klar, das Ding muss ja auch über 100000 Allokationen mehr machen.

Python, um Vergleichswerte zu haben

Damit könnte ich durch sein. Ich habe aber noch ein paar Vergleiche mit anderen Sprachen gezogen. Als erstes Python 3. Variante 1:

f = open("/usr/share/dict/words", "r", encoding="utf-8")

content = f.read()

dictionary = set(content.splitlines())

if "banana" in dictionary:
    print("Oh, banana!")
else:
    print("No banana!")

Und Variante 2:

f = open("/usr/share/dict/words", "r", encoding="utf-8")

dictionary = set(f)

if "banana\n" in dictionary:
    print("Oh, banana!")
else:
    print("No banana!")

Die Programme sind viel Übersichtlicher. In Variante 2 wird mit set(f) das Wörterbuch direkt aus der Datei in die Datenstruktur geladen. Das geht, weil geöffnete Dateien hier ein Iterator über die Zeilen der Datei sind.

Ich bin mir übrigens nicht sicher, ob der Code wirklich genau das macht, was ich erwarte, aber wenn ich die Dokus richtig verstanden habe, sollte er es tun. Die Ergebnisse:

$ hyperfine -w 10 -m 500 ./big_string.py
Benchmark #1: ./big_string.py
  Time (mean ± σ):      30.6 ms ±   0.5 ms    [User: 24.5 ms, System: 6.1 ms]
  Range (min … max):    29.7 ms …  32.8 ms    500 runs

und Variante 2:

$ hyperfine -w 10 -m 500 ./small_strings.py
Benchmark #1: ./small_strings.py
  Time (mean ± σ):      29.3 ms ±   0.6 ms    [User: 23.6 ms, System: 5.7 ms]
  Range (min … max):    28.4 ms …  32.0 ms    500 runs

Die nicht-Überraschung: Beide Varianten sind viel langsamer als die rust-Implementierung. Trotzdem recht schnell, für python-Verhältnisse, was vermutlich daran liegt, dass hinter set() und den io-Funktionen optimierter Code liegt.

Interessant ist jedoch, dass beide Varianten etwa gleich schnell sind. Variante 2 ist sogar minimal schneller. Ob das signifikant ist, kann ich nicht sagen. Warum das ist, auch nicht. Ich habe kein besonders gutes Verständnis davon, was bei Python unter der Haube vor sich geht. Bei rust schon eher, aber nur in der Theorie.

Go

Manche Leute sagen, man könne rust nicht mit Go vergleichen, weil die Sprachen für Unterschiedliche Zwecke gedacht sind. Andere Leute (sowohl rust- als auch Go-Fans) bereiten mit Vergnügen ausgefeilte Vergleiche zu. Ich halte mich hier zurück. Dieses Minimalbeispiel reicht nicht, um die Performance von rust und Go im Großen zu vergleichen. Und eigentlich geht es auch mehr um den Vergleich zwischen den beiden Varianten in Go.

Variante 1:

package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"
)

func main() {
	file, err := os.Open("/usr/share/dict/words")
	if err != nil {
		log.Fatal(err)
	}

	read := bufio.NewReader(file)

	contentBytes, err := ioutil.ReadAll(read)
	if err != nil {
		log.Fatal(err)
	}
	// just assume it is utf-8, this is sufficient for this test
	content := string(contentBytes)

	dict := make(map[string]bool)
	for _, line := range strings.Split(content, "\n") {
		dict[line] = true
	}

	if dict["banana"] {
		fmt.Println("Oh, banana!")
	} else {
		fmt.Println("No banana!")
	}
}

Variante 2:

package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
)

func main() {
	file, err := os.Open("/usr/share/dict/words")
	if err != nil {
		log.Fatal(err)
	}

	//read := bufio.NewReader(file)

	scanner := bufio.NewScanner(file)

	dict := make(map[string]bool)
	for scanner.Scan() {
		dict[scanner.Text()] = true
	}
	if err = scanner.Err(); err != nil {
		log.Fatal(err)
	}

	if dict["banana"] {
		fmt.Println("Oh, banana!")
	} else {
		fmt.Println("No banana!")
	}
}

Und die Performance:

$ hyperfine -w 1 -m 500 ./big_string
Benchmark #1: ./big_string
  Time (mean ± σ):      15.6 ms ±   0.6 ms    [User: 17.4 ms, System: 4.9 ms]
  Range (min … max):    14.6 ms …  19.8 ms    500 runs

Deutlich langsamer als die erste Variante in rust, aber auch schneller als die zweite Variante in rust. Und Variante 2?

$ hyperfine -w 1 -m 500 ./small_strings
Benchmark #1: ./small_strings
  Time (mean ± σ):      18.3 ms ±   1.3 ms    [User: 18.1 ms, System: 5.2 ms]
  Range (min … max):    16.8 ms …  23.2 ms    500 runs

Das Bild bei Go ist also ähnlich zu dem in rust: die zweite Variante ist langsamer, auch wenn sie bei Go deutlich näher aneinanderliegen. Und der Unterschied zwischen Variante 2 in rust und go ist ziemlich klein.

Folgerungen

In rust und Go ist es tatsächlich besser, die ganze Datei am Stück einzuladen und dann zu zerlegen, um Allokationen zu sparen. Zumindest von der Performance her. Praktisch will man in rust vielleicht nicht immer den String, von dem geborrowed wurde mit herumschleppen. Da ist ein Vorteil von Go: Dank garbage collection braucht man hier keine Sorgen zu haben. In den meisten Fällen ist es vermutlich einfach egal und fällt damit unter die „keine vorzeitige Optimierung“-Regel.

Ausblick

Mir sind noch ein paar Variationen eingefallen, die ich mal ausprobieren möchte. Zum Beispiel wird hier die Hashtabelle nicht in der richtigen Größe pre-allokiert. Grund dafür ist, dass die Anzahl der Zeilen bei der ersten Allokation einfach noch nicht bekannt ist. Sowohl in rust als auch in Go könnte man aber einen Wert abschätzen (entweder indem man schummelt und einen passenden Wert einträgt, oder indem man die Länge der Datei nimmt und eine durchschnittliche Wortlänge annimmt, mit der man zumindest eine grobe Schätzung bekommt).

Auch interessant wäre, ob es einen Unterschied beim Abfragen von Werten aus dem Wörterbuch macht. Je nach Variante sind da alle Strings im Speicher ziemlich nah beieinander oder irgendwo im Speicher verteilt. Aufgrund der Art, wie in einer Hashtabelle Werte abgelegt werden ist meine Hypothese aber, dass es keinen großen Unterschied macht.

Damit kämen wir zur nächsten Idee: Vergleichen wir Hashtabellen mit B-Bäumen. Das ist aber auch recht langweilig, für diesen Fall sind B-Bäume vermutlich stark im Nachteil.

Das war's. Vielleicht nicht besonders nützlich, aber interessant.

Positivprojekt 11: Frühlingsanfang

Diese Woche kommt nichts, was nicht schon jeder weiß. Ich finde es allerdings sehr positiv: Heute ist Frühlingsanfang.

Das bedeutet unter Anderem, dass es jetzt wieder länger hell als dunkel ist und dass bald wieder alles grün wird. Ich mag grün. Und insbesondere bedeutet es, dass bald wieder alles besser riecht. Winter stinkt.

Als ich noch studiert habe, konnte ich immer sagen: „Meine Lieblingsjahreszeit ist Sommersemester“.

Positivprojekt 12: Zeitumstellung

Ich bin ja froh, dass in der 2019 in der EU entschieden wurde, dass die EU-weite verpflichtende Zeitumstellung abgeschafft werden soll. Die Zeitumstellung bringt meiner Ansicht nach mehr Ärger als Nutzen, alleine schon weil so viele Softwareentwickler es nicht hinkriegen, ihre Timestamps ordentlich zu verarbeiten. Zeitzonen sind schwierig, aber das ist keine Entschuldigung, nicht zumindest einen offset an den Timestamp zu schreiben.

Ich bin aber ebenso froh, dass die Sommerzeit nicht sofort abgeschafft wurde. Ich hatte ja ein bisschen Sorge, dass das zu schnell passieren würde. Das wäre zum einen doof, weil es unweigerlich zu einem europäischen Flickenteppich an Sommerzeit/nicht Sommerzeit geführt hätte (was die ganze Lager verschlimmert hätte).

Zum anderen wäre eine zu schnelle Umstellung problematisch, weil ein riesiger Haufen Software von Zeitzonenberechnungen abhängt. Wenn man jetzt die Zeitzohnen ändert, muss man erst einmal die Bibliotheken, die damit umgehen, anpassen. Dann muss man in allem, was direkt davon abhängt die Abhängigkeit aktualisieren. Dann muss man das Ganze noch für alles machen, was davon abhängt, und so weiter.

Bei vielen Programmen und Bibliotheken sollte das ganz schnell gehen. Aber selbst dann muss die aktualisierte Software auch überall ausgespielt werden, und wir wissen alle, wie schwer sich gerade Unternehmen und Ämter damit tun, Updates zu installieren. Aber selbst wen da alles schnell läuft, gibt es immer noch einen Haufen Software, der schlecht gepflegt wird oder bei dem es aufgrund von Versionskonflikten nicht einfach sein wird, die aktualisierten Bibliotheken einzubinden.

Von daher sollten wir uns alle freuen, dass die Mühlen der Legislative so langsam laufen. Mein Vorschlag ist: Mitteleuropäische Sommerzeit in allen beteiligten Ländern abschaffen, zum gleichen Zeitpunkt, und mit einer Vorlaufzeit von mindestens fünf Jahren. Wer dann seine Software immer noch nicht aktualisiert hat, darf berechtigt verspottet werden.

Positivprojekt 13: Pandoc

Markdown oder besser die standardisierte Form, Commonmark ist doch etwas Feines. Es ist textbasiert, arbeitet daher sehr gut mit den üblichen Versionsverwaltungstools (z.B. git) zusammen. Die meiste Syntax ist einfach zu merken. Es ist mittlerweile recht weit verbreitet, github und stackoverflow zum Beispiel benutzen markdown.

Der meiner Ansicht nach größte Vorteil gegenüber anderen Auszeichnungssprachen? Es ist auch als Rohtext sehr schön formatiert lesbar, im Gegensatz z.B. zu HTML oder LaTex. Das macht es zwar auch weniger mächtig, aber für mehr oder weniger einfache Textstrukturen reicht es vollkommen aus. Dieses Blog hier nutzt zum Beispiel Commonmark (Stand April 2021, hier auch vielen Dank an das schöne [pulldown-cmark-crate)[https://crates.io/crates/pulldown-cmark]).

Pandoc

Aber was, wenn man nur mal eben eine Markdown-Datei in ein HTML-Dokument umwandeln muss? Oder in ein PDF? Oder (brrr) ein MS-Word-Dokument?

Nun, dafür gibt es Pandoc. Pandoc ist ein in Haskell geschriebenes Tool zum Konvertieren von Dokumenten (kann also noch deutlich mehr als im letzten Absatz angedeutet und ist ziemlich flexibel dabei.

Also, ein PDF aus einer Markdown-Datei?

pandoc --from commonmark --to pdf example.md -o example.pdf

Einfach genug. Natürlich gibt es da noch ein paar Optionen, um dem Ergebnis den Feinschliff zu verpassen, da muss man ein bisschen im Netz suchen oder die Doku durchlesen. Das --from commonmark kann man auch weglassen, dann nimmt pandoc aber vermutlich nicht commonmark, sondern einen anderen Markdown-Dialekt.

Wie steht es mit einer HTML-Datei? Hier gibt es eine kleine Falle, denn Folgendes gibt kein vollständiges HTML-Dokument aus, sondern nur den HTML-Schnipsel, der aus der Markdown-Datei entsteht:

pandoc --from commonmark --to html5 example.md -o example.html

Hat auch seine Anwendungszwecke, aber für alleinstehende Dokumente macht man lieber das hier:

pandoc --standalone --from commonmark --to html5 example.md -o example.html

Nun gibt einem das aber auch eine Warnung aus, à la

[WARNING] This document format requires a nonempty <title> element.
  Defaulting to 'example' as the title.
  To specify a title, use 'title' in metadata or --metadata title="...".

Für HTML brauchen wir also ein Titel-Tag, und Pandoc weiß nicht so recht, was es da reinschreiben soll. Man kann in Pandoc auf verschiedene Arten Metadaten angeben, die einfachste steht dabei: Über den --metadata title="..."-Parameter. Alternativ kann man das in einer gesonderten Datei als json oder yamle ablegen (--metadata-file=...) oder es im Header der Quelldatei angeben (dann muss beim --from noch die entsprechende Erweiterung angegeben werden, sucht euch das selber zusammen).

Wenn man mit dem Default-HTML-Template von pandoc nicht zufrieden ist, kann man auch selber eins anlegen, und es dann mit --template=... übergeben. Das Template-Format ist in der Doku beschrieben.

Andere Formate, wie zum Beispiel MS-Word-Docx, Epub in verschiedenen Versionen, LaTex und mehr, sind auch verfügbar. In welche Richtungen mal alles konvertieren kann weiß ich nicht, aber von Markdown kann sollte man in die meisten oder alle konvertieren können (wiederum weil Markdown so eingeschränkt ist). Ihr braucht mal schnell ein epub? Kein Problem. Und alles freie Software, GPL lizensiert.

Positivprojekt 14: Augsburger Puppenkiste erklärt Covid-19-Selbsttest

Ich habe ja aus meiner Kindheit liebgewonnene Erinnerungen an die Augsburger Puppenkiste, insbesondere natürlich an Jim Knops und Lukas den Lokomotivführer.

Aktuell fallen dort wie an vielen anderen Stellen auch aktuelle Aufführungen aus. Allerdings haben sie ein Video produziert, in dem der Kasper die Bedienung von Corona-Schnelltests erklärt.