Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5fd02a8
fix: Normalize global variable names in BytecodeCompiler
fglock Feb 17, 2026
38fb403
feat: Add error handling and fix issues in interpreter eval mode
fglock Feb 17, 2026
32943d2
feat: Add wantarray operator support to BytecodeCompiler
fglock Feb 17, 2026
c077dec
wip: Fix context propagation for eval interpreter mode
fglock Feb 17, 2026
306cd3e
fix: Preserve context for last statement in interpreter mode blocks
fglock Feb 17, 2026
b243ca9
fix: Remove incorrect STORE after post-increment/decrement in interpr…
fglock Feb 17, 2026
cc2c812
fix: Add local variable restoration and local($var)=value support
fglock Feb 17, 2026
ca103bb
fix: Add strict subs enforcement in BytecodeCompiler
fglock Feb 17, 2026
a03816e
feat: Add warn operator support to interpreter mode
fglock Feb 17, 2026
3235235
fix: Prevent nextRegister reset for eval STRING with parentRegistry
fglock Feb 17, 2026
d16d0d4
docs: Add investigation document for remaining interpreter failures
fglock Feb 17, 2026
3f261c2
fix: Preserve capturedVarIndices from constructor in detectClosureVar…
fglock Feb 17, 2026
0aebc2f
fix: Use two registers for POST_AUTOINCREMENT/DECREMENT in interpreter
fglock Feb 17, 2026
5f5e0e9
fix: Support closure variable capture in interpreter mode
fglock Feb 17, 2026
c6f0728
fix: Track all declared variables for eval STRING capture
fglock Feb 17, 2026
84e140d
feat: Add package version and class keyword support to interpreter
fglock Feb 17, 2026
1d9ff77
fix: Correct parameter order for RuntimeRegex.matchRegex in interpreter
fglock Feb 17, 2026
67aeaf7
feat: Add study operator support to interpreter
fglock Feb 17, 2026
6f1eb5a
feat: Add require operator support to interpreter
fglock Feb 17, 2026
23f9207
feat: Add pos operator with lvalue support to interpreter
fglock Feb 17, 2026
1afe189
feat: Add index and rindex operators to interpreter
fglock Feb 17, 2026
512fcb5
feat: Add bitwise compound assignment operators to interpreter
fglock Feb 17, 2026
f7fbea7
fix: Modify compound assignments in place for captured variables
fglock Feb 17, 2026
fb89eae
fix: Allow compound assignments on expression results
fglock Feb 17, 2026
4c88acf
feat: Add support for binary&=, binary|=, binary^= operators
fglock Feb 17, 2026
b7016b5
fix: Support binary&=, binary|=, binary^= operators in interpreter mode
fglock Feb 17, 2026
1f3b4dc
feat: Add string bitwise compound assignment operators to interpreter
fglock Feb 17, 2026
8836cea
feat: Add bitwise operators (&, |, ^, ~, &., |., ^., ~.) to interpreter
fglock Feb 17, 2026
c167c2e
fix: Return proper count for array assignments in eval STRING scalar …
fglock Feb 17, 2026
e379270
feat: Add stat, lstat, and all file test operators to interpreter
fglock Feb 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 198 additions & 0 deletions dev/bench/memory_benchmark.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
#!/usr/bin/env perl
use strict;
use warnings;
use Time::HiRes qw(time);
use Cwd qw(abs_path);
use File::Basename qw(dirname);
use File::Spec;

# Find repo root
sub find_repo_root {
my $dir = abs_path(dirname($0));
while (1) {
my $jperl = File::Spec->catfile($dir, 'jperl');
my $git = File::Spec->catdir($dir, '.git');
if (-f $jperl && -d $git) {
return $dir;
}
my $parent = abs_path(File::Spec->catdir($dir, File::Spec->updir()));
last if !defined($parent) || $parent eq $dir;
$dir = $parent;
}
die "Unable to locate repo root\n";
}

my $repo_root = find_repo_root();
my $jperl = File::Spec->catfile($repo_root, 'jperl');

# Memory benchmark workloads with delta measurement
my @workloads = (
{
name => "Array creation (1M elements)",
code_before => 'use Devel::Peek; print "READY\n"; my $line = <STDIN>;',
code_create => 'my @arr = (1..1_000_000);',
code_after => 'my $sum = 0; $sum += $_ for @arr; print $sum, "\n";',
},
{
name => "Hash creation (100K entries)",
code_before => 'print "READY\n"; my $line = <STDIN>;',
code_create => 'my %hash; $hash{$_} = $_ * 2 for (1..100_000);',
code_after => 'my $sum = 0; $sum += $hash{$_} for keys %hash; print $sum, "\n";',
},
{
name => "String operations (10K iterations)",
code_before => 'print "READY\n"; my $line = <STDIN>;',
code_create => 'my $str = "x" x 1000; my $result = ""; for (1..10_000) { $result .= substr($str, 0, 10); }',
code_after => 'print length($result), "\n";',
},
{
name => "Nested data structures",
code_before => 'print "READY\n"; my $line = <STDIN>;',
code_create => 'my @data; for my $i (1..1000) { push @data, { id => $i, values => [1..$i] }; }',
code_after => 'my $sum = 0; for my $item (@data) { $sum += scalar(@{$item->{values}}); } print $sum, "\n";',
},
);

print "# Memory Usage Benchmark: perl vs jperl\n\n";
print "Measuring peak memory usage (RSS) for various workloads.\n";
print "Using /usr/bin/time to capture memory statistics.\n\n";

# Check if /usr/bin/time exists
if (!-x '/usr/bin/time') {
die "Error: /usr/bin/time not found. This script requires GNU time or BSD time.\n";
}

# Detect time format (GNU vs BSD)
my $time_format;
my $time_test = `/usr/bin/time -l echo test 2>&1`;
if ($time_test =~ /maximum resident set size/) {
# BSD time (macOS)
$time_format = 'bsd';
} else {
# Try GNU time
$time_test = `/usr/bin/time -v echo test 2>&1`;
if ($time_test =~ /Maximum resident set size/) {
$time_format = 'gnu';
} else {
die "Error: Unable to determine time format. Need GNU time or BSD time.\n";
}
}

print "Detected time format: $time_format\n\n";

sub get_memory_usage {
my ($interpreter, $code) = @_;

# Write code to a temp file to avoid shell quoting issues
my $tmpfile = "/tmp/perlbench_$$.pl";
my $timefile = "/tmp/perlbench_time_$$.txt";

open my $fh, '>', $tmpfile or die "Cannot write to $tmpfile: $!";
print $fh $code;
close $fh;

# Use shell to redirect time output to a file
my $cmd;
if ($time_format eq 'bsd') {
# BSD time: redirect stderr to file
$cmd = "/usr/bin/time -l $interpreter $tmpfile > /dev/null 2> $timefile";
} else {
# GNU time
$cmd = "/usr/bin/time -v $interpreter $tmpfile > /dev/null 2> $timefile";
}

system($cmd);

# Read the time output
open my $tfh, '<', $timefile or do {
unlink $tmpfile;
unlink $timefile;
return undef;
};
my $output = do { local $/; <$tfh> };
close $tfh;

unlink $tmpfile;
unlink $timefile;

if ($time_format eq 'bsd') {
# Match format: " 1228800 maximum resident set size"
if ($output =~ /^\s*(\d+)\s+maximum resident set size/m) {
# BSD reports in bytes
return int($1 / 1024); # Convert to KB
}
} else {
# GNU time
if ($output =~ /Maximum resident set size \(kbytes\): (\d+)/) {
return $1;
}
}

return undef;
}

sub format_memory {
my ($kb) = @_;
return "N/A" unless defined $kb;

if ($kb < 1024) {
return sprintf("%d KB", $kb);
} elsif ($kb < 1024 * 1024) {
return sprintf("%.1f MB", $kb / 1024);
} else {
return sprintf("%.2f GB", $kb / (1024 * 1024));
}
}

sub format_ratio {
my ($perl_mem, $jperl_mem) = @_;
return "N/A" unless defined $perl_mem && defined $jperl_mem && $perl_mem > 0;

my $ratio = $jperl_mem / $perl_mem;
return sprintf("%.2fx", $ratio);
}

# Run benchmarks
my @results;

for my $workload (@workloads) {
print "Running: $workload->{name}\n";

my $code = $workload->{code};

# Run with perl
my $perl_mem = get_memory_usage('perl', $code);
print " perl: " . format_memory($perl_mem) . "\n";

# Run with jperl
my $jperl_mem = get_memory_usage($jperl, $code);
print " jperl: " . format_memory($jperl_mem) . "\n";

my $ratio = format_ratio($perl_mem, $jperl_mem);
print " ratio: $ratio\n\n";

push @results, {
name => $workload->{name},
perl_mem => $perl_mem,
jperl_mem => $jperl_mem,
ratio => $ratio,
};
}

# Print summary table
print "\n# Summary\n\n";
print "| Workload | Perl 5 | PerlOnJava | Ratio (jperl/perl) |\n";
print "|----------|--------|------------|--------------------|\n";

for my $result (@results) {
printf "| %-40s | %10s | %10s | %10s |\n",
$result->{name},
format_memory($result->{perl_mem}),
format_memory($result->{jperl_mem}),
$result->{ratio};
}

print "\n";
print "Note: Memory measurements are peak RSS (Resident Set Size).\n";
print "JVM startup overhead is included in these measurements.\n";
print "For long-running processes, the overhead becomes less significant.\n";
Loading