Hack: Beschleunigte Verzeichnisauflistung bei sehr vielen MP3s

Mittlerweile haben sich auf meiner SD-Karte 1500 MP3-Dateien in 150 Ordnern angesammelt.
Das Laden der Datei-Explorer Ansicht in der Weboberfläche ist durch die vielen Daten jetzt spürbar langsamer geworden > 2 Sekunden.

Es gab ja schon einige Versuche hier etwas zu optimieren wie schlankeres JSON, Übertragung über Websocket usw. Das Problem liegt aber im Dateisystem (FS) selbst. Die Funktion getNextFile() ist für das Auflisten von Verzeichnissen nicht optimiert. Es wird für jeden Dateinamen ein File-Objekt erzeugt und Dateieigenschaften wie Größe/Datum gelesen. Obwohl wir ja nur einen Dateinamen benötigen.
Auf Basis dieses Feature-Request habe ich das FS miit der Funktion getNextFileName() erweitert. Die gibt nur Dateinamen zurück ohne jeglichen Overhead. Siehe da, die Auflistung ist spürbar schneller (Faktor 20).

Ladezeit jetzt:

build filelist finished: 2041ms

Mit Patch:

build filelist finished: 98ms

Einen Pull-request dazu plane ich im Momemt nicht wg. laufenden Refaktoring der Weboberfläche durch @sonovice , auch riecht das Ändern des FS ein wenig nach „Hack“. Vielleicht schafft es der Feature-Request ins offizielle Arduino-ES32 Release… Wer es ausprobieren möchte, hier die Anleitung:

In platform.ini das erweiterte FS verknüpfen:

lib_deps =
	https://github.com/tueddy/FS.git#1.0.6
	;https://github.com/tueddy/FS.git		<- Für Arduino 2.0.5
	https://github.com/schreibfaul1/ESP32-audioI2S.git

Diese Zeilen in Web.cpp eintragen. Über den Compilerschalter #define FILEEXPLORER_SPEEDUP kann man dann umschalten.

Happy hacking!

5 „Gefällt mir“

Wie viele Dateien waren in dem Ordner?

Das habe ich gelesen, glaube aber nicht, dass diese Zeit für 1500 Files gültig ist. Zumal ja zur Zeit immer nur ein Verzeichnis geholt wird.

Meine Messung stammt direkt aus dem ESPuino Code hier.

Stimmt, es wird nur ein Verzeichnis gelesen (Beim ersten Aufruf der Weboberfläche das Root-Verzeichnis).
Das übertragene JSON ist recht klein und enthält nur die erste Ebene. Ich verwende SD_MMC mit einer aktuellen SD Karte. Also Alles optimal.

Habe heute Abend all meine SD-Karten rausgekramt und nochmal Messungen durchgeführt. Mit dem Espuino-Code Dateilisting des Root-Verzeichnisses ohne Patch (SD_MMC): Die Kingston Karte ist etwas betagt, alle anderen recht aktuell:

Samsung Evo 32GB, 406 Elemente (Alle Dateien + Ordner)
build filelist finished: 195 ms, 28 files in root

Kingston 8GB, 373 Elemente (Alle Dateien + Ordner)
build filelist finished: 592 ms, 41 files in root

Transcend Preminum 16GB, 1201 Elemente (Alle Dateien + Ordner)
build filelist finished: 931 ms, 82 files

Scandisk Ultra 16GB, 1569 Elemente (Alle Dateien + Ordner)
build filelist finished: 2152 ms, 146 files in root

Bei der Transcend Karte habe ich ein dann einziges Root-Verzeichnis erstellt und alle Dateien dort hinein verschoben:

Transcend Preminum 16GB
build filelist finished: 52 ms, 1 files in root

Dann habe ich ein Testprojekt erstellt das nur den Root listet ohne JSON usw. Habe dazu den Lolin D32 Pro mit dem SPI Anschluss verwendet, da wird es noch viel schlimmer:

Samsung Evo:
getNextFileName(), done reading root-directory 36 elemts in 18 ms = 0.50 ms/folder
openNextFile(), done reading root-directory 36 elements in 492 ms = 13.67 ms/elements

Kingston:
getNextFileName(), done reading root-directory 42 elemts in 29 ms = 0.69 ms/folder
openNextFile(), done reading root-directory 42 elements in 1119 ms = 26.64 ms/elements

Transcend:
getNextFileName(), done reading root-directory 84 elemts in 83 ms = 0.99 ms/folder
openNextFile(), done reading root-directory 84 elements in 6960 ms = 82.86 ms/elements

Samsung:
getNextFileName(), done reading root-directory 151 elemts in 68 ms = 0.45 ms/folder
openNextFile(), done reading root-directory 151 elements in 6872 ms = 45.51 ms/elements

Fazit: Das Arduino-FS ist grottenlangsam beim Auflisten eines Verzeichnis bei vielen Dateien , worst-case fast 7 Sekunden…
Je mehr Dateien/Ordner auf der SD vorhanden sind desto langsamer wird das. Die Messungen zeigen immer die gleichen Werte und weichen nur um wenige Millisekunden ab.
Alles über 0,5 Sekunden hakelt deutlich in der Weboberfläche!

Oder habe ich hier etwas übersehen oder einen Fehler gemacht?

Nicht dass wir uns hier falsch verstehen - das wollte ich mit meiner Rückfrage nicht behaupten :slight_smile:

Mir ging es darum ein Verhältnis zwischen Laufzeit und Anzahl der Files zu bekommen. Das der Patch mega gut ist und die Auflistung beschleunigt steht ja ausser Frage.
Hintergrund meiner Frage:
In meinem Fork ist es so, dass sofort die ersten Files übertragen werden, man also nicht auf die komplette Liste warten muss. Das Verzeichnis baut sich quasi auf. Das ist erst einmal cool, allerdings verliert man natürlich auch Zeit dadurch, dass mehrere kleine JSONs übertragen werden anstatt nur ein etwas größeres.
Wenn nun die gesamte Auflistung nur noch 100 ms dauert, könnte dieser Vorteil schnell kippen und es könnte sich schneller anfühlen, wenn doch das ganze Verzeichnis übertragen wird.
Das wollte ich gerne mal ausprobieren - dafür brauche ich aber die Information,
wie viele Files benötigen jetzt nur noch 98ms anstatt vorher 2041ms.

Also würde ich jetzt auch mal mit 150 Files testen wollen…

Moin @Christian,
ja das wäre spannend wie es sich bei Dir verhält, berichte mal hier.
Bei so 150 Dateien im Root sollte man das gut sehen können.

Ich meine jetzt einen Bug im Espuino-Code gefunden zu haben (Und den habe ich evt. selbst eingebaut :sunglasses:). Hier die betroffene Stelle:

Wenn keine Musik abgespielt wird soll feedTheDog() aufgerufen werden um einen Watchdog-Reset zu verhindern. Bei laufender Musik sorgt vTaskDelay(5) für ruckelfeies abspielen.

Beim Start/Reset der Box und sofortigen Aufruf der Weboberfläche:

gPlayProperties.pausePlay: 0
Ohne Patch: build filelist finished: 3076 ms, 82 files
Mit Patch: build filelist finished: 455 ms, 82 files

Jetzt lasse ich Musik laufen und bekomme

gPlayProperties.pausePlay: 0
Ohne Patch: build filelist finished: 3140 ms, 82 files
Mit Patch:build filelist finished: 459 ms, 82 files

Nun die Karte abziehen:

gPlayProperties.pausePlay: 1
Ohne Patch: build filelist finished: 2691 ms, 82 files
Mit Patch: build filelist finished: 59 ms, 82 files

Letzer Fall zeigt akzeptable Werte. Müsste beim Start gPlayProperties.pausePlay nicht mit True initialisiert sein und die Bedingung umgedreht sein ?

        if (gPlayProperties.pausePlay) {
            // time critical, avoid delay with many files on SD-card!
            feedTheDog(); 
        } else {
            // If playback is active this can (at least sometimes) prevent scattering
            vTaskDelay(portTICK_PERIOD_MS * 5);
        } 

Edit: Hatte die obigen Werte mit aktivierten Patch angegeben, jetzt mit und ohne.