input: Cache parsed mappings.h

Parsing this file was taking the most time out of glfwInit(), or about
14ms on my PinePhone before the start of this series.  In the case where
the string is already in cache, it now takes only 0.95ms.

It would be possible to make that even faster, and additionally to keep
that page shared with other GLFW processes, by using mmap() instead, but
for now I opted to not change the memory representation.
This commit is contained in:
Emmanuel Gil Peyrot 2021-03-21 13:43:25 +01:00
parent acee7eadd7
commit 04922c63de
6 changed files with 312 additions and 0 deletions

View File

@ -218,6 +218,16 @@ if (_GLFW_WAYLAND)
endif()
endif()
# TODO: Move that elsewhere, as other platforms would equally benefit from it.
if (_GLFW_X11 OR _GLFW_WAYLAND)
include(FindPkgConfig)
pkg_check_modules(XXHash3 libxxhash)
if (XXHash3_FOUND)
list(APPEND glfw_INCLUDE_DIRS "${XXHash3_INCLUDE_DIRS}")
set(HAVE_XXHASH TRUE)
endif()
endif()
#--------------------------------------------------------------------
# Use Cocoa for window creation and NSOpenGL for context creation
#--------------------------------------------------------------------

View File

@ -4,6 +4,10 @@ add_library(glfw "${GLFW_SOURCE_DIR}/include/GLFW/glfw3.h"
internal.h mappings.h context.c init.c input.c monitor.c
vulkan.c window.c)
if (HAVE_XXHASH)
target_sources(glfw PRIVATE cache.c cache.h)
endif()
if (_GLFW_COCOA)
target_sources(glfw PRIVATE cocoa_platform.h cocoa_joystick.h posix_thread.h
nsgl_context.h egl_context.h osmesa_context.h

223
src/cache.c Normal file
View File

@ -0,0 +1,223 @@
//========================================================================
// GLFW 3.4 - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2021 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//
//========================================================================
// Please use C89 style variable declarations in this file because VS 2010
//========================================================================
#define _XOPEN_SOURCE 500
#include "cache.h"
#ifndef HAVE_XXHASH
#error "libxxhash is required for the compose cache"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#define XXH_INLINE_ALL
#include <xxhash.h>
static GLFWbool
hashString(const char *string, size_t len, XXH128_canonical_t *out)
{
XXH3_state_t *state;
XXH_errorcode err;
XXH128_hash_t hash;
state = XXH3_createState();
if (!state)
return GLFW_FALSE;
err = XXH3_128bits_reset(state);
if (err != XXH_OK)
{
XXH3_freeState(state);
return GLFW_FALSE;
}
err = XXH3_128bits_update(state, string, len);
if (err != XXH_OK)
{
XXH3_freeState(state);
return GLFW_FALSE;
}
hash = XXH3_128bits_digest(state);
XXH3_freeState(state);
XXH128_canonicalFromHash(out, hash);
return GLFW_TRUE;
}
GLFWbool
cacheGetPathFromString(const char *string, char **cachePath)
{
const char *home, *xdg;
char *path;
int length;
char *hash_part;
XXH128_canonical_t hash;
/* Mostly copied over from xkb_context_include_path_append_default(). */
home = getenv("HOME");
xdg = getenv("XDG_CACHE_HOME");
if (xdg != NULL)
{
length = strlen(xdg) + strlen("/glfw/") + 32 + 1;
path = malloc(length);
sprintf(path, "%s/glfw/", xdg);
}
else if (home != NULL)
{
/* XDG_CACHE_HOME fallback is $HOME/.cache/ */
length = strlen(home) + strlen("/.cache/glfw/") + 32 + 1;
path = malloc(length);
sprintf(path, "%s/.cache/glfw/", home);
}
else
{
return GLFW_FALSE;
}
if (!hashString(string, strlen(string), &hash))
{
free(path);
return GLFW_FALSE;
}
hash_part = strrchr(path, '/') + 1;
*hash_part = '\0';
mkdir(path, 0755);
for (int i = 0; i < 16; i++)
{
sprintf(hash_part, "%2.2x", hash.digest[i]);
hash_part += 2;
}
*cachePath = path;
return GLFW_TRUE;
}
GLFWbool
cacheRead(const char *path, _GLFWmapping** out, int* count)
{
FILE *f;
int length, i;
_GLFWmapping* mappings;
_GLFWmapping* mapping;
#define FREAD(ptr, size, nmemb, stream) \
do \
{ \
if (fread(ptr, size, nmemb, stream) < nmemb) \
return GLFW_FALSE; \
} while (0)
#define FREAD_n(ptr, nmemb, stream) \
FREAD(ptr, sizeof(*ptr), nmemb, stream)
#define FREAD_1(value, stream) \
FREAD_n(&value, 1, stream)
f = fopen(path, "rb");
if (!f)
return GLFW_FALSE;
FREAD_1(length, f);
mappings = malloc(length * sizeof(_GLFWmapping));
if (!mappings)
goto error;
for (i = 0; i < length; ++i)
{
mapping = &mappings[i];
FREAD_n(mapping->name, 128, f);
FREAD_n(mapping->guid, 32, f);
mapping->guid[32] = '\0';
FREAD_n(mapping->buttons, 15, f);
FREAD_n(mapping->axes, 6, f);
}
#undef FREAD_1
#undef FREAD_n
#undef FREAD
fclose(f);
*count = length;
*out = mappings;
return GLFW_TRUE;
error:
fclose(f);
unlink(path);
return GLFW_FALSE;
}
void
cacheWrite(const char *path, const _GLFWmapping* mappings, int count)
{
FILE *f;
int i;
const _GLFWmapping* mapping;
#define FWRITE(ptr, size, nmemb, stream) \
do \
{ \
if (fwrite(ptr, size, nmemb, stream) < nmemb) \
goto error; \
} while (0)
#define FWRITE_n(ptr, nmemb, stream) \
FWRITE(ptr, sizeof(*ptr), nmemb, stream)
#define FWRITE_1(value, stream) \
FWRITE_n(&value, 1, stream)
f = fopen(path, "wb");
if (!f)
return;
FWRITE_1(count, f);
for (i = 0; i < count; ++i) {
mapping = &mappings[i];
FWRITE_n(mapping->name, 128, f);
FWRITE_n(mapping->guid, 32, f);
FWRITE_n(mapping->buttons, 15, f);
FWRITE_n(mapping->axes, 6, f);
}
if (fclose(f) != 0)
goto fclose_error;
return;
#undef FWRITE_1
#undef FWRITE_n
#undef FWRITE
error:
fclose(f);
fclose_error:
unlink(path);
}

44
src/cache.h Normal file
View File

@ -0,0 +1,44 @@
//========================================================================
// GLFW 3.4 - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2021 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//
//========================================================================
// Please use C89 style variable declarations in this file because VS 2010
//========================================================================
#pragma once
#include "internal.h"
#ifndef HAVE_XXHASH
#error "libxxhash is required for the compose cache"
#endif
GLFWbool
cacheGetPathFromString(const char *string, char **cachePath);
GLFWbool
cacheRead(const char *path, _GLFWmapping** out, int* count);
void
cacheWrite(const char *path, const _GLFWmapping* mappings, int count);

View File

@ -55,4 +55,6 @@
#cmakedefine HAVE_XKBCOMMON_COMPOSE_H
// Define this to 1 if the libc supports memfd_create()
#cmakedefine HAVE_MEMFD_CREATE
// Define this to 1 if xxhash has been found at build time
#cmakedefine HAVE_XXHASH

View File

@ -35,6 +35,10 @@
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_XXHASH
#include "cache.h"
#endif
// Internal key state used for sticky keys
#define _GLFW_STICK 3
@ -1158,11 +1162,25 @@ GLFWAPI int glfwUpdateGamepadMappings(const char* string)
{
int jid;
const char* c = string;
#ifdef HAVE_XXHASH
char *cachePath;
GLFWbool usableCache, readFromCache = GLFW_FALSE;
#endif
assert(string != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
#ifdef HAVE_XXHASH
usableCache = cacheGetPathFromString(string, &cachePath);
if (usableCache && cacheRead(cachePath, &_glfw.mappings, &_glfw.mappingCount))
{
readFromCache = GLFW_TRUE;
goto doMapping;
}
#endif
while (*c)
{
if ((*c >= '0' && *c <= '9') ||
@ -1204,6 +1222,10 @@ GLFWAPI int glfwUpdateGamepadMappings(const char* string)
}
}
#ifdef HAVE_XXHASH
doMapping:
#endif
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{
_GLFWjoystick* js = _glfw.joysticks + jid;
@ -1211,6 +1233,13 @@ GLFWAPI int glfwUpdateGamepadMappings(const char* string)
js->mapping = findValidMapping(js);
}
#ifdef HAVE_XXHASH
if (usableCache && !readFromCache)
cacheWrite(cachePath, _glfw.mappings, _glfw.mappingCount);
free(cachePath);
#endif
return GLFW_TRUE;
}