abseil-cpp/absl/container/internal/raw_hash_set_benchmark.cc
Go to the documentation of this file.
1 // Copyright 2018 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "absl/container/internal/raw_hash_set.h"
16 
17 #include <numeric>
18 #include <random>
19 
20 #include "absl/base/internal/raw_logging.h"
21 #include "absl/container/internal/hash_function_defaults.h"
22 #include "absl/strings/str_format.h"
23 #include "benchmark/benchmark.h"
24 
25 namespace absl {
27 namespace container_internal {
28 
30  template <typename C>
31  static auto GetSlots(const C& c) -> decltype(c.slots_) {
32  return c.slots_;
33  }
34 };
35 
36 namespace {
37 
38 struct IntPolicy {
39  using slot_type = int64_t;
40  using key_type = int64_t;
41  using init_type = int64_t;
42 
43  static void construct(void*, int64_t* slot, int64_t v) { *slot = v; }
44  static void destroy(void*, int64_t*) {}
45  static void transfer(void*, int64_t* new_slot, int64_t* old_slot) {
46  *new_slot = *old_slot;
47  }
48 
49  static int64_t& element(slot_type* slot) { return *slot; }
50 
51  template <class F>
52  static auto apply(F&& f, int64_t x) -> decltype(std::forward<F>(f)(x, x)) {
53  return std::forward<F>(f)(x, x);
54  }
55 };
56 
57 class StringPolicy {
58  template <class F, class K, class V,
59  class = typename std::enable_if<
61  decltype(std::declval<F>()(
62  std::declval<const absl::string_view&>(), std::piecewise_construct,
63  std::declval<std::tuple<K>>(),
64  std::declval<V>())) static apply_impl(F&& f,
65  std::pair<std::tuple<K>, V> p) {
66  const absl::string_view& key = std::get<0>(p.first);
67  return std::forward<F>(f)(key, std::piecewise_construct, std::move(p.first),
68  std::move(p.second));
69  }
70 
71  public:
72  struct slot_type {
73  struct ctor {};
74 
75  template <class... Ts>
76  slot_type(ctor, Ts&&... ts) : pair(std::forward<Ts>(ts)...) {}
77 
78  std::pair<std::string, std::string> pair;
79  };
80 
81  using key_type = std::string;
82  using init_type = std::pair<std::string, std::string>;
83 
84  template <class allocator_type, class... Args>
85  static void construct(allocator_type* alloc, slot_type* slot, Args... args) {
87  *alloc, slot, typename slot_type::ctor(), std::forward<Args>(args)...);
88  }
89 
90  template <class allocator_type>
91  static void destroy(allocator_type* alloc, slot_type* slot) {
93  }
94 
95  template <class allocator_type>
96  static void transfer(allocator_type* alloc, slot_type* new_slot,
97  slot_type* old_slot) {
98  construct(alloc, new_slot, std::move(old_slot->pair));
99  destroy(alloc, old_slot);
100  }
101 
102  static std::pair<std::string, std::string>& element(slot_type* slot) {
103  return slot->pair;
104  }
105 
106  template <class F, class... Args>
107  static auto apply(F&& f, Args&&... args)
108  -> decltype(apply_impl(std::forward<F>(f),
109  PairArgs(std::forward<Args>(args)...))) {
110  return apply_impl(std::forward<F>(f),
111  PairArgs(std::forward<Args>(args)...));
112  }
113 };
114 
115 struct StringHash : container_internal::hash_default_hash<absl::string_view> {
116  using is_transparent = void;
117 };
118 struct StringEq : std::equal_to<absl::string_view> {
119  using is_transparent = void;
120 };
121 
122 struct StringTable
123  : raw_hash_set<StringPolicy, StringHash, StringEq, std::allocator<int>> {
124  using Base = typename StringTable::raw_hash_set;
125  StringTable() {}
126  using Base::Base;
127 };
128 
129 struct IntTable
130  : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>,
131  std::equal_to<int64_t>, std::allocator<int64_t>> {
132  using Base = typename IntTable::raw_hash_set;
133  IntTable() {}
134  using Base::Base;
135 };
136 
137 struct string_generator {
138  template <class RNG>
139  std::string operator()(RNG& rng) const {
140  std::string res;
141  res.resize(12);
142  std::uniform_int_distribution<uint32_t> printable_ascii(0x20, 0x7E);
143  std::generate(res.begin(), res.end(), [&] { return printable_ascii(rng); });
144  return res;
145  }
146 
147  size_t size;
148 };
149 
150 // Model a cache in steady state.
151 //
152 // On a table of size N, keep deleting the LRU entry and add a random one.
153 void BM_CacheInSteadyState(benchmark::State& state) {
154  std::random_device rd;
155  std::mt19937 rng(rd());
156  string_generator gen{12};
157  StringTable t;
158  std::deque<std::string> keys;
159  while (t.size() < state.range(0)) {
160  auto x = t.emplace(gen(rng), gen(rng));
161  if (x.second) keys.push_back(x.first->first);
162  }
163  ABSL_RAW_CHECK(state.range(0) >= 10, "");
164  while (state.KeepRunning()) {
165  // Some cache hits.
166  std::deque<std::string>::const_iterator it;
167  for (int i = 0; i != 90; ++i) {
168  if (i % 10 == 0) it = keys.end();
169  ::benchmark::DoNotOptimize(t.find(*--it));
170  }
171  // Some cache misses.
172  for (int i = 0; i != 10; ++i) ::benchmark::DoNotOptimize(t.find(gen(rng)));
173  ABSL_RAW_CHECK(t.erase(keys.front()), keys.front().c_str());
174  keys.pop_front();
175  while (true) {
176  auto x = t.emplace(gen(rng), gen(rng));
177  if (x.second) {
178  keys.push_back(x.first->first);
179  break;
180  }
181  }
182  }
183  state.SetItemsProcessed(state.iterations());
184  state.SetLabel(absl::StrFormat("load_factor=%.2f", t.load_factor()));
185 }
186 
187 template <typename Benchmark>
188 void CacheInSteadyStateArgs(Benchmark* bm) {
189  // The default.
190  const float max_load_factor = 0.875;
191  // When the cache is at the steady state, the probe sequence will equal
192  // capacity if there is no reclamation of deleted slots. Pick a number large
193  // enough to make the benchmark slow for that case.
194  const size_t capacity = 1 << 10;
195 
196  // Check N data points to cover load factors in [0.4, 0.8).
197  const size_t kNumPoints = 10;
198  for (size_t i = 0; i != kNumPoints; ++i)
199  bm->Arg(std::ceil(
200  capacity * (max_load_factor + i * max_load_factor / kNumPoints) / 2));
201 }
202 BENCHMARK(BM_CacheInSteadyState)->Apply(CacheInSteadyStateArgs);
203 
204 void BM_EndComparison(benchmark::State& state) {
205  std::random_device rd;
206  std::mt19937 rng(rd());
207  string_generator gen{12};
208  StringTable t;
209  while (t.size() < state.range(0)) {
210  t.emplace(gen(rng), gen(rng));
211  }
212 
213  for (auto _ : state) {
214  for (auto it = t.begin(); it != t.end(); ++it) {
217  benchmark::DoNotOptimize(it != t.end());
218  }
219  }
220 }
221 BENCHMARK(BM_EndComparison)->Arg(400);
222 
223 void BM_CopyCtor(benchmark::State& state) {
224  std::random_device rd;
225  std::mt19937 rng(rd());
226  IntTable t;
227  std::uniform_int_distribution<uint64_t> dist(0, ~uint64_t{});
228 
229  while (t.size() < state.range(0)) {
230  t.emplace(dist(rng));
231  }
232 
233  for (auto _ : state) {
234  IntTable t2 = t;
236  }
237 }
238 BENCHMARK(BM_CopyCtor)->Range(128, 4096);
239 
240 void BM_CopyAssign(benchmark::State& state) {
241  std::random_device rd;
242  std::mt19937 rng(rd());
243  IntTable t;
244  std::uniform_int_distribution<uint64_t> dist(0, ~uint64_t{});
245  while (t.size() < state.range(0)) {
246  t.emplace(dist(rng));
247  }
248 
249  IntTable t2;
250  for (auto _ : state) {
251  t2 = t;
253  }
254 }
255 BENCHMARK(BM_CopyAssign)->Range(128, 4096);
256 
257 void BM_RangeCtor(benchmark::State& state) {
258  std::random_device rd;
259  std::mt19937 rng(rd());
260  std::uniform_int_distribution<uint64_t> dist(0, ~uint64_t{});
261  std::vector<int> values;
262  const size_t desired_size = state.range(0);
263  while (values.size() < desired_size) {
264  values.emplace_back(dist(rng));
265  }
266 
267  for (auto unused : state) {
268  IntTable t{values.begin(), values.end()};
270  }
271 }
272 BENCHMARK(BM_RangeCtor)->Range(128, 65536);
273 
274 void BM_NoOpReserveIntTable(benchmark::State& state) {
275  IntTable t;
276  t.reserve(100000);
277  for (auto _ : state) {
279  t.reserve(100000);
280  }
281 }
282 BENCHMARK(BM_NoOpReserveIntTable);
283 
284 void BM_NoOpReserveStringTable(benchmark::State& state) {
285  StringTable t;
286  t.reserve(100000);
287  for (auto _ : state) {
289  t.reserve(100000);
290  }
291 }
292 BENCHMARK(BM_NoOpReserveStringTable);
293 
294 void BM_ReserveIntTable(benchmark::State& state) {
295  int reserve_size = state.range(0);
296  for (auto _ : state) {
297  state.PauseTiming();
298  IntTable t;
299  state.ResumeTiming();
301  t.reserve(reserve_size);
302  }
303 }
304 BENCHMARK(BM_ReserveIntTable)->Range(128, 4096);
305 
306 void BM_ReserveStringTable(benchmark::State& state) {
307  int reserve_size = state.range(0);
308  for (auto _ : state) {
309  state.PauseTiming();
310  StringTable t;
311  state.ResumeTiming();
313  t.reserve(reserve_size);
314  }
315 }
316 BENCHMARK(BM_ReserveStringTable)->Range(128, 4096);
317 
318 // Like std::iota, except that ctrl_t doesn't support operator++.
319 template <typename CtrlIter>
320 void Iota(CtrlIter begin, CtrlIter end, int value) {
321  for (; begin != end; ++begin, ++value) {
322  *begin = static_cast<ctrl_t>(value);
323  }
324 }
325 
326 void BM_Group_Match(benchmark::State& state) {
327  std::array<ctrl_t, Group::kWidth> group;
328  Iota(group.begin(), group.end(), -4);
329  Group g{group.data()};
330  h2_t h = 1;
331  for (auto _ : state) {
334  ::benchmark::DoNotOptimize(g.Match(h));
335  }
336 }
337 BENCHMARK(BM_Group_Match);
338 
339 void BM_Group_MaskEmpty(benchmark::State& state) {
340  std::array<ctrl_t, Group::kWidth> group;
341  Iota(group.begin(), group.end(), -4);
342  Group g{group.data()};
343  for (auto _ : state) {
345  ::benchmark::DoNotOptimize(g.MaskEmpty());
346  }
347 }
348 BENCHMARK(BM_Group_MaskEmpty);
349 
350 void BM_Group_MaskEmptyOrDeleted(benchmark::State& state) {
351  std::array<ctrl_t, Group::kWidth> group;
352  Iota(group.begin(), group.end(), -4);
353  Group g{group.data()};
354  for (auto _ : state) {
356  ::benchmark::DoNotOptimize(g.MaskEmptyOrDeleted());
357  }
358 }
359 BENCHMARK(BM_Group_MaskEmptyOrDeleted);
360 
361 void BM_Group_CountLeadingEmptyOrDeleted(benchmark::State& state) {
362  std::array<ctrl_t, Group::kWidth> group;
363  Iota(group.begin(), group.end(), -2);
364  Group g{group.data()};
365  for (auto _ : state) {
367  ::benchmark::DoNotOptimize(g.CountLeadingEmptyOrDeleted());
368  }
369 }
370 BENCHMARK(BM_Group_CountLeadingEmptyOrDeleted);
371 
372 void BM_Group_MatchFirstEmptyOrDeleted(benchmark::State& state) {
373  std::array<ctrl_t, Group::kWidth> group;
374  Iota(group.begin(), group.end(), -2);
375  Group g{group.data()};
376  for (auto _ : state) {
378  ::benchmark::DoNotOptimize(g.MaskEmptyOrDeleted().LowestBitSet());
379  }
380 }
381 BENCHMARK(BM_Group_MatchFirstEmptyOrDeleted);
382 
383 void BM_DropDeletes(benchmark::State& state) {
384  constexpr size_t capacity = (1 << 20) - 1;
385  std::vector<ctrl_t> ctrl(capacity + 1 + Group::kWidth);
386  ctrl[capacity] = ctrl_t::kSentinel;
387  std::vector<ctrl_t> pattern = {ctrl_t::kEmpty, static_cast<ctrl_t>(2),
388  ctrl_t::kDeleted, static_cast<ctrl_t>(2),
389  ctrl_t::kEmpty, static_cast<ctrl_t>(1),
391  for (size_t i = 0; i != capacity; ++i) {
392  ctrl[i] = pattern[i % pattern.size()];
393  }
394  while (state.KeepRunning()) {
395  state.PauseTiming();
396  std::vector<ctrl_t> ctrl_copy = ctrl;
397  state.ResumeTiming();
400  }
401 }
402 BENCHMARK(BM_DropDeletes);
403 
404 } // namespace
405 } // namespace container_internal
407 } // namespace absl
408 
409 // These methods are here to make it easy to examine the assembly for targeted
410 // parts of the API.
411 auto CodegenAbslRawHashSetInt64Find(absl::container_internal::IntTable* table,
412  int64_t key) -> decltype(table->find(key)) {
413  return table->find(key);
414 }
415 
417  absl::container_internal::IntTable* table, int64_t key) {
418  return table->find(key) != table->end();
419 }
420 
421 auto CodegenAbslRawHashSetInt64Insert(absl::container_internal::IntTable* table,
422  int64_t key)
423  -> decltype(table->insert(key)) {
424  return table->insert(key);
425 }
426 
428  absl::container_internal::IntTable* table, int64_t key) {
429  return table->contains(key);
430 }
431 
433  absl::container_internal::IntTable* table) {
434  for (auto x : *table) benchmark::DoNotOptimize(x);
435 }
436 
437 int odr =
443  1);
ABSL_RAW_CHECK
#define ABSL_RAW_CHECK(condition, message)
Definition: abseil-cpp/absl/base/internal/raw_logging.h:59
regen-readme.it
it
Definition: regen-readme.py:15
element
static std::function< Slot &(Slot *)> element
Definition: abseil-cpp/absl/container/internal/hash_policy_traits_test.cc:44
absl::container_internal::keys
auto keys(const Set &s) -> std::vector< typename std::decay< typename Set::key_type >::type >
Definition: abseil-cpp/absl/container/internal/hash_policy_testing.h:157
check_tracer_sanity.pattern
pattern
Definition: check_tracer_sanity.py:25
absl::StrFormat
ABSL_MUST_USE_RESULT std::string StrFormat(const FormatSpec< Args... > &format, const Args &... args)
Definition: abseil-cpp/absl/strings/str_format.h:338
CodegenAbslRawHashSetInt64FindNeEnd
bool CodegenAbslRawHashSetInt64FindNeEnd(absl::container_internal::IntTable *table, int64_t key)
Definition: abseil-cpp/absl/container/internal/raw_hash_set_benchmark.cc:416
begin
char * begin
Definition: abseil-cpp/absl/strings/internal/str_format/float_conversion.cc:1007
size
size_t size
Definition: abseil-cpp/absl/container/internal/raw_hash_set_benchmark.cc:147
capacity
uint16_t capacity
Definition: protobuf/src/google/protobuf/descriptor.cc:948
std::tr1::make_tuple
tuple make_tuple()
Definition: cares/cares/test/gmock-1.8.0/gtest/gtest.h:1619
CodegenAbslRawHashSetInt64Insert
auto CodegenAbslRawHashSetInt64Insert(absl::container_internal::IntTable *table, int64_t key) -> decltype(table->insert(key))
Definition: abseil-cpp/absl/container/internal/raw_hash_set_benchmark.cc:421
CodegenAbslRawHashSetInt64Contains
bool CodegenAbslRawHashSetInt64Contains(absl::container_internal::IntTable *table, int64_t key)
Definition: abseil-cpp/absl/container/internal/raw_hash_set_benchmark.cc:427
absl::container_internal::RawHashSetTestOnlyAccess::GetSlots
static auto GetSlots(const C &c) -> decltype(c.slots_)
Definition: abseil-cpp/absl/container/internal/raw_hash_set_benchmark.cc:31
absl::string_view
Definition: abseil-cpp/absl/strings/string_view.h:167
testing::internal::string
::std::string string
Definition: bloaty/third_party/protobuf/third_party/googletest/googletest/include/gtest/internal/gtest-port.h:881
benchmark::DoNotOptimize
BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const &value)
Definition: benchmark/include/benchmark/benchmark.h:375
absl::container_internal::kEmpty
@ kEmpty
xds_manager.p
p
Definition: xds_manager.py:60
ABSL_NAMESPACE_END
#define ABSL_NAMESPACE_END
Definition: third_party/abseil-cpp/absl/base/config.h:171
CodegenAbslRawHashSetInt64Iterate
void CodegenAbslRawHashSetInt64Iterate(absl::container_internal::IntTable *table)
Definition: abseil-cpp/absl/container/internal/raw_hash_set_benchmark.cc:432
ABSL_NAMESPACE_BEGIN
#define ABSL_NAMESPACE_BEGIN
Definition: third_party/abseil-cpp/absl/base/config.h:170
asyncio_get_stats.args
args
Definition: asyncio_get_stats.py:40
absl::container_internal::PairArgs
std::pair< std::tuple<>, std::tuple<> > PairArgs()
Definition: abseil-cpp/absl/container/internal/container_memory.h:178
absl::move
constexpr absl::remove_reference_t< T > && move(T &&t) noexcept
Definition: abseil-cpp/absl/utility/utility.h:221
end
char * end
Definition: abseil-cpp/absl/strings/internal/str_format/float_conversion.cc:1008
hpack_encoder_fixtures::Args
Args({0, 16384})
int64_t
signed __int64 int64_t
Definition: stdint-msvc2008.h:89
absl::container_internal::Group
GroupPortableImpl Group
Definition: abseil-cpp/absl/container/internal/raw_hash_set.h:715
setup.v
v
Definition: third_party/bloaty/third_party/capstone/bindings/python/setup.py:42
absl::container_internal::ctrl_t
ctrl_t
Definition: abseil-cpp/absl/container/internal/raw_hash_set.h:422
gmock_output_test._
_
Definition: bloaty/third_party/googletest/googlemock/test/gmock_output_test.py:175
uint64_t
unsigned __int64 uint64_t
Definition: stdint-msvc2008.h:90
CodegenAbslRawHashSetInt64Find
auto CodegenAbslRawHashSetInt64Find(absl::container_internal::IntTable *table, int64_t key) -> decltype(table->find(key))
Definition: abseil-cpp/absl/container/internal/raw_hash_set_benchmark.cc:411
apply_impl
static std::function< int(int)> apply_impl
Definition: abseil-cpp/absl/container/internal/hash_policy_traits_test.cc:46
x
int x
Definition: bloaty/third_party/googletest/googlemock/test/gmock-matchers_test.cc:3610
gen
OPENSSL_EXPORT GENERAL_NAME * gen
Definition: x509v3.h:495
absl::container_internal::h2_t
uint8_t h2_t
Definition: abseil-cpp/absl/container/internal/raw_hash_set.h:403
transfer
static std::function< void(void *, Slot *, Slot *)> transfer
Definition: abseil-cpp/absl/container/internal/hash_policy_traits_test.cc:58
g
struct @717 g
F
#define F(b, c, d)
Definition: md4.c:112
absl::container_internal::GroupPortableImpl::kWidth
static constexpr size_t kWidth
Definition: abseil-cpp/absl/container/internal/raw_hash_set.h:655
absl::container_internal::kSentinel
@ kSentinel
Definition: bloaty/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h:263
absl::container_internal::hash_default_hash
typename container_internal::HashEq< T >::Hash hash_default_hash
Definition: abseil-cpp/absl/container/internal/hash_function_defaults.h:150
odr
int odr
Definition: abseil-cpp/absl/container/internal/raw_hash_set_benchmark.cc:437
value
const char * value
Definition: hpack_parser_table.cc:165
abseil.generate
def generate(args)
Definition: abseil-cpp/absl/abseil.podspec.gen.py:200
absl::container_internal::kDeleted
@ kDeleted
Definition: bloaty/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h:262
construct
static std::function< void(void *, Slot *, Slot)> construct
Definition: abseil-cpp/absl/container/internal/hash_policy_traits_test.cc:41
key
const char * key
Definition: hpack_parser_table.cc:164
upload.group
group
Definition: bloaty/third_party/googletest/googlemock/scripts/upload.py:397
absl::container_internal::StringHash::is_transparent
void is_transparent
Definition: abseil-cpp/absl/container/internal/hash_function_defaults.h:71
bssl::acvp::StringEq
static bool StringEq(Span< const uint8_t > a, const char *b)
Definition: modulewrapper.cc:795
absl::str_format_internal::LengthMod::t
@ t
benchmark::State
Definition: benchmark/include/benchmark/benchmark.h:503
std
Definition: grpcpp/impl/codegen/async_unary_call.h:407
values
std::array< int64_t, Size > values
Definition: abseil-cpp/absl/container/btree_benchmark.cc:608
absl::container_internal::ConvertDeletedToEmptyAndFullToDeleted
void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t *ctrl, size_t capacity)
Definition: abseil-cpp/absl/container/internal/raw_hash_set.cc:56
state
Definition: bloaty/third_party/zlib/contrib/blast/blast.c:41
table
uint8_t table[256]
Definition: hpack_parser.cc:456
absl
Definition: abseil-cpp/absl/algorithm/algorithm.h:31
absl::forward
constexpr T && forward(absl::remove_reference_t< T > &t) noexcept
Definition: abseil-cpp/absl/utility/utility.h:230
Base::Base
Base(int an_x)
Definition: bloaty/third_party/googletest/googletest/test/gtest_unittest.cc:5143
BENCHMARK
#define BENCHMARK(n)
Definition: benchmark/include/benchmark/benchmark.h:1170
asyncio_get_stats.type
type
Definition: asyncio_get_stats.py:37
absl::container_internal::RawHashSetTestOnlyAccess
Definition: abseil-cpp/absl/container/internal/raw_hash_set_benchmark.cc:29
key_type
upb_fieldtype_t key_type
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/protobuf.h:1071
compare.Benchmark
def Benchmark(outbase, bench_cpu=True, runs=12, fasttable=False)
Definition: upb/benchmarks/compare.py:56
absl::str_format_internal::LengthMod::h
@ h
absl::apply
auto apply(Functor &&functor, Tuple &&t) -> decltype(utility_internal::apply_helper(absl::forward< Functor >(functor), absl::forward< Tuple >(t), absl::make_index_sequence< std::tuple_size< typename std::remove_reference< Tuple >::type >::value >
Definition: abseil-cpp/absl/utility/utility.h:289
pair
std::pair< std::string, std::string > pair
Definition: abseil-cpp/absl/container/internal/raw_hash_set_benchmark.cc:78
alloc
std::allocator< int > alloc
Definition: abseil-cpp/absl/container/internal/hash_policy_traits_test.cc:87
destroy
static std::function< void(void *, Slot *)> destroy
Definition: abseil-cpp/absl/container/internal/hash_policy_traits_test.cc:42
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230
absl::container_internal::StringEq::is_transparent
void is_transparent
Definition: abseil-cpp/absl/container/internal/hash_function_defaults.h:82


grpc
Author(s):
autogenerated on Fri May 16 2025 02:59:58