Low-hanging fruit mod loading optimizations (#493)

* Low-hanging fruit mod loading optimizations

* Fix DynOS fread spam and don't use a linked list for mod cache (still O(n^2), but much faster)
This commit is contained in:
Khangaroo 2024-11-15 21:44:57 -05:00 committed by GitHub
parent 15eb8dd8fc
commit 693a77078d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 89 additions and 70 deletions

View file

@ -36,14 +36,18 @@ char *DynOS_Read_Buffer(FILE* aFile, GfxData* aGfxData) {
aGfxData->mModelIdentifier = (u32) _Length; aGfxData->mModelIdentifier = (u32) _Length;
} }
// Remove comments char *_OrigFileBuffer = New<char>(_Length + 1);
char *pOrigFileBuffer = _OrigFileBuffer;
rewind(aFile); rewind(aFile);
_OrigFileBuffer[fread(_OrigFileBuffer, 1, _Length, aFile)] = 0;
// Remove comments
char *_FileBuffer = New<char>(_Length + 1); char *_FileBuffer = New<char>(_Length + 1);
char *pFileBuffer = _FileBuffer; char *pFileBuffer = _FileBuffer;
char _Previous = 0; char _Previous = 0;
char _Current = 0; char _Current = 0;
s32 _CommentType = 0; s32 _CommentType = 0;
while (fread(&_Current, 1, 1, aFile)) { while ((_Current = *pOrigFileBuffer++)) {
if (_CommentType == COMMENT_NONE) { if (_CommentType == COMMENT_NONE) {
if (_Current == '/') { if (_Current == '/') {
_CommentType = COMMENT_START; _CommentType = COMMENT_START;
@ -79,6 +83,7 @@ char *DynOS_Read_Buffer(FILE* aFile, GfxData* aGfxData) {
_Previous = _Current; _Previous = _Current;
} }
*(pFileBuffer++) = 0; *(pFileBuffer++) = 0;
Delete(_OrigFileBuffer);
// Remove ifdef blocks // Remove ifdef blocks
// Doesn't support nested blocks // Doesn't support nested blocks

View file

@ -11,6 +11,7 @@
#include <ctype.h> #include <ctype.h>
#ifdef _WIN32 #ifdef _WIN32
#include <direct.h> #include <direct.h>
#include <fileapi.h>
#endif #endif
#include "macros.h" #include "macros.h"
@ -292,18 +293,32 @@ bool fs_sys_filename_is_portable(char const *filename) {
/* these operate on the real file system */ /* these operate on the real file system */
bool fs_sys_path_exists(const char *name) { bool fs_sys_path_exists(const char *name) {
#ifdef _WIN32
return GetFileAttributesA(name) != INVALID_FILE_ATTRIBUTES;
#else
struct stat st; struct stat st;
return (stat(name, &st) == 0); return (stat(name, &st) == 0);
#endif
} }
bool fs_sys_file_exists(const char *name) { bool fs_sys_file_exists(const char *name) {
#ifdef _WIN32
DWORD attribs = GetFileAttributesA(name);
return attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY);
#else
struct stat st; struct stat st;
return (stat(name, &st) == 0 && S_ISREG(st.st_mode)); return (stat(name, &st) == 0 && S_ISREG(st.st_mode));
#endif
} }
bool fs_sys_dir_exists(const char *name) { bool fs_sys_dir_exists(const char *name) {
#ifdef _WIN32
DWORD attribs = GetFileAttributesA(name);
return attribs != INVALID_FILE_ATTRIBUTES && (attribs & FILE_ATTRIBUTE_DIRECTORY);
#else
struct stat st; struct stat st;
return (stat(name, &st) == 0 && S_ISDIR(st.st_mode)); return (stat(name, &st) == 0 && S_ISDIR(st.st_mode));
#endif
} }
bool fs_sys_dir_is_empty(const char *name) { bool fs_sys_dir_is_empty(const char *name) {

View file

@ -204,18 +204,22 @@ void mod_clear(struct Mod* mod) {
} }
mod->fileCount = 0; mod->fileCount = 0;
mod->fileCapacity = 0;
mod->size = 0; mod->size = 0;
free(mod); free(mod);
} }
static struct ModFile* mod_allocate_file(struct Mod* mod, char* relativePath) { static struct ModFile* mod_allocate_file(struct Mod* mod, char* relativePath) {
// actual allocation // actual allocation
u16 fileIndex = mod->fileCount++; if (mod->fileCount == mod->fileCapacity) {
mod->files = realloc(mod->files, sizeof(struct ModFile) * mod->fileCount); mod->fileCapacity = (mod->fileCapacity == 0) ? 16 : (mod->fileCapacity * 2);
if (mod->files == NULL) { mod->files = realloc(mod->files, sizeof(struct ModFile) * mod->fileCapacity);
LOG_ERROR("Failed to allocate file: '%s'", relativePath); if (mod->files == NULL) {
return NULL; LOG_ERROR("Failed to allocate file: '%s'", relativePath);
return NULL;
}
} }
u16 fileIndex = mod->fileCount++;
// clear memory // clear memory
struct ModFile* file = &mod->files[fileIndex]; struct ModFile* file = &mod->files[fileIndex];

View file

@ -30,6 +30,7 @@ struct Mod {
struct ModFile* files; struct ModFile* files;
s32 index; s32 index;
u16 fileCount; u16 fileCount;
u16 fileCapacity;
bool isDirectory; bool isDirectory;
bool enabled; bool enabled;
bool selectable; bool selectable;

View file

@ -15,25 +15,26 @@
#define MOD_CACHE_VERSION 7 #define MOD_CACHE_VERSION 7
#define MD5_BUFFER_SIZE 1024 #define MD5_BUFFER_SIZE 1024
struct ModCacheEntry* sModCacheHead = NULL; static struct ModCacheEntry* sModCacheEntries = NULL;
static size_t sModCacheLength = 0;
static size_t sModLengthCapacity = 0;
static void mod_cache_remove_node(struct ModCacheEntry* node, struct ModCacheEntry* parent) { static void mod_cache_remove_node(struct ModCacheEntry* node) {
if (node == NULL) { return; }
if (node == sModCacheHead) { sModCacheHead = node->next; }
if (parent != NULL) { parent->next = node->next; }
//LOG_INFO("Removing node: %s", node->path);
if (node->path) { if (node->path) {
free(node->path); free(node->path);
node->path = NULL; node->path = NULL;
} }
free(node); if (node != &sModCacheEntries[sModCacheLength - 1])
memcpy(node, &sModCacheEntries[sModCacheLength - 1], sizeof(struct ModCacheEntry));
sModCacheLength--;
} }
void mod_cache_shutdown(void) { void mod_cache_shutdown(void) {
LOG_INFO("Shutting down mod cache."); LOG_INFO("Shutting down mod cache.");
while (sModCacheHead) { sModCacheLength = 0;
mod_cache_remove_node(sModCacheHead, NULL); sModLengthCapacity = 0;
} free(sModCacheEntries);
sModCacheEntries = NULL;
} }
void mod_cache_md5(const char* inPath, u8* outDataPath) { void mod_cache_md5(const char* inPath, u8* outDataPath) {
@ -77,6 +78,15 @@ void mod_cache_md5(const char* inPath, u8* outDataPath) {
MD5_Final(outDataPath, &ctx); MD5_Final(outDataPath, &ctx);
} }
static u64 mod_cache_fnv1a(const char* str) {
u64 hash = 0xCBF29CE484222325;
while (*str) {
hash *= 0x100000001B3;
hash ^= *str++;
}
return hash;
}
static bool mod_cache_is_valid(struct ModCacheEntry* node) { static bool mod_cache_is_valid(struct ModCacheEntry* node) {
if (node == NULL || node->path == NULL || strlen(node->path) == 0) { if (node == NULL || node->path == NULL || strlen(node->path) == 0) {
return false; return false;
@ -88,42 +98,37 @@ static bool mod_cache_is_valid(struct ModCacheEntry* node) {
struct ModCacheEntry* mod_cache_get_from_hash(u8* dataHash) { struct ModCacheEntry* mod_cache_get_from_hash(u8* dataHash) {
if (dataHash == NULL) { return NULL; } if (dataHash == NULL) { return NULL; }
struct ModCacheEntry* node = sModCacheHead; for (size_t i = 0; i < sModCacheLength;) {
struct ModCacheEntry* prev = NULL; struct ModCacheEntry* node = &sModCacheEntries[i];
while (node != NULL) {
struct ModCacheEntry* next = node->next;
if (!memcmp(node->dataHash, dataHash, 16)) { if (!memcmp(node->dataHash, dataHash, 16)) {
if (mod_cache_is_valid(node)) { if (mod_cache_is_valid(node)) {
return node; return node;
} else { } else {
mod_cache_remove_node(node, prev); mod_cache_remove_node(node);
node = prev; continue;
} }
} }
prev = node; i++;
node = next;
} }
return NULL; return NULL;
} }
struct ModCacheEntry* mod_cache_get_from_path(const char* path, bool validate) { struct ModCacheEntry* mod_cache_get_from_path(const char* path, bool validate) {
if (path == NULL || strlen(path) == 0) { return NULL; } if (path == NULL || strlen(path) == 0) { return NULL; }
struct ModCacheEntry* node = sModCacheHead; u64 pathHash = mod_cache_fnv1a(path);
struct ModCacheEntry* prev = NULL; for (size_t i = 0; i < sModCacheLength;) {
while (node != NULL) { struct ModCacheEntry* node = &sModCacheEntries[i];
struct ModCacheEntry* next = node->next; if (node->pathHash == pathHash && !strcmp(node->path, path)) {
if (!strcmp(node->path, path)) {
if (!validate) { if (!validate) {
return node; return node;
} else if (mod_cache_is_valid(node)) { } else if (mod_cache_is_valid(node)) {
return node; return node;
} else { } else {
mod_cache_remove_node(node, prev); mod_cache_remove_node(node);
node = prev; continue;
} }
} }
prev = node; i++;
node = next;
} }
return NULL; return NULL;
} }
@ -143,6 +148,7 @@ void mod_cache_add_internal(u8* dataHash, u64 lastLoaded, char* inPath) {
return; return;
} }
normalize_path((char*)path); normalize_path((char*)path);
u64 pathHash = mod_cache_fnv1a(path);
bool foundNonZero = false; bool foundNonZero = false;
for (u8 i = 0; i < 16; i++) { for (u8 i = 0; i < 16; i++) {
@ -157,43 +163,34 @@ void mod_cache_add_internal(u8* dataHash, u64 lastLoaded, char* inPath) {
return; return;
} }
struct ModCacheEntry* node = calloc(1, sizeof(struct ModCacheEntry)); if (sModCacheEntries == NULL) {
memcpy(node->dataHash, dataHash, sizeof(u8) * 16); sModLengthCapacity = 16;
if (lastLoaded == 0) { lastLoaded = clock(); } sModCacheLength = 0;
node->lastLoaded = lastLoaded; sModCacheEntries = calloc(sModLengthCapacity, sizeof(struct ModCacheEntry));
node->path = (char*)path; } else if (sModCacheLength == sModLengthCapacity) {
node->next = NULL; sModLengthCapacity *= 2;
sModCacheEntries = realloc(sModCacheEntries, sizeof(struct ModCacheEntry) * sModLengthCapacity);
if (sModCacheHead == NULL) {
sModCacheHead = node;
LOG_INFO("Added head: %s", node->path);
return;
} }
struct ModCacheEntry* n = sModCacheHead; struct ModCacheEntry node = {};
struct ModCacheEntry* prev = NULL; memcpy(node.dataHash, dataHash, sizeof(u8) * 16);
while (n != NULL) { if (lastLoaded == 0) { lastLoaded = clock(); }
struct ModCacheEntry* next = n->next; node.lastLoaded = lastLoaded;
node.path = (char*)path;
node.pathHash = pathHash;
// found end of list, add it for (size_t i = 0; i < sModCacheLength;) {
if (next == NULL) { struct ModCacheEntry* n = &sModCacheEntries[i];
LOG_INFO("Added node: %s", node->path);
if (n != node) { n->next = node; }
return;
}
// found old hash, remove it // found old hash, remove it
if (!strcmp(n->path, path)) { if (n->pathHash == pathHash && !strcmp(n->path, path)) {
LOG_INFO("Removing old node: %s", node->path); LOG_INFO("Removing old node: %s", node->path);
mod_cache_remove_node(n, prev); mod_cache_remove_node(n);
} else { } else {
prev = n; i++;
} }
n = next;
} }
memcpy(&sModCacheEntries[sModCacheLength++], &node, sizeof(node));
LOG_ERROR("Did not add node for some reason?");
} }
void mod_cache_add(struct Mod* mod, struct ModFile* file, bool useFilePath) { void mod_cache_add(struct Mod* mod, struct ModFile* file, bool useFilePath) {
@ -329,19 +326,16 @@ void mod_cache_save(void) {
u8 t = *gBehaviorOffset != 0; u8 t = *gBehaviorOffset != 0;
fwrite(&t, sizeof(u8), 1, fp); fwrite(&t, sizeof(u8), 1, fp);
struct ModCacheEntry* node = sModCacheHead; for (size_t i = 0; i < sModCacheLength; i++) {
while (node != NULL) { struct ModCacheEntry* node = &sModCacheEntries[i];
struct ModCacheEntry* next = node->next; if (node->path == NULL) { continue; }
if (node->path == NULL) { goto iterate; }
u16 pathLen = strlen(node->path); u16 pathLen = strlen(node->path);
if (pathLen == 0) { goto iterate; } if (pathLen == 0) { continue; }
fwrite(node->dataHash, sizeof(u8), 16, fp); fwrite(node->dataHash, sizeof(u8), 16, fp);
fwrite(&node->lastLoaded, sizeof(u64), 1, fp); fwrite(&node->lastLoaded, sizeof(u64), 1, fp);
fwrite(&pathLen, sizeof(u16), 1, fp); fwrite(&pathLen, sizeof(u16), 1, fp);
fwrite(node->path, sizeof(u8), pathLen + 1, fp); fwrite(node->path, sizeof(u8), pathLen + 1, fp);
iterate:
node = next;
} }
fclose(fp); fclose(fp);

View file

@ -7,7 +7,7 @@ struct ModCacheEntry {
u8 dataHash[16]; u8 dataHash[16];
u64 lastLoaded; u64 lastLoaded;
char* path; char* path;
struct ModCacheEntry* next; u64 pathHash;
}; };
void mod_cache_md5(const char* inPath, u8* outDataPath); void mod_cache_md5(const char* inPath, u8* outDataPath);