Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions contrib/rp2350/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ endif()
if(NOT DEFINED BENCH_HUNK_CAP)
set(BENCH_HUNK_CAP 0)
endif()
if(NOT DEFINED BENCH_LINKMAP)
set(BENCH_LINKMAP 1)
endif()
if(NOT DEFINED SD_BAUD_HZ)
set(SD_BAUD_HZ 25000000)
endif()
Expand Down Expand Up @@ -131,6 +134,7 @@ target_compile_definitions(rp2350-bench PRIVATE
VERIFY_BLOCK_CRC=1
LOWRAM_TARGET=${LOWRAM_TARGET_VAL}
BENCH_HUNK_CAP=${BENCH_HUNK_CAP}
BENCH_LINKMAP=${BENCH_LINKMAP}
SD_BAUD_HZ=${SD_BAUD_HZ}
MINIZ_NO_ARCHIVE_APIS
MINIZ_NO_DEFLATE_APIS
Expand Down
48 changes: 46 additions & 2 deletions contrib/rp2350/benchmark_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@
#define BENCH_CACHE_BUDGET (32 * 1024)
#endif

/* Build a FatFs cluster link map on every open (needs FF_USE_FASTSEEK). Without
* one, every backward f_lseek walks the FAT chain from the file's first
* cluster, and every COMPRESSION_SELF hunk is a backward seek. Set 0 to measure
* what the map buys. */
#ifndef BENCH_LINKMAP
#define BENCH_LINKMAP 1
#endif

/* ---- files under test; missing entries are skipped, not fatal ---- */
static const char *const g_files[] = {
"0:/roms/pcenginecd/Insanity (USA) (Unl).cue.chd",
Expand Down Expand Up @@ -127,6 +135,35 @@ static const char *basename_of(const char *p)
return s ? s + 1 : p;
}

#if FF_USE_FASTSEEK
#define BENCH_DROP_MAP(f) ((f)->cltbl = NULL)
#else
#define BENCH_DROP_MAP(f) ((void)(f))
#endif

#if BENCH_LINKMAP && FF_USE_FASTSEEK
/* Same sizing as contrib/tangcore-bl616/chd/chd_fatfs.c: start small, grow to
* what FatFs reports it needs, give up past a cap and run without. Returns
* the table's size in words, 0 if there is none. */
static DWORD *bench_map_clusters(FIL *fil)
{
DWORD *clmt = malloc(sizeof(DWORD) * 16);
FRESULT fr;
if (!clmt) return NULL;
clmt[0] = 16;
fil->cltbl = clmt;
fr = f_lseek(fil, CREATE_LINKMAP);
if (fr == FR_NOT_ENOUGH_CORE && clmt[0] <= 1024) {
DWORD need = clmt[0];
DWORD *grown = realloc(clmt, sizeof(DWORD) * need);
if (grown) { clmt = grown; clmt[0] = need; fil->cltbl = clmt; fr = f_lseek(fil, CREATE_LINKMAP); }
}
if (fr != FR_OK) { fil->cltbl = NULL; free(clmt); clmt = NULL; }
f_lseek(fil, 0);
return clmt;
}
#endif

static int run_one(const char *path)
{
sdfile sf;
Expand All @@ -138,14 +175,19 @@ static int run_one(const char *path)
printf("%-34s SKIP (not on card)\n", basename_of(path));
return 0;
}
#if BENCH_LINKMAP && FF_USE_FASTSEEK
DWORD *clmt = bench_map_clusters(&sf.fil);
#else
void *clmt = NULL;
#endif

g_base = heap_used();
g_peak = 0;

err = chd_open_core_file_callbacks(&sd_callbacks, &sf, CHD_OPEN_READ, NULL, &chd);
if (err != CHDERR_NONE) {
printf("%-34s OPEN FAILED: %s\n", basename_of(path), chd_error_string(err));
f_close(&sf.fil);
BENCH_DROP_MAP(&sf.fil); f_close(&sf.fil); free(clmt);
return 0;
}
#if BENCH_CACHE_BUDGET
Expand All @@ -170,7 +212,7 @@ static int run_one(const char *path)
unsigned char *buf = malloc(h->hunkbytes);
if (!buf) {
printf("%-34s hunk buffer alloc failed (%u B)\n", basename_of(path), h->hunkbytes);
chd_close(chd); f_close(&sf.fil);
chd_close(chd); BENCH_DROP_MAP(&sf.fil); f_close(&sf.fil); free(clmt);
return 0;
}

Expand Down Expand Up @@ -202,7 +244,9 @@ static int run_one(const char *path)

free(buf);
chd_close(chd);
BENCH_DROP_MAP(&sf.fil);
f_close(&sf.fil);
free(clmt);

if (i != n) return 0;

Expand Down
83 changes: 80 additions & 3 deletions contrib/tangcore-bl616/chd/chd_fatfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

#include "chd_fatfs.h"

#include <stdio.h> /* SEEK_SET / SEEK_CUR / SEEK_END */
#include <stdio.h> /* SEEK_SET / SEEK_CUR / SEEK_END */
#include <stdlib.h> /* malloc / realloc / free */

static uint64_t chd_fatfs_fsize(void *argp)
{
Expand All @@ -31,10 +32,22 @@ static size_t chd_fatfs_fread(void *ptr, size_t size, size_t nmemb, void *argp)
return (size_t)(br / size);
}

/* libchdr calls this once, from chd_close() or from a failed open; the link
* map goes with the file. */
static int chd_fatfs_fclose(void *argp)
{
FIL *fil = (FIL *)argp;
return (f_close(fil) == FR_OK) ? 0 : -1;
FRESULT fr;
#if FF_USE_FASTSEEK
DWORD *clmt = fil->cltbl;

fil->cltbl = NULL;
#endif
fr = f_close(fil);
#if FF_USE_FASTSEEK
free(clmt);
#endif
return (fr == FR_OK) ? 0 : -1;
}

static int chd_fatfs_fseek(void *argp, int64_t offset, int whence)
Expand All @@ -61,6 +74,64 @@ static int chd_fatfs_fseek(void *argp, int64_t offset, int whence)
return (f_lseek(fil, abs_offset) == FR_OK) ? 0 : -1;
}

#if FF_USE_FASTSEEK
/* Without a cluster link map, every backward f_lseek walks the FAT chain from
* the file's first cluster again - and every COMPRESSION_SELF hunk is a
* backward seek, to an earlier hunk the file 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 map was built. FF_USE_FASTSEEK
* provides the map, but f_open does not build one - the caller has to.
*
* The table needs two words per fragment plus one. A freshly written card
* holds each file as one fragment, so start small and grow only to what
* FatFs reports it needs, capped so a pathologically fragmented file costs at
* most CHD_FATFS_CLMT_MAX words and otherwise just runs without the map. */
#ifndef CHD_FATFS_CLMT_FIRST
#define CHD_FATFS_CLMT_FIRST 16
#endif
#ifndef CHD_FATFS_CLMT_MAX
#define CHD_FATFS_CLMT_MAX 1024
#endif

static void chd_fatfs_map_clusters(FIL *fil)
{
DWORD *clmt = (DWORD *)malloc(sizeof(DWORD) * CHD_FATFS_CLMT_FIRST);
FRESULT fr;

if (clmt == NULL)
return;

clmt[0] = CHD_FATFS_CLMT_FIRST;
fil->cltbl = clmt;
fr = f_lseek(fil, CREATE_LINKMAP);

/* on FR_NOT_ENOUGH_CORE, FatFs leaves the size it needs in clmt[0] */
if (fr == FR_NOT_ENOUGH_CORE && clmt[0] <= CHD_FATFS_CLMT_MAX)
{
DWORD need = clmt[0];
DWORD *grown = (DWORD *)realloc(clmt, sizeof(DWORD) * need);

if (grown != NULL)
{
clmt = grown;
clmt[0] = need;
fil->cltbl = clmt;
fr = f_lseek(fil, CREATE_LINKMAP);
}
}

if (fr != FR_OK)
{
/* no map: slower backward seeks, but correct */
fil->cltbl = NULL;
free(clmt);
}

f_lseek(fil, 0);
}
#endif

const core_file_callbacks chd_fatfs_callbacks = {
.fsize = chd_fatfs_fsize,
.fread = chd_fatfs_fread,
Expand All @@ -75,7 +146,13 @@ chd_error chd_fatfs_open(const char *path, FIL *fil, chd_file **chd)
if (f_open(fil, path, FA_READ) != FR_OK)
return CHDERR_FILE_NOT_FOUND;

/* on failure libchdr has already closed fil through the callback */
#if FF_USE_FASTSEEK
/* before chd_open, so the header and map reads benefit too */
chd_fatfs_map_clusters(fil);
#endif

/* on failure libchdr has already closed fil through the callback, which
* also frees the link map */
err = chd_open_core_file_callbacks(&chd_fatfs_callbacks, fil, CHD_OPEN_READ, NULL, chd);
if (err != CHDERR_NONE)
return err;
Expand Down