[Mesa-dev] [PATCH V2] util/disk_cache: use a thread queue to write to shader cache

Timothy Arceri tarceri at itsqueeze.com
Tue Mar 14 01:35:28 UTC 2017


This should help reduce any overhead added by the shader cache
when programs are not found in the cache.

To avoid creating any special function just for the sake of the
tests we add a one second delay whenever we call dick_cache_put()
to give it time to finish.

V2: poll for file when waiting for thread in test
---
 src/compiler/glsl/tests/cache_test.c | 75 +++++++++++++++++++++++++++++++++---
 src/util/disk_cache.c                | 41 +++++++++++++-------
 2 files changed, 97 insertions(+), 19 deletions(-)

diff --git a/src/compiler/glsl/tests/cache_test.c b/src/compiler/glsl/tests/cache_test.c
index 6451e65..224353e 100644
--- a/src/compiler/glsl/tests/cache_test.c
+++ b/src/compiler/glsl/tests/cache_test.c
@@ -25,20 +25,21 @@
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdbool.h>
 #include <string.h>
 #include <ftw.h>
 #include <errno.h>
 #include <stdarg.h>
 #include <inttypes.h>
 #include <limits.h>
+#include <time.h>
 #include <unistd.h>
 
 #include "util/mesa-sha1.h"
 #include "util/disk_cache.h"
 
 bool error = false;
 
 #ifdef ENABLE_SHADER_CACHE
 
 static void
@@ -224,64 +225,110 @@ does_cache_contain(struct disk_cache *cache, cache_key key)
 
    if (result) {
       free(result);
       return true;
    }
 
    return false;
 }
 
 static void
+wait_until_file_written(cache_key key)
+{
+   struct timespec req;
+   struct timespec rem;
+
+   /* Set 100ms delay */
+   req.tv_sec = 0;
+   req.tv_nsec = 100000;
+
+   char *cache_path = getenv("MESA_GLSL_CACHE_DIR");
+
+   char buf[41];
+   _mesa_sha1_format(buf, key);
+
+   char *path_to_file;
+   asprintf(&path_to_file, "%s/mesa/%s/make_check/test/%c%c/%s", cache_path,
+            get_arch_bitness_str(), buf[0], buf[1], buf + 2);
+
+   unsigned retries = 0;
+   while (retries++ < 20) {
+      if (access(path_to_file, F_OK) != -1)
+         break;
+
+      nanosleep(&req, &rem);
+   }
+
+   free(path_to_file);
+}
+
+static void
 test_put_and_get(void)
 {
    struct disk_cache *cache;
    /* If the text of this blob is changed, then blob_key_byte_zero
     * also needs to be updated.
     */
    char blob[] = "This is a blob of thirty-seven bytes";
    uint8_t blob_key[20];
    uint8_t blob_key_byte_zero = 0xca;
    char string[] = "While this string has thirty-four";
    uint8_t string_key[20];
    char *result;
    size_t size;
    uint8_t *one_KB, *one_MB;
    uint8_t one_KB_key[20], one_MB_key[20];
    int count;
 
+   setenv("MESA_GLSL_CACHE_DIR", CACHE_TEST_TMP "/mesa-glsl-cache-dir", 1);
+
    cache = disk_cache_create("test", "make_check");
 
    _mesa_sha1_compute(blob, sizeof(blob), blob_key);
 
    /* Ensure that disk_cache_get returns nothing before anything is added. */
    result = disk_cache_get(cache, blob_key, &size);
    expect_null(result, "disk_cache_get with non-existent item (pointer)");
    expect_equal(size, 0, "disk_cache_get with non-existent item (size)");
 
    /* Simple test of put and get. */
    disk_cache_put(cache, blob_key, blob, sizeof(blob), NULL);
 
+   /* disk_cache_put() hands things off to a thread give it some time to
+    * finsih.
+    */
+   wait_until_file_written(blob_key);
+
    result = disk_cache_get(cache, blob_key, &size);
-   expect_equal_str(blob, result, "disk_cache_get of existing item (pointer)");
-   expect_equal(size, sizeof(blob), "disk_cache_get of existing item (size)");
+   if (result) {
+      expect_equal_str(blob, result, "disk_cache_get of existing item (pointer)");
+      expect_equal(size, sizeof(blob), "disk_cache_get of existing item (size)");
 
-   free(result);
+      free(result);
+   }
 
    /* Test put and get of a second item. */
    _mesa_sha1_compute(string, sizeof(string), string_key);
    disk_cache_put(cache, string_key, string, sizeof(string), NULL);
 
+   /* disk_cache_put() hands things off to a thread give it some time to
+    * finsih.
+    */
+   wait_until_file_written(string_key);
+
    result = disk_cache_get(cache, string_key, &size);
-   expect_equal_str(result, string, "2nd disk_cache_get of existing item (pointer)");
-   expect_equal(size, sizeof(string), "2nd disk_cache_get of existing item (size)");
+   if (result) {
+      expect_equal_str(result, string, "2nd disk_cache_get of existing item (pointer)");
+      expect_equal(size, sizeof(string), "2nd disk_cache_get of existing item (size)");
 
-   free(result);
+      free(result);
+   }
 
    /* Set the cache size to 1KB and add a 1KB item to force an eviction. */
    disk_cache_destroy(cache);
 
    setenv("MESA_GLSL_CACHE_MAX_SIZE", "1K", 1);
    cache = disk_cache_create("test", "make_check");
 
    one_KB = calloc(1, 1024);
 
    /* Obviously the SHA-1 hash of 1024 zero bytes isn't particularly
@@ -299,20 +346,25 @@ test_put_and_get(void)
     * interestingly full than that).
     *
     * For this test, we force this signature to land in the same
     * directory as the original blob first written to the cache.
     */
    _mesa_sha1_compute(one_KB, 1024, one_KB_key);
    one_KB_key[0] = blob_key_byte_zero;
 
    disk_cache_put(cache, one_KB_key, one_KB, 1024, one_KB);
 
+   /* disk_cache_put() hands things off to a thread give it some time to
+    * finish.
+    */
+   wait_until_file_written(one_KB_key);
+
    result = disk_cache_get(cache, one_KB_key, &size);
    expect_non_null(result, "3rd disk_cache_get of existing item (pointer)");
    expect_equal(size, 1024, "3rd disk_cache_get of existing item (size)");
 
    free(result);
 
    /* Ensure eviction happened by checking that only one of the two
     * previously-added items can still be fetched.
     */
    count = 0;
@@ -328,40 +380,51 @@ test_put_and_get(void)
     * three that have been added are available via disk_cache_get.
     */
    disk_cache_destroy(cache);
 
    setenv("MESA_GLSL_CACHE_MAX_SIZE", "1M", 1);
    cache = disk_cache_create("test", "make_check");
 
    disk_cache_put(cache, blob_key, blob, sizeof(blob), NULL);
    disk_cache_put(cache, string_key, string, sizeof(string), NULL);
 
+   /* disk_cache_put() hands things off to a thread give it some time to
+    * finish.
+    */
+   wait_until_file_written(blob_key);
+   wait_until_file_written(string_key);
+
    count = 0;
    if (does_cache_contain(cache, blob_key))
        count++;
 
    if (does_cache_contain(cache, string_key))
        count++;
 
    if (does_cache_contain(cache, one_KB_key))
        count++;
 
    expect_equal(count, 3, "no eviction before overflow with MAX_SIZE=1M");
 
    /* Finally, check eviction again after adding an object of size 1M. */
    one_MB = calloc(1024, 1024);
 
    _mesa_sha1_compute(one_MB, 1024 * 1024, one_MB_key);
    one_MB_key[0] = blob_key_byte_zero;;
 
    disk_cache_put(cache, one_MB_key, one_MB, 1024 * 1024, one_MB);
 
+   /* disk_cache_put() hands things off to a thread give it some time to
+    * finish.
+    */
+   wait_until_file_written(one_MB_key);
+
    count = 0;
    if (does_cache_contain(cache, blob_key))
        count++;
 
    if (does_cache_contain(cache, string_key))
        count++;
 
    if (does_cache_contain(cache, one_KB_key))
        count++;
 
diff --git a/src/util/disk_cache.c b/src/util/disk_cache.c
index 4f902df..2a1024a 100644
--- a/src/util/disk_cache.c
+++ b/src/util/disk_cache.c
@@ -781,47 +781,49 @@ destroy_put_job(void *job, int thread_index)
       free(dc_job->mem);
       free(dc_job);
    }
 }
 
 struct cache_entry_file_data {
    uint32_t crc32;
    uint32_t uncompressed_size;
 };
 
-void
-disk_cache_put(struct disk_cache *cache, const cache_key key,
-               const void *data, size_t size, void *mem_to_free)
+static void
+cache_put(void *job, int thread_index)
 {
+   assert(job);
+
    int fd = -1, fd_final = -1, err, ret;
    size_t len;
    char *filename = NULL, *filename_tmp = NULL;
+   struct disk_cache_put_job *dc_job = (struct disk_cache_put_job *) job;
 
-   filename = get_cache_file(cache, key);
+   filename = get_cache_file(dc_job->cache, dc_job->key);
    if (filename == NULL)
       goto done;
 
    /* Write to a temporary file to allow for an atomic rename to the
     * final destination filename, (to prevent any readers from seeing
     * a partially written file).
     */
    if (asprintf(&filename_tmp, "%s.tmp", filename) == -1)
       goto done;
 
    fd = open(filename_tmp, O_WRONLY | O_CLOEXEC | O_CREAT, 0644);
 
    /* Make the two-character subdirectory within the cache as needed. */
    if (fd == -1) {
       if (errno != ENOENT)
          goto done;
 
-      make_cache_file_directory(cache, key);
+      make_cache_file_directory(dc_job->cache, dc_job->key);
 
       fd = open(filename_tmp, O_WRONLY | O_CLOEXEC | O_CREAT, 0644);
       if (fd == -1)
          goto done;
    }
 
    /* With the temporary file open, we take an exclusive flock on
     * it. If the flock fails, then another process still has the file
     * open with the flock held. So just let that file be responsible
     * for writing the file.
@@ -840,67 +842,80 @@ disk_cache_put(struct disk_cache *cache, const cache_key key,
    if (fd_final != -1)
       goto done;
 
    /* OK, we're now on the hook to write out a file that we know is
     * not in the cache, and is also not being written out to the cache
     * by some other process.
     *
     * Before we do that, if the cache is too large, evict something
     * else first.
     */
-   if (*cache->size + size > cache->max_size)
-      evict_random_item(cache);
+   if (*dc_job->cache->size + dc_job->size > dc_job->cache->max_size)
+      evict_random_item(dc_job->cache);
 
    /* Create CRC of the data and store at the start of the file. We will
     * read this when restoring the cache and use it to check for corruption.
     */
    struct cache_entry_file_data cf_data;
-   cf_data.crc32 = util_hash_crc32(data, size);
-   cf_data.uncompressed_size = size;
+   cf_data.crc32 = util_hash_crc32(dc_job->data, dc_job->size);
+   cf_data.uncompressed_size = dc_job->size;
 
    size_t cf_data_size = sizeof(cf_data);
    for (len = 0; len < cf_data_size; len += ret) {
       ret = write(fd, ((uint8_t *) &cf_data) + len, cf_data_size - len);
       if (ret == -1) {
          unlink(filename_tmp);
          goto done;
       }
    }
 
    /* Now, finally, write out the contents to the temporary file, then
     * rename them atomically to the destination filename, and also
     * perform an atomic increment of the total cache size.
     */
-   size_t file_size = deflate_and_write_to_disk(data, size, fd, filename_tmp);
+   size_t file_size = deflate_and_write_to_disk(dc_job->data, dc_job->size,
+                                                fd, filename_tmp);
    if (file_size == 0) {
       unlink(filename_tmp);
       goto done;
    }
    rename(filename_tmp, filename);
 
    file_size += cf_data_size;
-   p_atomic_add(cache->size, file_size);
+   p_atomic_add(dc_job->cache->size, file_size);
 
  done:
    if (fd_final != -1)
       close(fd_final);
    /* This close finally releases the flock, (now that the final dile
     * has been renamed into place and the size has been added).
     */
    if (fd != -1)
       close(fd);
    if (filename_tmp)
       free(filename_tmp);
    if (filename)
       free(filename);
-   if (mem_to_free)
-      free(mem_to_free);
+}
+
+void
+disk_cache_put(struct disk_cache *cache, const cache_key key,
+               const void *data, size_t size, void *mem_to_free)
+{
+   struct disk_cache_put_job *dc_job =
+      create_put_job(cache, key, data, size, mem_to_free);
+
+   if (dc_job) {
+      util_queue_fence_init(&dc_job->fence);
+      util_queue_add_job(&cache->cache_queue, dc_job, &dc_job->fence,
+                         cache_put, destroy_put_job);
+   }
 }
 
 /**
  * Decompresses cache entry, returns true if successful.
  */
 static bool
 inflate_cache_data(uint8_t *in_data, size_t in_data_size,
                    uint8_t *out_data, size_t out_data_size)
 {
    z_stream strm;
-- 
2.9.3



More information about the mesa-dev mailing list