Conversation
Without one, every backward f_lseek walks the FAT chain again from the file's first cluster - and every COMPRESSION_SELF hunk is a backward seek, to an earlier hunk the image references instead of storing twice. The cost grows with how far into the file the reader is, so a short test near the start never shows it. On an ESP32-P4 a 29%-self-referenced image did not finish in 47 minutes until the benchmark built a map (070341a). The BL616 bridge an integration copies never had that fix, and its FatFs is built with FF_USE_FASTSEEK on. chd_fatfs_open() now builds the map right after f_open, before chd_open, so the header and map reads benefit too. The table is two words per fragment plus one; it starts at 16 words, grows to exactly what FatFs reports it needs, and past CHD_FATFS_CLMT_MAX runs without a map rather than failing - slower backward seeks, still correct. Compiled out entirely when FF_USE_FASTSEEK is 0. The close callback frees the table. libchdr calls it on a failed open too (#195), so the bridge's error path has nothing left to close. The RP2350 benchmark builds the same map, behind BENCH_LINKMAP so it can be measured on and off. Not measured yet on either board. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01846EhHuAFk5qvxwEA5Gq6y
cf60b8b to
9fcd4bc
Compare
|
Tested this on real BL616 hardware (Sipeed Tang Console 60K, TangCore firmware) and it Symptom
DE2 opened cleanly on every build before this change. Bisectlibchdr
So the cluster link map is the cause. Why it looks like wrong seeksA decompression error at open means the huffman-compressed v5 hunk map decoded from the Environment, since it is probably fragmentation-dependent
What I could not determineI read the builder and FatFs's side carefully and could not fault the logic:
So this is an empirical result, not a diagnosis. Two things I would look at first:
Happy to run more instrumented builds on this hardware if it would help — I can log |
Without a cluster link map, every backward
f_lseekwalks the FAT chain again from the file's first cluster — and everyCOMPRESSION_SELFhunk is a backward seek, to an earlier hunk the image references instead of storing twice. The cost grows with how far into the file the reader is, so a short test near the start never shows it.On an ESP32-P4 a 29%-self-referenced image did not finish in 47 minutes until the benchmark built a map (
070341a). The BL616 bridge that an integration copies never got that fix, and TangCore's FatFs is built withFF_USE_FASTSEEK 1— the capability is there, nothing was using it.What changes
chd_fatfs_open()builds the map right afterf_open, beforechd_open, so the header and map reads benefit too. The table needs two words per fragment plus one: it starts at 16 words, grows to exactly what FatFs reports it needs, and pastCHD_FATFS_CLMT_MAXruns without a map rather than failing — slower backward seeks, still correct. Compiled out whenFF_USE_FASTSEEKis 0.The close callback frees the table. Since #195, libchdr calls it on a failed open too, so the bridge's error path has nothing left to close.
The RP2350 benchmark builds the same map behind
BENCH_LINKMAP, so it can be measured with and without.Measured
On an ESP32-C6: FatFs over SPI, no SDMMC host, a single RISC-V core, which is the same storage shape as the FatFs firmware targets. The benchmark compiled this PR's
chd_fatfs.cas is, with ESP-IDF'sFF_USE_FASTSEEKon and off, everything else identical. Every hunk was CRC-checked and read back correctly in both runs.CHD_FATFS_CLMT_MAXThe map makes seeks about 3x cheaper, and the 52-fragment copy seeks as fast as the contiguous file. End to end that is 1-2% on these reads: storage is a quarter to two fifths of the time, and seeks are a small part of storage. The 822-fragment copy needs more than 1,024 words, so the bridge runs without a map and behaves like the build without one.
The seeks here are not self-references. The benchmark builds with
LOWRAM_TARGET, which reads the v5 map lazily, a window at a time, and the large image's map sits at 99.9% of the file. Every window fetch is a seek to the end and the next hunk read a seek back to the start, a full chain walk each way. So the map pays on any image underLOWRAM_TARGET, self-referenced or not.The fragmented copies were written on the card itself, one cluster at a time, with a cluster of a filler file after every 16th (or every) cluster, and the filler deleted afterwards.
On an RP2350 (a second FatFs stack, carlk3's
no-OS-FatFS-SD-SDIO-SPI-RPi-Pico, SD over SPI at 25MHz), the same map-on/off switch against three real PC Engine CD images already on the card, freshly written so each is one fragment near the start:No measurable difference either way — within 0.1% noise. These files never trigger the pathology this PR fixes: single fragment, map near the front of the file, nothing forces a chain walk. The C6 numbers above show the same map matters once the map is far into the file (
LOWRAM_TARGET) or the file is fragmented; on a corpus without either condition, building it is free but buys nothing.Checked
bouffalo_sdk/components/fs/fatfs, R0.15) withFF_USE_FASTSEEKat 1 and at 0.f_lseekcall sites with it on. Measured on-hardware above.FR_NOT_ENOUGH_CORE, then a resize) and the fall-back pastCHD_FATFS_CLMT_MAX, on real fragmented files as above.Not covered
🤖 Generated with Claude Code