code: def eager_benchmark(
      obj: Union[Callable, str],
      functional_api: bool = False,
      num_experiments: int = 1,
      label: Optional[str] = None,
      backends: Optional[List[str]] = None,
      devices: Optional[List[str]] = None,
      args: Optional[Tuple[Any]] = None,
      kwargs: Optional[Dict[str, Any]] = None,
      output_path="./report.csv",
  ):
      """Benchmark the function or module passed in input on the required
      backends and devices.
  
      Parameters
      ----------
      obj
          The function or module to be benchmarked with and without graph compilation.
          In case of a function from ivy's functional API, this parameter would receive
          a string which is the function name, along with functional_api set to True.
      functional_api
          Should only be set to ``True`` if the obj being passed is a part of ivy's
          functional API. (Default value = ``False``).
      num_experiments
          Option to run benchmarking multiple times to account for subtle variations.
          (Default value = 1).
      label
          The preferred name for the experiment as would be added to the csv. If no
          name is provided, then the __name__ of the obj would be picked by default
          (Default value = ``None``).
      backends
          A list of strings for backends to benchmark with. Should be among the backends
          that ivy supports (Default value = ``None``).
      devices
          A list of target devices that ivy supports with the backends. The devices that
          are invalid for a particular backend would be ignored
          (Default value  = ``None``).
      args
          The positional arguments to be passed to the obj.
      kwargs
          The keyword arguments to be passed to obj.
      output_path
          The path to the csv file to write to. By default results are written to
          reports.csv in the folder from where the script it run
          (Default value = ``None``).
  
      Examples
      --------
      With an :code:`ivy` function:
  
      >>> import ivy
      >>> from benchmark import eager_benchmark
      >>> ivy.set_backend("torch")
      >>> fn = "conv1d"
      >>> args = (
      ...     ivy.array([[[0.0], [3.0], [0.0]]], device="cpu"),
      ...     ivy.array([[[0.0]], [[1.0]], [[0.0]]], device="cpu"),
      ...     (1,),
      ...     "SAME",
      ... )
      >>> kwargs = {"data_format": "NWC", "dilations": (1,)}
      >>> eager_benchmark(
      ...     fn,
      ...     label="conv1d",
      ...     backends=["jax", "numpy", "tensorflow", "torch"],
      ...     devices=["cpu", "gpu:0"],
      ...     args=args,
      ...     kwargs=kwargs,
      ...     functional_api=True,
      ...     output_path="./ivy/report.csv"
      ... )
  
      With a compositional function:
  
      >>> import ivy
      >>> from benchmark import eager_benchmark
      >>> ivy.set_backend("torch")
      >>> def fn(*args, **kwargs):
      ...     return ivy.conv1d(*args, **kwargs) + 1
      >>> args = (
      ...     ivy.array([[[0.0], [3.0], [0.0]]], device="cpu"),
      ...     ivy.array([[[0.0]], [[1.0]], [[0.0]]], device="cpu"),
      ...     (1,),
      ...     "SAME",
      ... )
      >>> kwargs = {"data_format": "NWC", "dilations": (1,)}
      >>> eager_benchmark(
      ...     fn,
      ...     label="compos",
      ...     backends=["jax", "numpy", "tensorflow", "torch"],
      ...     devices=["cpu", "gpu:0"],
      ...     args=args,
      ...     kwargs=kwargs,
      ...     output_path="./ivy/report.csv"
      ... )
  
      With a module:
  
      >>> import ivy
      >>> from benchmark import eager_benchmark
      >>> ivy.set_backend("torch")
      >>> module = ivy.GELU(approximate=False)
      >>> args = (ivy.random_uniform(shape=(4, 32)),)
      >>> eager_benchmark(
      ...     module,
      ...     label="GELU",
      ...     backends=["jax", "numpy", "tensorflow", "torch"],
      ...     devices=["cpu", "gpu:0"],
      ...     args=args,
      ...     output_path="./ivy/report.csv"
      ... )
      """
      backends = ivy.default(backends, [])
      devices = ivy.default(devices, [])
      output_path = ivy.default(output_path, "./report.csv")
      print("\nBenchmarking backends : " + " ".join(backends))
      print(f"Number of experiments : {num_experiments}" + "\n")
      for i in range(num_experiments):
          if num_experiments > 1:
              print("====================")
              print(f"Experiment {i + 1}")
              print("====================\n")
          for backend in backends:
              with _AvoidGPUPreallocation(backend) as _:
                  print(
                      "------------------------------------------------\n"
                  )
                  print(f"backend : {backend}")
                  ivy.set_backend(backend, dynamic=True)
                  valid_devices = [
                      device
                      for device in devices
                      if device.split(":")[0] not in ivy.invalid_devices
                  ]
                  for device in valid_devices:
                      print(f"device : {device}")
                  obj_call = obj
                  if functional_api:
                      obj_call = ivy.__dict__[obj]
                  for i, device in enumerate(valid_devices):
                      args, kwargs = _move_to_device(
                          args=args, kwargs=kwargs, device=device
                      )
                      if isinstance(obj_call, ivy.Module):
                          obj_call_copy = copy.deepcopy(obj_call)
                          obj_call_copy.trace(args=args, kwargs=kwargs)
                          traced_fn = obj_call_copy
                      else:
                          traced_fn = ivy.trace(
                              obj_call, args=args, kwargs=kwargs
                          )
                      kwargs = ivy.default(kwargs, {})
                      args = ivy.default(args, ())
                      untraced_time = _compute_time(obj_call)(
                          *args, **kwargs
                      )
                      traced_time = _compute_time(traced_fn)(
                          *args, **kwargs
                      )
                      label = (
                          obj_call.__name__ if label is None else label
                      )
                      percent_speed_up = round(
                          abs(untraced_time - traced_time)
                          / untraced_time
                          * 100,
                          6,
                      )
                      df = _read_or_create_csv(output_path)
                      _write_to_csv(
                          df,
                          [
                              len(df.index),
                              label,
                              backend,
                              device,
                              untraced_time,
                              traced_time,
                              percent_speed_up,
                          ],
                          output_path,
                      )
                      args, kwargs = _move_to_device(
                          args=args, kwargs=kwargs, device="cpu"
                      )
                      ivy.clear_cached_mem_on_dev(device)
                      print(
                          LINE_UP * (len(valid_devices) - i),
                          end=LINE_CLEAR,
                      )
                      print(f"device : {device}\t --> done\n")
                  ivy.unset_backend()
      print(f"Results written to {output_path} ...")
  
imports: from typing import Any, Callable, Dict, List, Optional, Tuple, Union
  import functools
  import time
  import os
  import copy
  import importlib
  import warnings
  import numpy
  import pandas
  import matplotlib.pyplot
  import seaborn
  import ivy
label_desc: Use image node_id 5 for calling a global function with example usage: eager_benchmark(obj, functional_api, num_experiments, label, backends, devices, args, kwargs, output_path) without return types