av1_k_means_test.cc (10433B)
1 /* 2 * Copyright (c) 2020, Alliance for Open Media. All rights reserved. 3 * 4 * This source code is subject to the terms of the BSD 2 Clause License and 5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License 6 * was not distributed with this source code in the LICENSE file, you can 7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open 8 * Media Patent License 1.0 was not distributed with this source code in the 9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent. 10 */ 11 12 #include <cstdlib> 13 #include <new> 14 #include <tuple> 15 16 #include "config/aom_config.h" 17 #include "config/av1_rtcd.h" 18 19 #include "aom/aom_codec.h" 20 #include "aom/aom_integer.h" 21 #include "aom_mem/aom_mem.h" 22 #include "aom_ports/aom_timer.h" 23 #include "aom_ports/mem.h" 24 #include "av1/encoder/palette.h" 25 #include "gtest/gtest.h" 26 #include "test/acm_random.h" 27 #include "test/register_state_check.h" 28 #include "test/util.h" 29 30 namespace AV1Kmeans { 31 using av1_calc_indices_dim1_func = void (*)(const int16_t *data, 32 const int16_t *centroids, 33 uint8_t *indices, 34 int64_t *total_dist, int n, int k); 35 using av1_calc_indices_dim2_func = void (*)(const int16_t *data, 36 const int16_t *centroids, 37 uint8_t *indices, 38 int64_t *total_dist, int n, int k); 39 40 using av1_calc_indices_dim1Param = 41 std::tuple<av1_calc_indices_dim1_func, BLOCK_SIZE>; 42 43 using av1_calc_indices_dim2Param = 44 std::tuple<av1_calc_indices_dim2_func, BLOCK_SIZE>; 45 46 class AV1KmeansTest1 47 : public ::testing::TestWithParam<av1_calc_indices_dim1Param> { 48 public: 49 ~AV1KmeansTest1() override; 50 void SetUp() override; 51 52 protected: 53 void RunCheckOutput(av1_calc_indices_dim1_func test_impl, BLOCK_SIZE bsize, 54 int centroids); 55 void RunSpeedTest(av1_calc_indices_dim1_func test_impl, BLOCK_SIZE bsize, 56 int centroids); 57 bool CheckResult(int n) { 58 for (int idx = 0; idx < n; ++idx) { 59 if (indices1_[idx] != indices2_[idx]) { 60 printf("%d ", idx); 61 printf("%d != %d ", indices1_[idx], indices2_[idx]); 62 return false; 63 } 64 } 65 return true; 66 } 67 68 libaom_test::ACMRandom rnd_; 69 int16_t data_[4096]; 70 int16_t centroids_[8]; 71 uint8_t indices1_[4096]; 72 uint8_t indices2_[4096]; 73 }; 74 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AV1KmeansTest1); 75 76 AV1KmeansTest1::~AV1KmeansTest1() = default; 77 78 void AV1KmeansTest1::SetUp() { 79 rnd_.Reset(libaom_test::ACMRandom::DeterministicSeed()); 80 for (int i = 0; i < 4096; ++i) { 81 data_[i] = (int)rnd_.Rand8() << 4; 82 } 83 for (int i = 0; i < 8; i++) { 84 centroids_[i] = (int)rnd_.Rand8() << 4; 85 } 86 } 87 88 void AV1KmeansTest1::RunCheckOutput(av1_calc_indices_dim1_func test_impl, 89 BLOCK_SIZE bsize, int k) { 90 const int w = block_size_wide[bsize]; 91 const int h = block_size_high[bsize]; 92 const int n = w * h; 93 int64_t total_dist_dim1, total_dist_impl; 94 av1_calc_indices_dim1_c(data_, centroids_, indices1_, &total_dist_dim1, n, k); 95 test_impl(data_, centroids_, indices2_, &total_dist_impl, n, k); 96 97 ASSERT_EQ(total_dist_dim1, total_dist_impl); 98 ASSERT_EQ(CheckResult(n), true) 99 << " block " << bsize << " index " << n << " Centroids " << k; 100 } 101 102 void AV1KmeansTest1::RunSpeedTest(av1_calc_indices_dim1_func test_impl, 103 BLOCK_SIZE bsize, int k) { 104 const int w = block_size_wide[bsize]; 105 const int h = block_size_high[bsize]; 106 const int n = w * h; 107 const int num_loops = 1000000000 / n; 108 109 av1_calc_indices_dim1_func funcs[2] = { av1_calc_indices_dim1_c, test_impl }; 110 double elapsed_time[2] = { 0 }; 111 for (int i = 0; i < 2; ++i) { 112 aom_usec_timer timer; 113 aom_usec_timer_start(&timer); 114 av1_calc_indices_dim1_func func = funcs[i]; 115 for (int j = 0; j < num_loops; ++j) { 116 func(data_, centroids_, indices1_, /*total_dist=*/nullptr, n, k); 117 } 118 aom_usec_timer_mark(&timer); 119 double time = static_cast<double>(aom_usec_timer_elapsed(&timer)); 120 elapsed_time[i] = 1000.0 * time / num_loops; 121 } 122 printf("av1_calc_indices_dim1 indices= %d centroids=%d: %7.2f/%7.2fns", n, k, 123 elapsed_time[0], elapsed_time[1]); 124 printf("(%3.2f)\n", elapsed_time[0] / elapsed_time[1]); 125 } 126 127 TEST_P(AV1KmeansTest1, CheckOutput) { 128 // centroids = 2..8 129 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 2); 130 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 3); 131 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 4); 132 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 5); 133 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 6); 134 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 7); 135 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 8); 136 } 137 138 TEST_P(AV1KmeansTest1, DISABLED_Speed) { 139 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 2); 140 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 3); 141 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 4); 142 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 5); 143 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 6); 144 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 7); 145 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 8); 146 } 147 148 class AV1KmeansTest2 149 : public ::testing::TestWithParam<av1_calc_indices_dim2Param> { 150 public: 151 ~AV1KmeansTest2() override; 152 void SetUp() override; 153 154 protected: 155 void RunCheckOutput(av1_calc_indices_dim2_func test_impl, BLOCK_SIZE bsize, 156 int centroids); 157 void RunSpeedTest(av1_calc_indices_dim2_func test_impl, BLOCK_SIZE bsize, 158 int centroids); 159 bool CheckResult(int n) { 160 bool flag = true; 161 for (int idx = 0; idx < n; ++idx) { 162 if (indices1_[idx] != indices2_[idx]) { 163 printf("%d ", idx); 164 printf("%d != %d ", indices1_[idx], indices2_[idx]); 165 flag = false; 166 } 167 } 168 if (flag == false) { 169 return false; 170 } 171 return true; 172 } 173 174 libaom_test::ACMRandom rnd_; 175 int16_t data_[4096 * 2]; 176 int16_t centroids_[8 * 2]; 177 uint8_t indices1_[4096]; 178 uint8_t indices2_[4096]; 179 }; 180 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AV1KmeansTest2); 181 182 AV1KmeansTest2::~AV1KmeansTest2() = default; 183 184 void AV1KmeansTest2::SetUp() { 185 rnd_.Reset(libaom_test::ACMRandom::DeterministicSeed()); 186 for (int i = 0; i < 4096 * 2; ++i) { 187 data_[i] = (int)rnd_.Rand8(); 188 } 189 for (int i = 0; i < 8 * 2; i++) { 190 centroids_[i] = (int)rnd_.Rand8(); 191 } 192 } 193 194 void AV1KmeansTest2::RunCheckOutput(av1_calc_indices_dim2_func test_impl, 195 BLOCK_SIZE bsize, int k) { 196 const int w = block_size_wide[bsize]; 197 const int h = block_size_high[bsize]; 198 const int n = w * h; 199 int64_t total_dist_dim2, total_dist_impl; 200 av1_calc_indices_dim2_c(data_, centroids_, indices1_, &total_dist_dim2, n, k); 201 test_impl(data_, centroids_, indices2_, &total_dist_impl, n, k); 202 203 ASSERT_EQ(total_dist_dim2, total_dist_impl); 204 ASSERT_EQ(CheckResult(n), true) 205 << " block " << bsize << " index " << n << " Centroids " << k; 206 } 207 208 void AV1KmeansTest2::RunSpeedTest(av1_calc_indices_dim2_func test_impl, 209 BLOCK_SIZE bsize, int k) { 210 const int w = block_size_wide[bsize]; 211 const int h = block_size_high[bsize]; 212 const int n = w * h; 213 const int num_loops = 1000000000 / n; 214 215 av1_calc_indices_dim2_func funcs[2] = { av1_calc_indices_dim2_c, test_impl }; 216 double elapsed_time[2] = { 0 }; 217 for (int i = 0; i < 2; ++i) { 218 aom_usec_timer timer; 219 aom_usec_timer_start(&timer); 220 av1_calc_indices_dim2_func func = funcs[i]; 221 for (int j = 0; j < num_loops; ++j) { 222 func(data_, centroids_, indices1_, /*total_dist=*/nullptr, n, k); 223 } 224 aom_usec_timer_mark(&timer); 225 double time = static_cast<double>(aom_usec_timer_elapsed(&timer)); 226 elapsed_time[i] = 1000.0 * time / num_loops; 227 } 228 printf("av1_calc_indices_dim2 indices= %d centroids=%d: %7.2f/%7.2fns", n, k, 229 elapsed_time[0], elapsed_time[1]); 230 printf("(%3.2f)\n", elapsed_time[0] / elapsed_time[1]); 231 } 232 233 TEST_P(AV1KmeansTest2, CheckOutput) { 234 // centroids = 2..8 235 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 2); 236 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 3); 237 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 4); 238 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 5); 239 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 6); 240 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 7); 241 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 8); 242 } 243 244 TEST_P(AV1KmeansTest2, DISABLED_Speed) { 245 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 2); 246 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 3); 247 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 4); 248 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 5); 249 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 6); 250 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 7); 251 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 8); 252 } 253 254 #if HAVE_SSE2 || HAVE_AVX2 || HAVE_NEON 255 const BLOCK_SIZE kValidBlockSize[] = { BLOCK_8X8, BLOCK_8X16, BLOCK_8X32, 256 BLOCK_16X8, BLOCK_16X16, BLOCK_16X32, 257 BLOCK_32X8, BLOCK_32X16, BLOCK_32X32, 258 BLOCK_32X64, BLOCK_64X32, BLOCK_64X64, 259 BLOCK_16X64, BLOCK_64X16 }; 260 #endif 261 262 #if HAVE_SSE2 263 INSTANTIATE_TEST_SUITE_P( 264 SSE2, AV1KmeansTest1, 265 ::testing::Combine(::testing::Values(&av1_calc_indices_dim1_sse2), 266 ::testing::ValuesIn(kValidBlockSize))); 267 INSTANTIATE_TEST_SUITE_P( 268 SSE2, AV1KmeansTest2, 269 ::testing::Combine(::testing::Values(&av1_calc_indices_dim2_sse2), 270 ::testing::ValuesIn(kValidBlockSize))); 271 #endif 272 273 #if HAVE_AVX2 274 INSTANTIATE_TEST_SUITE_P( 275 AVX2, AV1KmeansTest1, 276 ::testing::Combine(::testing::Values(&av1_calc_indices_dim1_avx2), 277 ::testing::ValuesIn(kValidBlockSize))); 278 INSTANTIATE_TEST_SUITE_P( 279 AVX2, AV1KmeansTest2, 280 ::testing::Combine(::testing::Values(&av1_calc_indices_dim2_avx2), 281 ::testing::ValuesIn(kValidBlockSize))); 282 #endif 283 284 #if HAVE_NEON 285 INSTANTIATE_TEST_SUITE_P( 286 NEON, AV1KmeansTest1, 287 ::testing::Combine(::testing::Values(&av1_calc_indices_dim1_neon), 288 ::testing::ValuesIn(kValidBlockSize))); 289 INSTANTIATE_TEST_SUITE_P( 290 NEON, AV1KmeansTest2, 291 ::testing::Combine(::testing::Values(&av1_calc_indices_dim2_neon), 292 ::testing::ValuesIn(kValidBlockSize))); 293 #endif 294 295 } // namespace AV1Kmeans