#!/usr/bin/env perl
use v5.38;
use Object::Pad;
use Benchmark qw(cmpthese timethese);

# Compara el patron actual (require X unless $INC{...}; $field //= ...)
# contra el patron propuesto ($field //= do { require X; ... }).
#
# Mide el coste por llamada en el caso CALIENTE (modulo ya cargado y
# field ya inicializado), que es el 99.9% de las llamadas reales.

package Dummy {
  sub new { bless {}, shift }
}

class Bench::Current {
  field $_schema;

  method schema {
    require Dummy unless $INC{'Dummy.pm'};
    $_schema //= Dummy->new;
  }
}

class Bench::Proposed {
  field $_schema;

  method schema {
    $_schema //= do {
      require Dummy;
      Dummy->new;
    };
  }
}

# Truco para que require encuentre el paquete inline sin fichero real
$INC{'Dummy.pm'} = __FILE__;

my $current  = Bench::Current->new;
my $proposed = Bench::Proposed->new;

# Caliente: primera llamada inicializa el field
$current->schema  for 1 .. 2;
$proposed->schema for 1 .. 2;

say "=== Hot path: field ya inicializado (caso 99.9%) ===";
cmpthese(-3, {
  current  => sub { $current->schema  for 1 .. 1000 },
  proposed => sub { $proposed->schema for 1 .. 1000 },
});

say "\n=== Cold path: primera llamada por instancia (caso 0.1%) ===";
cmpthese(-2, {
  current => sub {
    my $obj = Bench::Current->new;
    $obj->schema;
  },
  proposed => sub {
    my $obj = Bench::Proposed->new;
    $obj->schema;
  },
});

say "\n=== Solo el overhead de la comprobacion (sin llamada a metodo) ===";
my $loaded = 1;
my $sch    = bless {}, 'Dummy';
cmpthese(-3, {
  inc_check => sub {
    for (1 .. 1000) {
      my $x;
      $x = 1 unless $INC{'Dummy.pm'};
      $sch //= $sch;
    }
  },
  just_defor => sub {
    for (1 .. 1000) {
      $sch //= do { 1 };
    }
  },
});
