LaTeXML

 view release on metacpan or  search on metacpan

lib/LaTeXML/Core/Definition.pm  view on Meta::CPAN

#======================================================================
# Overriding methods
sub stringify {
  my ($self) = @_;
  my $type = ref $self;
  $type =~ s/^LaTeXML:://;
  my $name = ($$self{alias} || $$self{cs}->getCSName);
  return $type . '[' . ($$self{parameters}
    ? $name . ' ' . Stringify($$self{parameters}) : $name) . ']'; }

sub toString {
  my ($self) = @_;
  return ($$self{parameters}
    ? ToString($$self{cs}) . ' ' . ToString($$self{parameters}) : ToString($$self{cs})); }

#======================================================================
# Tracing support
sub tracingCSName {
  my ($self) = @_;
  my $parameters = $$self{parameters};
  return ToString($self->getCSName)
    # Show parameter string too
    . ($parameters ? ' ' . ToString($parameters) : '')
    # And if this was \let to something show the name it was called by
    . ($LaTeXML::CURRENT_TOKEN && !$$self{cs}->equals($LaTeXML::CURRENT_TOKEN)
    ? ' [for ' . ToString($LaTeXML::CURRENT_TOKEN) . ']' : ''); }

sub tracingArgs {
  my ($self, @args) = @_;
  my $i = 1;
  return join("\n", map { '#' . $i++ . '<-' . tracingArgToString($_) } @args); }

# Annoying special handing of registers
sub tracingArgToString {
  my ($arg) = @_;
  return (ref $arg eq 'ARRAY' ? '[' . join(',', map { ToString($_) } @$arg) . ']' : ToString($arg)); }

#======================================================================
# Profiling support
#======================================================================
# If the value PROFILING is true, we'll collect some primitive profiling info.

# Start profiling $CS (typically $LaTeXML::CURRENT_TOKEN)
# Call from within ->invoke.
# $mode = expand | digest | absorb
sub startProfiling {
  my ($cs, $mode) = @_;
  my $name  = $cs->getCSName;
  my $entry = $STATE->lookupMapping('runtime_profile', $name);
  # [#calls, max_depth, inclusive_time, exclusive_time, #pending
  #   starts of pending calls...]
  if (!defined $entry) {
    $entry = [0, 0, 0, 0, 0]; $STATE->assignMapping('runtime_profile', $name, $entry); }
  $$entry[0]++ unless $mode eq 'absorb';    # One more call.
  $$entry[4]++;                             # One more pending...
                                            #Debug("START PROFILE $mode of ".ToString($cs));
  $STATE->pushValue('runtime_stack', [$name, $mode, [Time::HiRes::gettimeofday], $entry]);
  return; }

# Stop profiling $CS.
# Complication w/Macros: If the expansion of a macro contains CS's that read tokens,
# the end MARKER of macros may get read before the macro's effects have really been processed.
# So we need to ignore a stop on a macro that isn't at the top of the stack,
# and conversely, automatically stop a macro that is at the top above a CS that is being stopped.
sub stopProfiling {
  my ($cs, $mode) = @_;
  $cs = $cs->getString if $cs->getCatcode == CC_MARKER;    # Special case for macros!!
  return unless ref $cs;
  my $name      = $cs->getCSName;
  my $stack     = $STATE->lookupValue('runtime_stack');
  my $currdepth = scalar(@$stack);
  my $prevdepth = $STATE->lookupValue('runtime_maxdepth') || '0';
  $STATE->assignValue('runtime_maxdepth' => $currdepth, 'global') if $currdepth > $prevdepth;

  while (@{$stack}) {
    my ($top, $topmode, $t0, $entry) = @{ $$stack[-1] };
    if ((($top ne $name) || ($topmode ne $mode)) && ($topmode ne 'expand')) {
      return if $mode eq 'expand';    # No error (yet) if this is a macro end marker.
      Debug("PROFILE Error: ending $mode of $name but stack holds "
          . join(',', map { $$_[0] . '(' . $$_[1] . ')' } @$stack) . ", $top ($topmode)");
      return; }
    pop(@$stack);
    my $duration = Time::HiRes::tv_interval($t0, [Time::HiRes::gettimeofday]);
    my $depth    = $$entry[4];
    if ($depth > $$entry[1]) {
      $$entry[1] = $depth; }
    $$entry[2] += $duration if $depth == 1;    # add to inclusive time only in uppermost call
    $$entry[3] += $duration;                   # add to exclusive time (see below)
    $$entry[4]--;
    if (my $caller = $$stack[-1]) {
      my ($callername, $callermode, $callerstart, $callerentry) = @$caller;
      $$callerentry[3] -= $duration; }         # Remove our cost from caller's exclusive time.
    return if $top eq $name; }
  Debug("PROFILE Error: ending $mode of $name but stack is empty")
    unless $mode eq 'expand';
  return; }

our $MAX_PROFILE_ENTRIES = 30;    # [CONSTANT]

# Print out profiling information, if any was collected
sub showProfile {
  if (my $profile = $STATE->lookupValue('runtime_profile')) {
    my @cs    = keys %$profile;
    my $calls = 0;
    map { $calls += $$profile{$_}[0] } @cs;
    my $depth    = $STATE->lookupValue('runtime_maxdepth');
    my @frequent = sort { $$profile{$b}[0] <=> $$profile{$a}[0] } @cs;
    @frequent = @frequent[0 .. $MAX_PROFILE_ENTRIES];
    my @deepest = sort { $$profile{$b}[1] <=> $$profile{$a}[1] } @cs;
    @deepest = @deepest[0 .. $MAX_PROFILE_ENTRIES];
    my @inclusive = sort { $$profile{$b}[2] <=> $$profile{$a}[2] } @cs;
    @inclusive = @inclusive[0 .. $MAX_PROFILE_ENTRIES];
    my @exclusive = sort { $$profile{$b}[3] <=> $$profile{$a}[3] } @cs;
    @exclusive = @exclusive[0 .. $MAX_PROFILE_ENTRIES];
    Debug("Profiling results:");
    Debug("Total calls: $calls; Maximum depth: $depth");
    Debug("Most frequent:\n   "
        . join(', ', map { $_ . ':' . $$profile{$_}[0] } @frequent));
    Debug("Deepest :\n   "
        . join(', ', map { $_ . ':' . $$profile{$_}[1] } @deepest));
    Debug("Most expensive inclusive:\n   "



( run in 1.650 second using v1.01-cache-2.11-cpan-d8267643d1d )