drm/i915/selftests/perf: measure memcpy bw between regions
authorMatthew Auld <matthew.auld@intel.com>
Wed, 29 Jan 2020 09:33:43 +0000 (09:33 +0000)
committerChris Wilson <chris@chris-wilson.co.uk>
Wed, 29 Jan 2020 13:13:50 +0000 (13:13 +0000)
Measure the memcpy bw between our CPU accessible regions, trying all
supported mapping combinations(WC, WB) across various sizes.

v2:
    use smaller sizes
    throw in memcpy32/memcpy64/memcpy_from_wc

Signed-off-by: Matthew Auld <matthew.auld@intel.com>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Link: https://patchwork.freedesktop.org/patch/msgid/20200129093343.194570-1-matthew.auld@intel.com
drivers/gpu/drm/i915/selftests/i915_perf_selftests.h
drivers/gpu/drm/i915/selftests/intel_memory_region.c

index 5a577a1..3bf7f53 100644 (file)
@@ -17,3 +17,4 @@
  */
 selftest(engine_cs, intel_engine_cs_perf_selftests)
 selftest(blt, i915_gem_object_blt_perf_selftests)
+selftest(region, intel_memory_region_perf_selftests)
index 3ef3620..2a1d4ba 100644 (file)
@@ -4,6 +4,7 @@
  */
 
 #include <linux/prime_numbers.h>
+#include <linux/sort.h>
 
 #include "../i915_selftest.h"
 
@@ -19,6 +20,7 @@
 #include "gem/selftests/mock_context.h"
 #include "gt/intel_engine_user.h"
 #include "gt/intel_gt.h"
+#include "i915_memcpy.h"
 #include "selftests/igt_flush_test.h"
 #include "selftests/i915_random.h"
 
@@ -572,6 +574,195 @@ out_put:
        return err;
 }
 
+static const char *repr_type(u32 type)
+{
+       switch (type) {
+       case I915_MAP_WB:
+               return "WB";
+       case I915_MAP_WC:
+               return "WC";
+       }
+
+       return "";
+}
+
+static struct drm_i915_gem_object *
+create_region_for_mapping(struct intel_memory_region *mr, u64 size, u32 type,
+                         void **out_addr)
+{
+       struct drm_i915_gem_object *obj;
+       void *addr;
+
+       obj = i915_gem_object_create_region(mr, size, 0);
+       if (IS_ERR(obj))
+               return obj;
+
+       addr = i915_gem_object_pin_map(obj, type);
+       if (IS_ERR(addr)) {
+               i915_gem_object_put(obj);
+               if (PTR_ERR(addr) == -ENXIO)
+                       return ERR_PTR(-ENODEV);
+               return addr;
+       }
+
+       *out_addr = addr;
+       return obj;
+}
+
+static int wrap_ktime_compare(const void *A, const void *B)
+{
+       const ktime_t *a = A, *b = B;
+
+       return ktime_compare(*a, *b);
+}
+
+static void igt_memcpy_long(void *dst, const void *src, size_t size)
+{
+       unsigned long *tmp = dst;
+       const unsigned long *s = src;
+
+       size = size / sizeof(unsigned long);
+       while (size--)
+               *tmp++ = *s++;
+}
+
+static inline void igt_memcpy(void *dst, const void *src, size_t size)
+{
+       memcpy(dst, src, size);
+}
+
+static inline void igt_memcpy_from_wc(void *dst, const void *src, size_t size)
+{
+       i915_memcpy_from_wc(dst, src, size);
+}
+
+static int _perf_memcpy(struct intel_memory_region *src_mr,
+                       struct intel_memory_region *dst_mr,
+                       u64 size, u32 src_type, u32 dst_type)
+{
+       struct drm_i915_private *i915 = src_mr->i915;
+       const struct {
+               const char *name;
+               void (*copy)(void *dst, const void *src, size_t size);
+               bool skip;
+       } tests[] = {
+               {
+                       "memcpy",
+                       igt_memcpy,
+               },
+               {
+                       "memcpy_long",
+                       igt_memcpy_long,
+               },
+               {
+                       "memcpy_from_wc",
+                       igt_memcpy_from_wc,
+                       !i915_has_memcpy_from_wc(),
+               },
+       };
+       struct drm_i915_gem_object *src, *dst;
+       void *src_addr, *dst_addr;
+       int ret = 0;
+       int i;
+
+       src = create_region_for_mapping(src_mr, size, src_type, &src_addr);
+       if (IS_ERR(src)) {
+               ret = PTR_ERR(src);
+               goto out;
+       }
+
+       dst = create_region_for_mapping(dst_mr, size, dst_type, &dst_addr);
+       if (IS_ERR(dst)) {
+               ret = PTR_ERR(dst);
+               goto out_unpin_src;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(tests); ++i) {
+               ktime_t t[5];
+               int pass;
+
+               if (tests[i].skip)
+                       continue;
+
+               for (pass = 0; pass < ARRAY_SIZE(t); pass++) {
+                       ktime_t t0, t1;
+
+                       t0 = ktime_get();
+
+                       tests[i].copy(dst_addr, src_addr, size);
+
+                       t1 = ktime_get();
+                       t[pass] = ktime_sub(t1, t0);
+               }
+
+               sort(t, ARRAY_SIZE(t), sizeof(*t), wrap_ktime_compare, NULL);
+               pr_info("%s src(%s, %s) -> dst(%s, %s) %14s %4llu KiB copy: %5lld MiB/s\n",
+                       __func__,
+                       src_mr->name,
+                       repr_type(src_type),
+                       dst_mr->name,
+                       repr_type(dst_type),
+                       tests[i].name,
+                       size >> 10,
+                       div64_u64(mul_u32_u32(4 * size,
+                                             1000 * 1000 * 1000),
+                                 t[1] + 2 * t[2] + t[3]) >> 20);
+
+               cond_resched();
+       }
+
+       i915_gem_object_unpin_map(dst);
+       i915_gem_object_put(dst);
+out_unpin_src:
+       i915_gem_object_unpin_map(src);
+       i915_gem_object_put(src);
+
+       i915_gem_drain_freed_objects(i915);
+out:
+       if (ret == -ENODEV)
+               ret = 0;
+
+       return ret;
+}
+
+static int perf_memcpy(void *arg)
+{
+       struct drm_i915_private *i915 = arg;
+       static const u32 types[] = {
+               I915_MAP_WB,
+               I915_MAP_WC,
+       };
+       static const u32 sizes[] = {
+               SZ_4K,
+               SZ_64K,
+               SZ_4M,
+       };
+       struct intel_memory_region *src_mr, *dst_mr;
+       int src_id, dst_id;
+       int i, j, k;
+       int ret;
+
+       for_each_memory_region(src_mr, i915, src_id) {
+               for_each_memory_region(dst_mr, i915, dst_id) {
+                       for (i = 0; i < ARRAY_SIZE(sizes); ++i) {
+                               for (j = 0; j < ARRAY_SIZE(types); ++j) {
+                                       for (k = 0; k < ARRAY_SIZE(types); ++k) {
+                                               ret = _perf_memcpy(src_mr,
+                                                                  dst_mr,
+                                                                  sizes[i],
+                                                                  types[j],
+                                                                  types[k]);
+                                               if (ret)
+                                                       return ret;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       return 0;
+}
+
 int intel_memory_region_mock_selftests(void)
 {
        static const struct i915_subtest tests[] = {
@@ -619,3 +810,15 @@ int intel_memory_region_live_selftests(struct drm_i915_private *i915)
 
        return i915_live_subtests(tests, i915);
 }
+
+int intel_memory_region_perf_selftests(struct drm_i915_private *i915)
+{
+       static const struct i915_subtest tests[] = {
+               SUBTEST(perf_memcpy),
+       };
+
+       if (intel_gt_is_wedged(&i915->gt))
+               return 0;
+
+       return i915_live_subtests(tests, i915);
+}