/* Benchmark tcache hotpath allocations.
   Copyright (C) 2013-2025 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.
   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   .  */
#ifndef TEST_FUNC
# define TEST_FUNC(size) malloc(size)
# define TEST_NAME "malloc"
#endif
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "bench-util.h"
#include "bench-util.c"
#include "bench-timing.h"
#include "json-lib.h"
/* Benchmark duration in seconds.  */
#define BENCHMARK_DURATION	3
static volatile bool timeout;
size_t alloc_size;
static void
alarm_handler (int signum)
{
  timeout = true;
}
struct bench_result {
  size_t iters;
  timing_t elapsed;
};
static __always_inline size_t
malloc_benchmark_loop (void **elems, size_t nr_items)
{
  size_t iters = nr_items;
  while (!timeout)
    {
      elems[iters % nr_items] = TEST_FUNC (alloc_size);
      iters++;
      free (elems[iters % nr_items]);
    }
  return iters - nr_items;
}
static void
do_benchmark (struct bench_result *res)
{
  timing_t start, stop;
  void *elems[10];
  memset (elems, 0, sizeof(elems));
  alarm (BENCHMARK_DURATION);
  /* Ramp up cpu before measuring.  */
  bench_start ();
  TIMING_NOW (start);
  res[0].iters = malloc_benchmark_loop (elems, 1);
  TIMING_NOW (stop);
  TIMING_DIFF (res[0].elapsed, start, stop);
  timeout = false;
  alarm (BENCHMARK_DURATION);
  bench_start ();
  TIMING_NOW (start);
  res[1].iters = malloc_benchmark_loop (elems, 4);
  TIMING_NOW (stop);
  TIMING_DIFF (res[1].elapsed, start, stop);
}
static void usage (const char *name)
{
  fprintf (stderr, "%s: \n", name);
  exit (1);
}
int
main (int argc, char **argv)
{
  json_ctx_t json_ctx;
  double d_total_s, d_total_i;
  struct sigaction act;
  if (argc == 1)
    alloc_size = 1024;
  else if (argc == 2)
    {
      long ret;
      errno = 0;
      ret = strtol (argv[1], NULL, 10);
      if (errno || ret == 0)
	usage (argv[0]);
      alloc_size = ret;
    }
  else
    usage (argv[0]);
  json_init (&json_ctx, 0, stdout);
  json_document_begin (&json_ctx);
  json_attr_string (&json_ctx, "timing_type", TIMING_TYPE);
  json_attr_object_begin (&json_ctx, "functions");
  json_attr_object_begin (&json_ctx, TEST_NAME);
  memset (&act, 0, sizeof (act));
  act.sa_handler = &alarm_handler;
  sigaction (SIGALRM, &act, NULL);
  struct bench_result res[2];
  memset (res, 0, sizeof (struct bench_result) * 2);
  do_benchmark (res);
  d_total_s = res[0].elapsed;
  d_total_i = res[0].iters;
  json_attr_object_begin (&json_ctx, "simple");
  json_attr_double (&json_ctx, "alloc_size", alloc_size);
  json_attr_double (&json_ctx, "duration", d_total_s);
  json_attr_double (&json_ctx, "iterations", d_total_i);
  json_attr_double (&json_ctx, "time_per_iteration", d_total_s / d_total_i);
  json_attr_object_end (&json_ctx);
  d_total_s = res[1].elapsed;
  d_total_i = res[1].iters;
  json_attr_object_begin (&json_ctx, "optimized");
  json_attr_double (&json_ctx, "alloc_size", alloc_size);
  json_attr_double (&json_ctx, "duration", d_total_s);
  json_attr_double (&json_ctx, "iterations", d_total_i);
  json_attr_double (&json_ctx, "time_per_iteration", d_total_s / d_total_i);
  json_attr_object_end (&json_ctx);
  json_attr_object_end (&json_ctx);
  json_attr_object_end (&json_ctx);
  json_document_end (&json_ctx);
  return 0;
}