eina file - stat generation inexactness support

this is a performance optimization. it brings in a "stat generation".
for now it's disabled by default so we retain previous behavior. this
stops eina file from opening and stating a file every time you open
... it only does it if stat generation is off, or, if the generation
changed since the last time it opened that file. this makes cache hits
not have a 3 syscall cost (open+fstat+close). this optimizes that
lower end of things path. but .. it comes at a cost. if the file
changes before generation ticks over (which this forces to tick over
every time the loop exits idle by default).

now here is something to ask.

1. should we have this on by default and accept the "inexactness"
since you can eina_file_statgen_next() before any call that would do
i/o to force it to look at the real file stat info...
2. should we tick over every idle enter OR every N idle enters  or
every frame we render instead? ... i want to avoid getting a timestamp
or having a timer interrupt often... so what should we do?

at least this introduces the idea, some api's and an env var to turn
this on. it definitely cuts down syscalls during things like creation
of widdgets or objects in large batches etc.
This commit is contained in:
Carsten Haitzler 2019-07-28 11:51:15 +01:00
parent 0ff42fa4e5
commit 9b294d6284
5 changed files with 121 additions and 32 deletions

View File

@ -331,6 +331,7 @@ _ecore_main_uv_poll_cb(uv_poll_t *handle, int status, int events)
{
DBG("not IDLE anymore");
_ecore_main_uv_idling = EINA_FALSE;
eina_file_statgen_next();
efl_event_callback_call(obj, EFL_LOOP_EVENT_IDLE_EXIT, NULL);
_ecore_animator_run_reset();
}
@ -794,6 +795,7 @@ _ecore_main_gsource_dispatch(GSource *source EINA_UNUSED,
if (ecore_idling && events_ready)
{
_ecore_animator_run_reset();
eina_file_statgen_next();
efl_event_callback_call(obj, EFL_LOOP_EVENT_IDLE_EXIT, NULL);
ecore_idling = 0;
}
@ -808,6 +810,7 @@ _ecore_main_gsource_dispatch(GSource *source EINA_UNUSED,
if (ecore_fds_ready || events_ready || timers_ready)
{
_ecore_animator_run_reset();
eina_file_statgen_next();
efl_event_callback_call(obj, EFL_LOOP_EVENT_IDLE_EXIT, NULL);
ecore_idling = 0;
}
@ -867,6 +870,7 @@ _ecore_main_loop_timer_run(uv_timer_t *timer EINA_UNUSED)
if (_ecore_main_uv_idling)
{
_ecore_main_uv_idling = EINA_FALSE;
eina_file_statgen_next();
efl_event_callback_call(obj, EFL_LOOP_EVENT_IDLE_EXIT, NULL);
_ecore_animator_run_reset();
}
@ -2243,6 +2247,7 @@ _ecore_main_loop_uv_prepare(uv_prepare_t *handle EINA_UNUSED)
if (_ecore_main_uv_idling)
{
eina_file_statgen_next();
efl_event_callback_call(obj, EFL_LOOP_EVENT_IDLE_EXIT, NULL);
_ecore_animator_run_reset();
_ecore_main_uv_idling = EINA_FALSE;
@ -2473,6 +2478,7 @@ process_all: //-*********************************************************
if (!once_only)
{
_ecore_animator_run_reset(); // XXX:
eina_file_statgen_next();
efl_event_callback_call(obj, EFL_LOOP_EVENT_IDLE_EXIT, NULL);
}
// call the fd handler per fd that became alive...

View File

@ -795,46 +795,51 @@ eina_file_open(const char *path, Eina_Bool shared)
Eina_Stringshare *filename;
struct stat file_stat;
int fd = -1;
Eina_Statgen statgen;
EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
filename = eina_file_sanitize(path);
if (!filename) return NULL;
if (shared)
{
#ifdef HAVE_SHM_OPEN
fd = shm_open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO);
if ((fd != -1) && (!eina_file_close_on_exec(fd, EINA_TRUE)))
goto on_error;
#else
goto on_error;
#endif
}
else
{
#ifdef HAVE_OPEN_CLOEXEC
fd = open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO | O_CLOEXEC);
#else
fd = open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO);
if ((fd != -1) && (!eina_file_close_on_exec(fd, EINA_TRUE)))
goto on_error;
#endif
}
if (fd < 0) goto on_error;
if (fstat(fd, &file_stat))
goto on_error;
statgen = eina_file_statgen_get();
eina_lock_take(&_eina_file_lock_cache);
file = eina_hash_find(_eina_file_cache, filename);
if ((file) && !_eina_file_timestamp_compare(file, &file_stat))
statgen = eina_file_statgen_get();
if ((!file) || (file->statgen != statgen) || (statgen == 0))
{
file->delete_me = EINA_TRUE;
eina_hash_del(_eina_file_cache, file->filename, file);
file = NULL;
if (shared)
{
#ifdef HAVE_SHM_OPEN
fd = shm_open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO);
if ((fd != -1) && (!eina_file_close_on_exec(fd, EINA_TRUE)))
goto on_error;
#else
goto on_error;
#endif
}
else
{
#ifdef HAVE_OPEN_CLOEXEC
fd = open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO | O_CLOEXEC);
#else
fd = open(filename, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO);
if ((fd != -1) && (!eina_file_close_on_exec(fd, EINA_TRUE)))
goto on_error;
#endif
}
if (fd < 0) goto on_error;
if (fstat(fd, &file_stat))
goto on_error;
if (file) file->statgen = statgen;
if ((file) && !_eina_file_timestamp_compare(file, &file_stat))
{
file->delete_me = EINA_TRUE;
eina_hash_del(_eina_file_cache, file->filename, file);
file = NULL;
}
}
if (!file)
@ -874,7 +879,7 @@ eina_file_open(const char *path, Eina_Bool shared)
}
else
{
close(fd);
if (fd >= 0) close(fd);
n = file;
}
eina_lock_take(&n->lock);
@ -886,6 +891,7 @@ eina_file_open(const char *path, Eina_Bool shared)
return n;
on_error:
eina_lock_release(&_eina_file_lock_cache);
INF("Could not open file [%s].", filename);
eina_stringshare_del(filename);

View File

@ -768,6 +768,38 @@ EAPI Eina_Bool eina_file_close_on_exec(int fd, Eina_Bool on);
#include "eina_inline_file.x"
/**
* @typedef Eina_Statgen
* @brief Stat Generation count state with it being 0 when disabled or some other value that is comparable (== or !=) to a stored value and if it is not equal, then do the actual stat i/o work
* @since 1.23
*/
typedef unsigned int Eina_Statgen;
/**
* @brief Force the stat generation counter to tick over so any following i/o does real i/o and stat calls
* @since 1.23
*/
EAPI void eina_file_statgen_next(void);
/**
* @brief Get the current stat generation counter value
* @return 0 if you should always do stat calls and compare, or some other value that changes like a generation counter
* @since 1.23
*/
EAPI Eina_Statgen eina_file_statgen_get(void);
/**
* @brief Enable stat generation count optimiziing to only stat/do file i/o between generation counts changing
* @since 1.23
*/
EAPI void eina_file_statgen_enable(void);
/**
* @brief Disable stat generation count optimiziing to only stat/do file i/o between generation counts changing
* @since 1.23
*/
EAPI void eina_file_statgen_bisable(void);
/**
* @}
*/

View File

@ -74,6 +74,47 @@ Eina_Lock _eina_file_lock_cache;
# define EINA_FILE_MAGIC_CHECK(f, ...) do {} while(0)
#endif
static Eina_Spinlock _eina_statgen_lock;
static Eina_Statgen _eina_statgen = 0;
EAPI void
eina_file_statgen_next(void)
{
eina_spinlock_take(&_eina_statgen_lock);
if (_eina_statgen != 0)
{
_eina_statgen++;
if (_eina_statgen == 0) _eina_statgen = 1;
}
eina_spinlock_release(&_eina_statgen_lock);
}
EAPI Eina_Statgen
eina_file_statgen_get(void)
{
Eina_Statgen s;
eina_spinlock_take(&_eina_statgen_lock);
s = _eina_statgen;
eina_spinlock_release(&_eina_statgen_lock);
return s;
}
EAPI void
eina_file_statgen_enable(void)
{
eina_spinlock_take(&_eina_statgen_lock);
if (_eina_statgen != 0) _eina_statgen = 1;
eina_spinlock_release(&_eina_statgen_lock);
}
EAPI void
eina_file_statgen_bisable(void)
{
eina_spinlock_take(&_eina_statgen_lock);
_eina_statgen = 0;
eina_spinlock_release(&_eina_statgen_lock);
}
static char *
_eina_file_escape(char *path, size_t len)
{
@ -1072,6 +1113,8 @@ eina_file_init(void)
return EINA_FALSE;
}
if (getenv("EINA_STATGEN")) _eina_statgen = 1;
eina_spinlock_new(&_eina_statgen_lock);
eina_lock_recursive_new(&_eina_file_lock_cache);
eina_magic_string_set(EINA_FILE_MAGIC, "Eina_File");
@ -1102,6 +1145,7 @@ eina_file_shutdown(void)
eina_log_domain_unregister(_eina_file_log_dom);
_eina_file_log_dom = -1;
eina_spinlock_free(&_eina_statgen_lock);
return EINA_TRUE;
}

View File

@ -89,6 +89,7 @@ struct _Eina_File
int refcount; /**< Keeps track of references to #map. */
int global_refcount; /**< Keeps track of references to #global_map. */
Eina_Statgen statgen;/**< For inexact stats a stat gen count to rate limit syscalls to stat file */
#ifndef _WIN32
int fd; /**< The file descriptor. */