mirror of
https://git.pwmt.org/pwmt/zathura.git
synced 2024-12-29 15:45:59 +01:00
Replace the periodic page reclaiming code with a LRU caching algorithm.
This patch implements a page cache that is invalidated in a LRU fashion. Pages are added to the cache as soon as they become visible. When the cache is full and a new page that isn't in the cache becomes visible, the least recently viewed page in the cache is evicted from memory and the new one takes it's place. The cache size is configurable using the page-cache-size configuration variable, with a default value of 15 pages. Very large values for the cache size are not recommended, though, as it will stress the system memory out. The old periodic page reclaiming code is no longer necessary with this patch, so I removed it. Special thanks to Ignas Anikevičius, and Sebastian Ramacher for the inspirations. Signed-off-by: Sebastian Ramacher <sebastian+dev@ramacher.at>
This commit is contained in:
parent
cd3314b490
commit
cb18fe8603
8 changed files with 163 additions and 72 deletions
|
@ -95,6 +95,8 @@ cb_view_vadjustment_value_changed(GtkAdjustment* GIRARA_UNUSED(adjustment), gpoi
|
|||
|
||||
if (gdk_rectangle_intersect(&view_rect, &page_rect, NULL) == TRUE) {
|
||||
zathura_page_set_visibility(page, true);
|
||||
zathura_page_widget_update_view_time(ZATHURA_PAGE(page_widget));
|
||||
zathura_page_cache_add(zathura, zathura_page_get_index(page));
|
||||
if (zathura->global.update_page_number == true && updated == false
|
||||
&& gdk_rectangle_intersect(¢er, &page_rect, NULL) == TRUE) {
|
||||
zathura_document_set_current_page_number(zathura->document, page_id);
|
||||
|
@ -103,7 +105,6 @@ cb_view_vadjustment_value_changed(GtkAdjustment* GIRARA_UNUSED(adjustment), gpoi
|
|||
} else {
|
||||
zathura_page_set_visibility(page, false);
|
||||
}
|
||||
zathura_page_widget_update_view_time(ZATHURA_PAGE(page_widget));
|
||||
}
|
||||
|
||||
statusbar_page_number_update(zathura);
|
||||
|
|
5
config.c
5
config.c
|
@ -155,9 +155,8 @@ config_load_default(zathura_t* zathura)
|
|||
girara_setting_add(gsession, "zoom-min", &int_value, INT, false, _("Zoom minimum"), NULL, NULL);
|
||||
int_value = 1000;
|
||||
girara_setting_add(gsession, "zoom-max", &int_value, INT, false, _("Zoom maximum"), NULL, NULL);
|
||||
int_value = 20;
|
||||
girara_setting_add(gsession, "page-store-threshold", &int_value, INT, false, _("Life time (in seconds) of a hidden page"), NULL, NULL);
|
||||
girara_setting_add(gsession, "page-store-interval", &int_value, INT, true, _("Amount of seconds between each cache purge"), NULL, NULL);
|
||||
int_value = ZATHURA_PAGE_CACHE_DEFAULT_SIZE;
|
||||
girara_setting_add(gsession, "page-cache-size", &int_value, INT, true, _("Maximum number of pages to keep in the cache"), NULL, NULL);
|
||||
int_value = 20;
|
||||
girara_setting_add(gsession, "jumplist-size", &int_value, INT, false, _("Number of positions to remember in the jumplist"), cb_jumplist_change, NULL);
|
||||
|
||||
|
|
|
@ -860,19 +860,3 @@ zathura_page_widget_update_view_time(ZathuraPage* widget)
|
|||
priv->last_view = g_get_real_time();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
zathura_page_widget_purge_unused(ZathuraPage* widget, gint64 threshold)
|
||||
{
|
||||
g_return_if_fail(ZATHURA_IS_PAGE(widget) == TRUE);
|
||||
zathura_page_widget_private_t* priv = ZATHURA_PAGE_GET_PRIVATE(widget);
|
||||
if (zathura_page_get_visibility(priv->page) == true || priv->surface == NULL || threshold <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const gint64 now = g_get_real_time();
|
||||
if (now - priv->last_view >= threshold * G_USEC_PER_SEC) {
|
||||
girara_debug("purge page %d from cache (unseen for %f seconds)", zathura_page_get_index(priv->page), ((double)now - priv->last_view) / G_USEC_PER_SEC);
|
||||
zathura_page_widget_update_surface(widget, NULL);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,12 +88,4 @@ zathura_link_t* zathura_page_widget_link_get(ZathuraPage* widget, unsigned int i
|
|||
*/
|
||||
void zathura_page_widget_update_view_time(ZathuraPage* widget);
|
||||
|
||||
/**
|
||||
* If the page has not been viewed for some time, purge the surface.
|
||||
*
|
||||
* @param widget the widget
|
||||
* @param threshold the threshold (in seconds)
|
||||
*/
|
||||
void zathura_page_widget_purge_unused(ZathuraPage* widget, gint64 threshold);
|
||||
|
||||
#endif
|
||||
|
|
4
render.c
4
render.c
|
@ -33,9 +33,9 @@ render_job(void* data, void* user_data)
|
|||
return;
|
||||
}
|
||||
|
||||
girara_debug("rendering page %d ...", zathura_page_get_index(page));
|
||||
girara_debug("rendering page %d ...", zathura_page_get_index(page) + 1);
|
||||
if (render(zathura, page) != true) {
|
||||
girara_error("Rendering failed (page %d)\n", zathura_page_get_index(page));
|
||||
girara_error("Rendering failed (page %d)\n", zathura_page_get_index(page) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
161
zathura.c
161
zathura.c
|
@ -53,7 +53,10 @@ typedef struct position_set_delayed_s {
|
|||
} position_set_delayed_t;
|
||||
|
||||
static gboolean document_info_open(gpointer data);
|
||||
static gboolean purge_pages(gpointer data);
|
||||
static bool zathura_page_cache_is_cached(zathura_t* zathura, unsigned int page_index);
|
||||
static ssize_t zathura_page_cache_lru_invalidate(zathura_t* zathura);
|
||||
static void zathura_page_cache_invalidate_all(zathura_t* zathura);
|
||||
static bool zathura_page_cache_is_full(zathura_t* zathura, bool* result);
|
||||
|
||||
/* function implementation */
|
||||
zathura_t*
|
||||
|
@ -264,11 +267,6 @@ zathura_init(zathura_t* zathura)
|
|||
zathura->bookmarks.bookmarks = girara_sorted_list_new2((girara_compare_function_t) zathura_bookmarks_compare,
|
||||
(girara_free_function_t) zathura_bookmark_free);
|
||||
|
||||
/* add even to purge old pages */
|
||||
int interval = 30;
|
||||
girara_setting_get(zathura->ui.session, "page-store-interval", &interval);
|
||||
g_timeout_add_seconds(interval, purge_pages, zathura);
|
||||
|
||||
/* jumplist */
|
||||
|
||||
zathura->jumplist.max_size = 20;
|
||||
|
@ -279,6 +277,14 @@ zathura_init(zathura_t* zathura)
|
|||
zathura->jumplist.cur = NULL;
|
||||
zathura_jumplist_append_jump(zathura);
|
||||
zathura->jumplist.cur = girara_list_iterator(zathura->jumplist.list);
|
||||
|
||||
/* page cache */
|
||||
|
||||
zathura->page_cache.size = ZATHURA_PAGE_CACHE_DEFAULT_SIZE;
|
||||
girara_setting_get(zathura->ui.session, "page-cache-size", &zathura->page_cache.size);
|
||||
zathura->page_cache.cache = g_malloc(zathura->page_cache.size * sizeof(int));
|
||||
zathura_page_cache_invalidate_all(zathura);
|
||||
|
||||
return true;
|
||||
|
||||
error_free:
|
||||
|
@ -353,6 +359,8 @@ zathura_free(zathura_t* zathura)
|
|||
girara_list_iterator_free(zathura->jumplist.cur);
|
||||
}
|
||||
|
||||
g_free(zathura->page_cache.cache);
|
||||
|
||||
g_free(zathura);
|
||||
}
|
||||
|
||||
|
@ -775,6 +783,9 @@ document_open(zathura_t* zathura, const char* path, const char* password,
|
|||
cb_view_vadjustment_value_changed(NULL, zathura);
|
||||
}
|
||||
|
||||
/* Invalidate all current entries in the page cache */
|
||||
zathura_page_cache_invalidate_all(zathura);
|
||||
|
||||
return true;
|
||||
|
||||
error_free:
|
||||
|
@ -1066,30 +1077,6 @@ page_widget_set_mode(zathura_t* zathura, unsigned int pages_per_row, unsigned in
|
|||
gtk_widget_show_all(zathura->ui.page_widget);
|
||||
}
|
||||
|
||||
static
|
||||
gboolean purge_pages(gpointer data)
|
||||
{
|
||||
zathura_t* zathura = data;
|
||||
if (zathura == NULL || zathura->document == NULL) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int threshold = 0;
|
||||
girara_setting_get(zathura->ui.session, "page-store-threshold", &threshold);
|
||||
if (threshold <= 0) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
girara_debug("purging pages ...");
|
||||
unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
|
||||
for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
|
||||
zathura_page_t* page = zathura_document_get_page(zathura->document, page_id);
|
||||
GtkWidget* page_widget = zathura_page_get_widget(zathura, page);
|
||||
zathura_page_widget_purge_unused(ZATHURA_PAGE(page_widget), threshold);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
position_set_delayed_impl(gpointer data)
|
||||
{
|
||||
|
@ -1215,3 +1202,117 @@ zathura_jumplist_save(zathura_t* zathura)
|
|||
cur->y = gtk_adjustment_get_value(view_vadjustment) / zathura_document_get_scale(zathura->document);;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
zathura_page_cache_is_cached(zathura_t* zathura, unsigned int page_index)
|
||||
{
|
||||
g_return_val_if_fail(zathura != NULL, false);
|
||||
|
||||
unsigned int i;
|
||||
|
||||
if (zathura->page_cache.num_cached_pages != 0) {
|
||||
for (i = 0; i < zathura->page_cache.size; ++i) {
|
||||
if (zathura->page_cache.cache[i] >= 0 && page_index == (unsigned int)zathura->page_cache.cache[i]) {
|
||||
girara_debug("Page %d is a cache hit", page_index + 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
girara_debug("Page %d is a cache miss", page_index + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
zathura_page_cache_lru_invalidate(zathura_t* zathura)
|
||||
{
|
||||
g_return_val_if_fail(zathura != NULL, -1);
|
||||
|
||||
ssize_t lru_index = 0;
|
||||
guint64 view_time = 0;
|
||||
guint64 lru_view_time = G_MAXUINT64;
|
||||
GtkWidget* page_widget;
|
||||
|
||||
for (unsigned int i = 0; i < zathura->page_cache.size; ++i) {
|
||||
page_widget = zathura_page_get_widget(zathura, zathura_document_get_page(zathura->document, zathura->page_cache.cache[i]));
|
||||
g_return_val_if_fail(page_widget != NULL, -1);
|
||||
g_object_get(G_OBJECT(page_widget), "last-view", &view_time, NULL);
|
||||
|
||||
if (view_time < lru_view_time) {
|
||||
lru_view_time = view_time;
|
||||
lru_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
zathura_page_t* page = zathura_document_get_page(zathura->document, zathura->page_cache.cache[lru_index]);
|
||||
g_return_val_if_fail(page != NULL, -1);
|
||||
|
||||
page_widget = zathura_page_get_widget(zathura, page);
|
||||
g_return_val_if_fail(page_widget != NULL, -1);
|
||||
|
||||
zathura_page_widget_update_surface(ZATHURA_PAGE(page_widget), NULL);
|
||||
girara_debug("Invalidated page %d at cache index %ld", zathura->page_cache.cache[lru_index] + 1, lru_index);
|
||||
zathura->page_cache.cache[lru_index] = -1;
|
||||
--zathura->page_cache.num_cached_pages;
|
||||
|
||||
return lru_index;
|
||||
}
|
||||
|
||||
static bool
|
||||
zathura_page_cache_is_full(zathura_t* zathura, bool* result)
|
||||
{
|
||||
g_return_val_if_fail(zathura != NULL, false);
|
||||
|
||||
*result = zathura->page_cache.num_cached_pages == zathura->page_cache.size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
zathura_page_cache_invalidate_all(zathura_t* zathura)
|
||||
{
|
||||
g_return_if_fail(zathura != NULL);
|
||||
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < zathura->page_cache.size; ++i) {
|
||||
zathura->page_cache.cache[i] = -1;
|
||||
}
|
||||
|
||||
zathura->page_cache.num_cached_pages = 0;
|
||||
}
|
||||
|
||||
void
|
||||
zathura_page_cache_add(zathura_t* zathura, unsigned int page_index)
|
||||
{
|
||||
g_return_if_fail(zathura != NULL);
|
||||
|
||||
zathura_page_t* page = zathura_document_get_page(zathura->document, page_index);
|
||||
|
||||
g_return_if_fail(page != NULL);
|
||||
|
||||
if (zathura_page_cache_is_cached(zathura, page_index)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool full;
|
||||
|
||||
if (zathura_page_cache_is_full(zathura, &full) == false) {
|
||||
return;
|
||||
} else if (full == true) {
|
||||
ssize_t idx = zathura_page_cache_lru_invalidate(zathura);
|
||||
|
||||
if (idx == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
zathura->page_cache.cache[idx] = page_index;
|
||||
++zathura->page_cache.num_cached_pages;
|
||||
girara_debug("Page %d is cached at cache index %ld", page_index + 1, idx);
|
||||
return;
|
||||
}
|
||||
|
||||
zathura->page_cache.cache[zathura->page_cache.num_cached_pages++] = page_index;
|
||||
girara_debug("Page %d is cached at cache index %d", page_index + 1, zathura->page_cache.num_cached_pages - 1);
|
||||
return;
|
||||
}
|
||||
|
|
18
zathura.h
18
zathura.h
|
@ -13,6 +13,8 @@
|
|||
#include <gtk/gtkx.h>
|
||||
#endif
|
||||
|
||||
#define ZATHURA_PAGE_CACHE_DEFAULT_SIZE 15
|
||||
|
||||
enum { NEXT, PREVIOUS, LEFT, RIGHT, UP, DOWN, BOTTOM, TOP, HIDE, HIGHLIGHT,
|
||||
DELETE_LAST_WORD, DELETE_LAST_CHAR, DEFAULT, ERROR, WARNING, NEXT_GROUP,
|
||||
PREVIOUS_GROUP, ZOOM_IN, ZOOM_OUT, ZOOM_ORIGINAL, ZOOM_SPECIFIC, FORWARD,
|
||||
|
@ -152,6 +154,15 @@ struct zathura_s
|
|||
gchar* file_path; /**< Save file path */
|
||||
gchar* password; /**< Save password */
|
||||
} file_monitor;
|
||||
|
||||
/**
|
||||
* The page cache
|
||||
*/
|
||||
struct {
|
||||
int* cache;
|
||||
unsigned int size;
|
||||
unsigned int num_cached_pages;
|
||||
} page_cache;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -366,5 +377,12 @@ void zathura_jumplist_add(zathura_t* zathura);
|
|||
*/
|
||||
void zathura_jumplist_append_jump(zathura_t* zathura);
|
||||
|
||||
/**
|
||||
* Add a page to the page cache
|
||||
*
|
||||
* @param zathura The zathura session
|
||||
* @param page_index The index of the page to be cached
|
||||
*/
|
||||
void zathura_page_cache_add(zathura_t* zathura, unsigned int page_index);
|
||||
|
||||
#endif // ZATHURA_H
|
||||
|
|
|
@ -530,20 +530,16 @@ The page padding defines the gap in pixels between each rendered page.
|
|||
* Value type: Integer
|
||||
* Default value: 1
|
||||
|
||||
page-store-threshold
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
Pages that are not visible get unloaded after some time. Every page that has not
|
||||
been visible for page-store-treshold seconds will be unloaded.
|
||||
page-cache-size
|
||||
^^^^^^^^^^^^^^^
|
||||
Defines the maximum number of pages that could be kept in the page cache. When
|
||||
the cache is full and a new page that isn't cached becomes visible, the least
|
||||
recently viewed page in the cache will be evicted to make room for the new one.
|
||||
Large values for this variable are NOT recommended, because this will lead to
|
||||
consuming a significant portion of the system memory.
|
||||
|
||||
* Value type: Integer
|
||||
* Default value: 30
|
||||
|
||||
page-store-interval
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
Defines the amount of seconds between the check to unload invisible pages.
|
||||
|
||||
* Value type: Integer
|
||||
* Default value: 30
|
||||
* Default value: 15
|
||||
|
||||
pages-per-row
|
||||
^^^^^^^^^^^^^
|
||||
|
|
Loading…
Reference in a new issue