UNIXwork

Tags

c dav unix shell linux xattr solaris links x11 java rant fun webdav sync gnome apple benchmark network ldap oracle analytics xnedit macos windows graalvm bsd curl mac apache wtf virtualbox microsoft zfs sparc tomcat rhel freebsd arm

Raspberry Pi1 vs Raspberry Pi4 vs Fujitsu s920 vs Sun Ultra 45

24. Dezember 2019

Vor Jahren hatte ich ein Raspberry Pi 1B als Server für ein paar kleine Dienste im Einsatz. Ersetzt habe ich das Teil aber später durch einen Fujitsu Futro s920, ein Thin-Client mit einer AMD G-Serie Quad-Core CPU (GX-415 GA). Die AMD G-Serie ist für embedded Computer entwickelt und daher hat der Fujitsu Thinclient auch eine recht niedrige Leistungsaufnahme. Zumindestens im Idle ist diese nicht viel höher als beim Raspberry Pi, aber dafür mit deutlich mehr Rechenleistung.

Ein Raspberry Pi 4 bietet ebenfalls eine Quadcore-CPU mit ähnlichem Takt. Daher hatte ich die Idee, mal ein bisschen die Hardware zu benchmarken. Und aus Spaß vergleiche ich das noch mit einer Sun Ultra 45 Workstation, die zumindestens einen ähnlichen CPU-Takt hat. Diese hat zwei UltraSPARC IIIi CPUs, ich glaube aus dem Jahr 2003.

Test-Hardware

  • Raspberry Pi 1 Model B: CPUs (ARMv6): 1 x 700 MHz
  • Raspberry Pi 4 Model B: CPUs (ARMv8): 4 x 1500 MHz
  • Sun Ultra 45: CPUs (spar4u): 2 x 1600 MHz
  • Fujitsu S920: CPUs (amd64): 4 x 1500 MHz

Sowohl beim Raspberry Pi 1 als auch 4 kommt ein 32bit Raspbian zum Einsatz. Vielleicht nicht optimal für die Performance, auf grund des wenigen RAMs (2 GB) aber praktisch vielleicht die bessere Wahl für den Produktivbetrieb.

Benchmarks

Als Integer-Benchmark wird die Schach-Engine stockfish benutzt. Stockfish bietet sehr gute Optimierung für x86 und auch ARM, aber nicht für sparc. Daher habe ich das ganze ohne spezielle Architekturoptimierungen kompiliert, also nur mit normaler Compiler-Optimierung.

stockfish bench 128 <num-cpus>

Als Floatingpoint-Benchmark kommt c-ray zum Einsatz. Dies ist ein einfacher in C geschriebener Raytracer.

c-ray-mt -t <num-cpus> -s 1000x1000 -r 10 -i scene -o out.ppm

Ergebnisse Stockfish

Die Ausgabe von stockfish bench sieht so aus:

Total time (ms) : 14304
Nodes searched  : 5607358
Nodes/second    : 392013

Für den Vergleich nehm ich nur Nodes/second. Je größer hier der Wert ist, desto besser:

rpi1     u45-1cpu   u45-2cpu   rpi4-1cpu   rpi4-4cpu   s920-1cpu   s920-4cpu
----------------------------------------------------------------------------
26503    277715     559029     392013      1421621     403146      1595700
26655    277386     559604     393222      1402173     402682      1530797
26623    277002     549077     385544      1424797     403261      1580684
26303    277152     566223     391739      1458121     402191      1551561
26926    277317     548266     391411      1435041     403146      1503410

Mittelwert mit Standardabweichung:

rpi1           26.602   ±    203,68
u45-1cpu      277.314,4 ±    240,66
u45-2cpu      556.439,8 ±  6.832,99
rpi4-1cpu     390.785,8 ±  2.691,36
rpi4-4cpu   1.428.350,6 ± 18.298,75
s920-1cpu     402.885,2 ±    400,12
s920-4cpu   1.552.430,4 ±  3.293,49

Ergebnisse C-Ray

C-Ray liefert am Ende die Anzahl der Millisekunden, die das Rendern gedauert hat.

rpi1     u45-1cpu   u45-2cpu   rpi4-1cpu   rpi4-4cpu   s920-1cpu   s920-4cpu
----------------------------------------------------------------------------
88444    29585      16133      10015       3128        13699       4282
86406    29013      16112      10028       3118        13698       4189
87455    29014      16134      10032       3138        14068       4186
86456    29000      16106      10032       3119        14058       4189
86676    28998      16033      10004       3110        13701       4185

Mittelwert mit Standardabweichung:

rpi1        87.087,4 ± 775,59
u45-1cpu    29.122   ± 231,59
u45-2cpu    16.103,6 ±  37,01
rpi4-1cpu   10.022,2 ±  11,03
rpi4-4cpu    3.122,6 ±   9,58
s920-1cpu   13.844,8 ± 178,19
s920-4cpu    4.206,2 ±  37,93

Auswertung

Der Unterschied zwischen Raspberry Pi 1 und 4 ist gigantisch. Allein im Single-Core Benchmark ist ein rpi4 14 mal schneller als ein rpi1. Dafür bietet ein rpi4 aber statt einem Core gleich 4. Auch die FP-Geschwindigkeit ist deutlich höher. Aber das war auch nicht anders zu erwarten.

Spannender ist der Kampf zwischen rpi4 und Fujitsu s920. Diese sind in etwa auf Augenhöhe. Im Integer-Benchmark führt die AMD-CPU, im FP-Benchmark die ARM-CPU. Interessanterweise ist beim Stockfish-Benchmark die Standardabweichung beim s920 deutlich geringer als beim rpi4. Eine mögliche Ursache könnten die thermischen Probleme beim rpi4 sein, die zu einer Reduzierung des CPU-Takts führen könnten. Doch bei C-Ray ist es genau umgekehrt. Ich habe noch keine Erklärung dafür, wieso beim rpi4 hier die Werte so viel stabiler sind, als bei den anderen Testkandidaten.

Etwas schwach finde ich die Resultate bei der Ultra 45. Die Hardware ist natürlich schon etwas in die Jahre gekommen, aber da es sich hier nicht um kleine embedded CPUs handelt, hätte ich doch mehr erwartet. Eine Ursache könnte aber auch sein, dass ich auf der Ultra 45 ein recht altes OS (Solaris 10) betreibe und zum kompilieren hatte ich nur gcc 5.5 zur Verfügung. Auf dem rpi4 z.B. kam gcc 8.3 zum Einsatz.

Das war jetzt ein einfacher CPU-Benchmark. Interessant wäre ein IO-lastiger Benchmark. Die Ultra 45 wird nicht kampflos aufgeben. Gerade die Bus-Architektur ist beim rpi4 immer noch Schrott, daher könnte die Ultra 45 hier punkten. Fortsetzung folgt (irgendwann).

C: Attribute von allen Dateien im Verzeichnis

23. Dezember 2016

Wenn man ein Verzeichnis liest und von allen enthaltenen Dateien die Extended Attributes erhalten will, gibt es zwei Möglichkeiten:

  1. Man fügt zum Verzeichnispfad den Dateinamen hinzu und nutzt den neu erhaltenen Pfad mit den Syscalls listxattr oder getxattr.
  2. Mit dem Filedescriptor des Verzeichnisses und openat öffnet man die Dateien und nutzt dann flistxattr und getxattr.

Ich hab mich gefragt was schneller ist. Dazu habe ich ein kleines Testprogramm geschrieben. Dieses kann mit unterschiedlichen Preprocessor-Optionen kompiliert werden. So habe ich 4 Testprogramme erstellt. Für getxattr und fgetxattr jeweils ein Programm, das ein Attribut liest und eines das 32 Attribute liest.

Bei einem Verzeichnis mit 128.000 Dateien hab ich folgende Werte erhalten:

getxattr:1  getxattr:32  fgetattr:1  fgetattr:32
------------------------------------------------
246100055   654704421    456172044   749849574
230183311   663632162    457183706   769223423
247109480   654775136    440397212   743349119

Die Datei erst zu öffnen um dann fgetxattr zu nutzen ist also langsamer. Erst als ich das Programm so modifiziert habe, dass es mehrere hundert Attribute liest, war es etwas schneller. Das ist jedoch ein eher unrealistisches Szenario. Allerdings war das ganze generell sehr schnell, so dass es eigentlich egal ist, welche Methode man anwendet.

Festplatten-Benchmark mit bonnie++

21. Dezember 2016

Ein einfaches Tool für Benchmarks von Festplatten bzw. Dateisystemen ist bonnie++. Man startet es einfach und es werden im aktuellen Arbeitsverzeichnis verschiedene Tests durchgeführt.

Hier mal ein Beispiel, auch wieder auf einem Raspberry Pi:

$ bonnie++
Writing with putc()...done
Writing intelligently...done
Rewriting...done
Reading with getc()...done
Reading intelligently...done
start 'em...done...done...done...
Create files in sequential order...done.
Stat files in sequential order...done.
Delete files in sequential order...done.
Create files in random order...done.
Stat files in random order...done.
Delete files in random order...done.
Version 1.03e       ------Sequential Output------ --Sequential Input- --Random-
	                -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP
raspi.local      1G  3565  95 20890  44  9180  19  3822  97 24020  27  1060  23
	                ------Sequential Create------ --------Random Create--------
	                -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
	          files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
	             16  3930  96 +++++ +++  6338 106  3920  94 +++++ +++  5559  96
raspi.local,1G,3565,95,20890,44,9180,19,3822,97,24020,27,1059.6,23,16,3930,96,+++++,+++,6338,106,3920,94,+++++,+++,5559,96

Die Tests mit putc() und getc() sind recht CPU-lastig, da hier Zeichen einzelnd geschrieben oder gelesen werden. Die block-basierten Tests hingegen zeigen gut den IO-Durchsatz.

Was auch auffällt, bei den Create-Tests fehlen die Werte für Read. Dies liegt daran, das in meinem Fall die Tests zu schnell fertig waren und dadurch keine genauen Ergebnisse berechnet werden können. Bonnie++ zeigt daher lieber keinen Wert an.

Netzwerk-Benchmark mit iperf

20. Dezember 2016

Mit iperf kann man sehr einfach sein Netzwerk benchmarken. Dazu startet man auf einem Computer einen iperf-Server:

$ iperf -s
------------------------------------------------------------
Server listening on TCP port 5001
TCP window size: 85.3 KByte (default)
------------------------------------------------------------

Und auf einem anderen Computer startet man den iperf-Client und gibt den Server an (in diesem Fall rpi):

$ iperf -c rpi
------------------------------------------------------------
Client connecting to rpi, TCP port 5001
TCP window size: 85.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.178.170 port 39890 connected with 192.168.178.28 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec   114 MBytes  95.5 Mbits/sec

Man sieht, ein Raspberry Pi 1 kann sein 100 Mbit/s LAN gut auslasten. Allerdings hat das bei mir auch zu 75% CPU-Last geführt.

stat Benchmark

09. Februar 2016

Der Syscall stat wird benutzt um an Informationen wie Dateigröße oder Änderungsdatum einer Datei zu gelangen. Die Funktion erwartet als Argumente nur einen Dateipfad und einen Buffer. Daneben gibt es noch die Variante fstat, die statt eines Dateipfades einen Filedescriptor erwartet, und fstatat, die den Filedescriptor eines geöffneten Verzeichnisses und einen Pfad relativ zu dem Verzeichnis erwartet.

int stat(const char *restrict path, struct stat *restrict buf);
int fstat(int fildes, struct stat *buf);
int fstatat(int fd, const char *restrict path,
    struct stat *restrict buf, int flag);

Mich hat jetzt ein Performancevergleich zwischen den Funktionen interessiert. Zum einen für den Fall, dass man für jede Datei in einem Verzeichnis stat aufrufen will. Hier würde sich der Einsatz von fstatat anbieten. Der andere Fall wäre, wenn man eine Datei öffnen und stat-en will. Um dies zu testen hab ich ein primitives Programm geschrieben, dass man hier findet. Das Programm erwartet als Argument einen Pfad zu einem Verzeichnis, welches es zunächst ließt. Danach führt es 4 Tests durch und misst für jeden die Zeit:

  • test_stat: Für jede Datei des Verzeichnisses wird stat aufgerufen.
  • test_fstatat: Für jede Datei wird fstatat aufgerufen.
  • test_open_stat: Jede Datei wird mit open geöffnet und danach noch stat aufgerufen.
  • test_fstat: Jede Datei wird geöffnet und fstat aufgerufen.

Ein kleiner Test unter Linux ergab:

test 195 files:

test_stat
time: 496386 ns

test_fstatat
time: 246039 ns

----------------------------------------

test_open_stat
time: 1106593 ns

test_open_fstat
time: 885549 ns

Ähnliche Ergebnisse konnte ich auch unter FreeBSD und Solaris, mit Festplatten und SSDs, reproduzieren. Der Vergleich zwischen test_stat und test_fstatat zeigt, dass fstatat deutlich schneller ist. In beiden Tests wird auch nur pro Datei jeweils ein Syscall aufgerufen. Bei test_open_stat und test_open_fstat wird in beiden Fällen zunächst open benutzt um die Datei zu öffnen und es zeigt sich, dass in diesem Fall fstat auch schneller ist als stat, allerdings fällt der Unterschied hier nicht so sehr ins Gewicht.