tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

convolve_test.cc (10428B)


      1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style
      4 // license that can be found in the LICENSE file.
      5 
      6 #include "lib/jxl/convolve.h"
      7 
      8 #include <jxl/memory_manager.h>
      9 #include <jxl/types.h>
     10 
     11 #include <cinttypes>  // PRIx64
     12 #include <ctime>
     13 
     14 #undef HWY_TARGET_INCLUDE
     15 #define HWY_TARGET_INCLUDE "lib/jxl/convolve_test.cc"
     16 #include <hwy/foreach_target.h>
     17 #include <hwy/highway.h>
     18 #include <hwy/nanobenchmark.h>
     19 #include <hwy/tests/hwy_gtest.h>
     20 #include <vector>
     21 
     22 #include "lib/jxl/base/compiler_specific.h"
     23 #include "lib/jxl/base/data_parallel.h"
     24 #include "lib/jxl/base/printf_macros.h"
     25 #include "lib/jxl/base/random.h"
     26 #include "lib/jxl/base/rect.h"
     27 #include "lib/jxl/image_ops.h"
     28 #include "lib/jxl/image_test_utils.h"
     29 #include "lib/jxl/test_memory_manager.h"
     30 #include "lib/jxl/test_utils.h"
     31 #include "lib/jxl/testing.h"
     32 
     33 #ifndef JXL_DEBUG_CONVOLVE
     34 #define JXL_DEBUG_CONVOLVE 0
     35 #endif
     36 
     37 #include "lib/jxl/convolve-inl.h"
     38 
     39 HWY_BEFORE_NAMESPACE();
     40 namespace jxl {
     41 namespace HWY_NAMESPACE {
     42 
     43 void TestNeighbors() {
     44  const Neighbors::D d;
     45  const Neighbors::V v = Iota(d, 0);
     46  constexpr size_t kMaxVectorSize = 64;
     47  constexpr size_t M = kMaxVectorSize / sizeof(float);
     48  HWY_ALIGN float actual[M] = {0};
     49 
     50  HWY_ALIGN float first_l1[M] = {0, 0, 1, 2,  3,  4,  5,  6,
     51                                 7, 8, 9, 10, 11, 12, 13, 14};
     52  Store(Neighbors::FirstL1(v), d, actual);
     53  const size_t N = Lanes(d);
     54  ASSERT_LE(N, M);
     55  EXPECT_EQ(std::vector<float>(first_l1, first_l1 + N),
     56            std::vector<float>(actual, actual + N));
     57 
     58 #if HWY_TARGET != HWY_SCALAR
     59  HWY_ALIGN float first_l2[M] = {1, 0, 0, 1, 2,  3,  4,  5,
     60                                 6, 7, 8, 9, 10, 11, 12, 13};
     61  Store(Neighbors::FirstL2(v), d, actual);
     62  EXPECT_EQ(std::vector<float>(first_l2, first_l2 + N),
     63            std::vector<float>(actual, actual + N));
     64 
     65  HWY_ALIGN float first_l3[] = {2, 1, 0, 0, 1, 2,  3,  4,
     66                                5, 6, 7, 8, 9, 10, 11, 12};
     67  Store(Neighbors::FirstL3(v), d, actual);
     68  EXPECT_EQ(std::vector<float>(first_l3, first_l3 + N),
     69            std::vector<float>(actual, actual + N));
     70 #endif  // HWY_TARGET != HWY_SCALAR
     71 }
     72 
     73 void VerifySymmetric3(const size_t xsize, const size_t ysize, ThreadPool* pool,
     74                      Rng* rng) {
     75  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
     76  const Rect rect(0, 0, xsize, ysize);
     77 
     78  JXL_TEST_ASSIGN_OR_DIE(ImageF in,
     79                         ImageF::Create(memory_manager, xsize, ysize));
     80  GenerateImage(*rng, &in, 0.0f, 1.0f);
     81 
     82  JXL_TEST_ASSIGN_OR_DIE(ImageF out_expected,
     83                         ImageF::Create(memory_manager, xsize, ysize));
     84  JXL_TEST_ASSIGN_OR_DIE(ImageF out_actual,
     85                         ImageF::Create(memory_manager, xsize, ysize));
     86 
     87  const WeightsSymmetric3& weights = WeightsSymmetric3Lowpass();
     88  ASSERT_TRUE(Symmetric3(in, rect, weights, pool, &out_expected));
     89  ASSERT_TRUE(SlowSymmetric3(in, rect, weights, pool, &out_actual));
     90 
     91  JXL_TEST_ASSERT_OK(
     92      VerifyRelativeError(out_expected, out_actual, 1E-5f, 1E-5f, _));
     93 }
     94 
     95 std::vector<Rect> GenerateTestRectangles(size_t xsize, size_t ysize) {
     96  std::vector<Rect> out;
     97  for (size_t tl : {0, 1, 13}) {
     98    for (size_t br : {0, 1, 13}) {
     99      if (xsize > tl + br && ysize > tl + br) {
    100        out.emplace_back(tl, tl, xsize - tl - br, ysize - tl - br);
    101      }
    102    }
    103  }
    104  return out;
    105 }
    106 
    107 // Ensures Symmetric and Separable give the same result.
    108 void VerifySymmetric5(const size_t xsize, const size_t ysize, ThreadPool* pool,
    109                      Rng* rng) {
    110  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
    111  JXL_TEST_ASSIGN_OR_DIE(ImageF in,
    112                         ImageF::Create(memory_manager, xsize, ysize));
    113  GenerateImage(*rng, &in, 0.0f, 1.0f);
    114 
    115  for (const Rect& in_rect : GenerateTestRectangles(xsize, ysize)) {
    116    JXL_DEBUG(JXL_DEBUG_CONVOLVE,
    117              "in_rect: %" PRIuS "x%" PRIuS "+%" PRIuS ",%" PRIuS "",
    118              in_rect.xsize(), in_rect.ysize(), in_rect.x0(), in_rect.y0());
    119    {
    120      Rect out_rect = in_rect;
    121      JXL_TEST_ASSIGN_OR_DIE(ImageF out_expected,
    122                             ImageF::Create(memory_manager, xsize, ysize));
    123      JXL_TEST_ASSIGN_OR_DIE(ImageF out_actual,
    124                             ImageF::Create(memory_manager, xsize, ysize));
    125      FillImage(-1.0f, &out_expected);
    126      FillImage(-1.0f, &out_actual);
    127 
    128      ASSERT_TRUE(SlowSeparable5(in, in_rect, WeightsSeparable5Lowpass(), pool,
    129                                 &out_expected, out_rect));
    130      ASSERT_TRUE(Symmetric5(in, in_rect, WeightsSymmetric5Lowpass(), pool,
    131                             &out_actual, out_rect));
    132 
    133      JXL_TEST_ASSERT_OK(
    134          VerifyRelativeError(out_expected, out_actual, 1E-5f, 1E-5f, _));
    135    }
    136    {
    137      Rect out_rect(0, 0, in_rect.xsize(), in_rect.ysize());
    138      JXL_TEST_ASSIGN_OR_DIE(
    139          ImageF out_expected,
    140          ImageF::Create(memory_manager, out_rect.xsize(), out_rect.ysize()));
    141      JXL_TEST_ASSIGN_OR_DIE(
    142          ImageF out_actual,
    143          ImageF::Create(memory_manager, out_rect.xsize(), out_rect.ysize()));
    144 
    145      ASSERT_TRUE(SlowSeparable5(in, in_rect, WeightsSeparable5Lowpass(), pool,
    146                                 &out_expected, out_rect));
    147      ASSERT_TRUE(Symmetric5(in, in_rect, WeightsSymmetric5Lowpass(), pool,
    148                             &out_actual, out_rect));
    149 
    150      JXL_TEST_ASSERT_OK(
    151          VerifyRelativeError(out_expected, out_actual, 1E-5f, 1E-5f, _));
    152    }
    153  }
    154 }
    155 
    156 void VerifySeparable5(const size_t xsize, const size_t ysize, ThreadPool* pool,
    157                      Rng* rng) {
    158  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
    159  const Rect rect(0, 0, xsize, ysize);
    160 
    161  JXL_TEST_ASSIGN_OR_DIE(ImageF in,
    162                         ImageF::Create(memory_manager, xsize, ysize));
    163  GenerateImage(*rng, &in, 0.0f, 1.0f);
    164 
    165  JXL_TEST_ASSIGN_OR_DIE(ImageF out_expected,
    166                         ImageF::Create(memory_manager, xsize, ysize));
    167  JXL_TEST_ASSIGN_OR_DIE(ImageF out_actual,
    168                         ImageF::Create(memory_manager, xsize, ysize));
    169 
    170  const WeightsSeparable5& weights = WeightsSeparable5Lowpass();
    171  ASSERT_TRUE(SlowSeparable5(in, rect, weights, pool, &out_expected, rect));
    172  ASSERT_TRUE(Separable5(in, rect, weights, pool, &out_actual));
    173 
    174  JXL_TEST_ASSERT_OK(
    175      VerifyRelativeError(out_expected, out_actual, 1E-5f, 1E-5f, _));
    176 }
    177 
    178 // For all xsize/ysize and kernels:
    179 void TestConvolve() {
    180  TestNeighbors();
    181 
    182  test::ThreadPoolForTests pool(4);
    183  const auto do_test = [](const uint32_t task, size_t /*thread*/) -> Status {
    184    const size_t xsize = task;
    185    Rng rng(129 + 13 * xsize);
    186 
    187    ThreadPool* null_pool = nullptr;
    188    test::ThreadPoolForTests pool3(3);
    189    for (size_t ysize = kConvolveMaxRadius; ysize < 16; ++ysize) {
    190      JXL_DEBUG(JXL_DEBUG_CONVOLVE,
    191                "%" PRIuS " x %" PRIuS " (target %" PRIx64
    192                ")===============================",
    193                xsize, ysize, static_cast<int64_t>(HWY_TARGET));
    194 
    195      JXL_DEBUG(JXL_DEBUG_CONVOLVE, "Sym3------------------");
    196      VerifySymmetric3(xsize, ysize, null_pool, &rng);
    197      VerifySymmetric3(xsize, ysize, pool3.get(), &rng);
    198 
    199      JXL_DEBUG(JXL_DEBUG_CONVOLVE, "Sym5------------------");
    200      VerifySymmetric5(xsize, ysize, null_pool, &rng);
    201      VerifySymmetric5(xsize, ysize, pool3.get(), &rng);
    202 
    203      JXL_DEBUG(JXL_DEBUG_CONVOLVE, "Sep5------------------");
    204      VerifySeparable5(xsize, ysize, null_pool, &rng);
    205      VerifySeparable5(xsize, ysize, pool3.get(), &rng);
    206    }
    207    return true;
    208  };
    209  EXPECT_EQ(true, RunOnPool(pool.get(), kConvolveMaxRadius, 40,
    210                            ThreadPool::NoInit, do_test, "TestConvolve"));
    211 }
    212 
    213 // Measures durations, verifies results, prints timings. `unpredictable1`
    214 // must have value 1 (unknown to the compiler to prevent elision).
    215 template <class Conv>
    216 void BenchmarkConv(const char* caption, const Conv& conv,
    217                   const hwy::FuncInput unpredictable1) {
    218  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
    219  const size_t kNumInputs = 1;
    220  const hwy::FuncInput inputs[kNumInputs] = {unpredictable1};
    221  hwy::Result results[kNumInputs];
    222 
    223  const size_t kDim = 160;  // in+out fit in L2
    224  JXL_TEST_ASSIGN_OR_DIE(ImageF in, ImageF::Create(memory_manager, kDim, kDim));
    225  ZeroFillImage(&in);
    226  in.Row(kDim / 2)[kDim / 2] = unpredictable1;
    227  JXL_TEST_ASSIGN_OR_DIE(ImageF out,
    228                         ImageF::Create(memory_manager, kDim, kDim));
    229 
    230  hwy::Params p;
    231  p.verbose = false;
    232  p.max_evals = 7;
    233  p.target_rel_mad = 0.002;
    234  const size_t num_results = MeasureClosure(
    235      [&in, &conv, &out](const hwy::FuncInput input) {
    236        conv(in, &out);
    237        return out.Row(input)[0];
    238      },
    239      inputs, kNumInputs, results, p);
    240  if (num_results != kNumInputs) {
    241    fprintf(stderr, "MeasureClosure failed.\n");
    242  }
    243  for (size_t i = 0; i < num_results; ++i) {
    244    const double seconds = static_cast<double>(results[i].ticks) /
    245                           hwy::platform::InvariantTicksPerSecond();
    246    printf("%12s: %7.2f MP/s (MAD=%4.2f%%)\n", caption,
    247           kDim * kDim * 1E-6 / seconds,
    248           static_cast<double>(results[i].variability) * 100.0);
    249  }
    250 }
    251 
    252 struct ConvSymmetric3 {
    253  void operator()(const ImageF& in, ImageF* JXL_RESTRICT out) const {
    254    ThreadPool* null_pool = nullptr;
    255    ASSERT_TRUE(
    256        Symmetric3(in, Rect(in), WeightsSymmetric3Lowpass(), null_pool, out));
    257  }
    258 };
    259 
    260 struct ConvSeparable5 {
    261  void operator()(const ImageF& in, ImageF* JXL_RESTRICT out) const {
    262    ThreadPool* null_pool = nullptr;
    263    ASSERT_TRUE(
    264        Separable5(in, Rect(in), WeightsSeparable5Lowpass(), null_pool, out));
    265  }
    266 };
    267 
    268 void BenchmarkAll() {
    269 #if JXL_FALSE  // disabled to avoid test timeouts, run manually on demand
    270  const hwy::FuncInput unpredictable1 = time(nullptr) != 1234;
    271  BenchmarkConv("Symmetric3", ConvSymmetric3(), unpredictable1);
    272  BenchmarkConv("Separable5", ConvSeparable5(), unpredictable1);
    273 #endif
    274 }
    275 
    276 // NOLINTNEXTLINE(google-readability-namespace-comments)
    277 }  // namespace HWY_NAMESPACE
    278 }  // namespace jxl
    279 HWY_AFTER_NAMESPACE();
    280 
    281 #if HWY_ONCE
    282 namespace jxl {
    283 
    284 class ConvolveTest : public hwy::TestWithParamTarget {};
    285 HWY_TARGET_INSTANTIATE_TEST_SUITE_P(ConvolveTest);
    286 
    287 HWY_EXPORT_AND_TEST_P(ConvolveTest, TestConvolve);
    288 
    289 HWY_EXPORT_AND_TEST_P(ConvolveTest, BenchmarkAll);
    290 
    291 }  // namespace jxl
    292 #endif