SlideShare a Scribd company logo
Raku Memory Manglement:
Checking yourself out.
Steven Lembark
Workhorse Computing
lembark@wrkhors.com
Yes, size() matters.
but it isn’t available in Raku.
Process-level stats.
Mainly “RSS”.
getrusage(2).
Acquiring & analyze data.
Raku tools.
RSS?
“Resident Set Size”
Virtual pages in physical memory.
Accessible without a page fault.
Non-resident VM may be swapped.
Requires a page fault to access.
Goals of memory managment:
Work within RSS.
Reduce page faults.
Avoid hard faults & swapping.
getrusage(2)
Returns process memory stats.
Aggregate values.
Results constrained by system limits.
getrusage(2)
struct rusage
{
struct timeval ru_utime; /* user CPU time used */
struct timeval ru_stime; /* system CPU time used */
long ru_maxrss; /* maximum resident set size */
long ru_ixrss; /* integral shared memory size */
long ru_idrss; /* integral unshared data size */
long ru_isrss; /* integral unshared stack size */
long ru_minflt; /* page reclaims (soft page faults) */
long ru_majflt; /* page faults (hard page faults) */
long ru_nswap; /* swaps */
long ru_inblock; /* block input operations */
long ru_oublock; /* block output operations */
long ru_msgsnd; /* IPC messages sent */
long ru_msgrcv; /* IPC messages received */
long ru_nsignals; /* signals received */
long ru_nvcsw; /* voluntary context switches */
long ru_nivcsw; /* involuntary context switches */
};
POSIX
getrusage(2)
struct rusage
{
struct timeval ru_utime; /* user CPU time used */
struct timeval ru_stime; /* system CPU time used */
long ru_maxrss; /* maximum resident set size */
long ru_ixrss; /* integral shared memory size */
long ru_idrss; /* integral unshared data size */
long ru_isrss; /* integral unshared stack size */
long ru_minflt; /* page reclaims (soft page faults) */
long ru_majflt; /* page faults (hard page faults) */
long ru_nswap; /* swaps */
long ru_inblock; /* block input operations */
long ru_oublock; /* block output operations */
long ru_msgsnd; /* IPC messages sent */
long ru_msgrcv; /* IPC messages received */
long ru_nsignals; /* signals received */
long ru_nvcsw; /* voluntary context switches */
long ru_nivcsw; /* involuntary context switches */
};
Linux
getrusage(2)
struct rusage
{
struct timeval ru_utime; /* user CPU time used */
struct timeval ru_stime; /* system CPU time used */
long ru_maxrss; /* maximum resident set size */
long ru_minflt; /* page reclaims (soft page faults) */
long ru_majflt; /* page faults (hard page faults) */
long ru_inblock; /* block input operations */
long ru_oublock; /* block output operations */
long ru_nvcsw; /* voluntary context switches */
long ru_nivcsw; /* involuntary context switches */
};
Only max
RSS.
getrusage(2)
struct rusage
{
struct timeval ru_utime; /* user CPU time used */
struct timeval ru_stime; /* system CPU time used */
long ru_maxrss; /* maximum resident set size */
long ru_minflt; /* page reclaims (soft page faults) */
long ru_majflt; /* page faults (hard page faults) */
long ru_inblock; /* block input operations */
long ru_oublock; /* block output operations */
long ru_nvcsw; /* voluntary context switches */
long ru_nivcsw; /* involuntary context switches */
};
Fault
counts.
Viewing RSS
Telemetry module.
Takes periodic snapshots.
Allows inserting a label to track events.
Core with nqp.
Not synchronous with tasks.
Raku
Viewing RSS
ProcStats
Soon to be on CPAN.
Exports “dump-rusage”.
Differences from first sample.
Only output changes.
Track wallclock time.
Optional label.
:final Output all stats compared to first sample
ProcStats
sub dump-rusage
(
Bool() :$final = False,
Bool() :$first = $final,
Bool() :$force = $final,
Stringy() :$label = $final ?? 'Final' !! ''
)
is export( :DEFAULT )
:first Values compared to first sample (vs. last).
ProcStats
sub dump-rusage
(
Bool() :$final = False,
Bool() :$first = $final,
Bool() :$force = $final,
Stringy() :$label = $final ?? 'Final' !! ''
)
is export( :DEFAULT )
:force Write all stats (vs. only changed).
ProcStats
sub dump-rusage
(
Bool() :$final = False,
Bool() :$first = $final,
Bool() :$force = $final,
Stringy() :$label = $final ?? 'Final' !! ''
)
is export( :DEFAULT )
:label Add “label” key (default from :final).
ProcStats
sub dump-rusage
(
Bool() :$final = False,
Bool() :$first = $final,
Bool() :$force = $final,
Stringy() :$label = $final ?? 'Final' !! ''
)
is export( :DEFAULT )
Wallclock time
Elapsed vs. CPU
sub dump-rusage
(
Bool() :$final = False,
Bool() :$first = $final,
Bool() :$force = $final,
Stringy() :$label = $final ?? 'Final' !! ''
)
is export( :DEFAULT )
{
my $wtime = now.Num;
Wallclock time
Sample at top to avoid time-shift.
sub dump-rusage
(
Bool() :$final = False,
Bool() :$first = $final,
Bool() :$force = $final,
Stringy() :$label = $final ?? 'Final' !! ''
)
is export( :DEFAULT )
{
my $wtime = now.Num;
Values from RSS
constant FIELDS =
<
maxrss ixrss idrss isrss minflt majflt
nswap inblock oublock msgsnd msgrcv nsignals
nvcsw nivcsw
>;
constant IGNORE = <ixrss idrss isrss ...>;
constant REPORT =
< maxrss majflt minflt inblock oublock >;
constant MICRO = 10 ** -6;
COMPARE avoids reporting on CPU swithes.
Track progress
Unchanged samples are not reported.
$passes tracks total calls.
state $passes = 0;
state %last = ();
Acquire data
Times are sec + µsec, deal with them separately.
“Z=>” zips fields & values into a hash.
use nqp;
nqp::getrusage( my int @raw );
my ( $user_s, $user_us, $syst_s, $syst_us )
= splice @raw, 0, 4;
my %sample = FIELDS Z=> @raw;
%sample{ IGNORE } :delete;
Making time
my $utime
= ( $user_s + $user_us / 1_000_000 ).round( MICRO );
my $stime
= ( $syst_s + $syst_us / 1_000_000 ).round( MICRO );
user & system time begin as two ints.
Round gives reasonable precision in output.
Store baseline values.
state %last =
state %first =
(
|%sample,
:$wtime,
:$utime,
:$stime,
);
First is never updated.
Get a working “last” value on the first pass.
Store baseline values.
Flatten %sample into pairs.
state %last =
state %first =
(
|%sample,
:$wtime,
:$utime,
:$stime,
);
Store baseline values.
Times as pairs.
state %last =
state %first =
(
|%sample,
:$wtime,
:$utime,
:$stime,
);
First is last at first.
After first last is last.
my %prior
= $first
?? %first
!! %last
;
What to compare?
Force reports full sample.
COMPARE limits keys compare to %prior & output.
my %curr
= ( $force || ! $passes )
?? %sample
!! do
{
my @diffs
= REPORT.grep( { %sample{$_} != %prior{$_} } );
@diffs Z=> %sample{ @diffs }
};
Write out one stat heading & value.
Compute column width once during execution.
sub write-stat ( Pair $p )
{
note
sprintf
'%-*s : %s',
once {FIELDS».chars.max},
$p.key,
$p.value
;
}
Write progressive value
Numerics compared to starting baseline.
Simplifies tracking code results.
sub write-diff ( Pair $p )
{
my $k = $p.key;
my $v = $p.value - %first{ $k };
write-stat $k => $v;
}
First pass writes all stats.
First pass has to report baseline values.
state $write = &write-stat;
First pass writes all stats.
First pass has to report baseline values.
After that report differences.
state &write = &write-stat;
...
write $stat;
...
once { &write = &write-diff };
for %curr.sort -> $stat
{
FIRST
{
note '---';
write-stat ( output => $++ );
write-stat ( :$passes );
write-stat ( :$label ) if $label;
write-diff ( :$wtime );
write-diff ( :$utime );
write-diff ( :$stime );
}
write $stat
}
for %curr.sort -> $stat
{
FIRST
{
note '---';
write-stat ( output => $++ );
write-stat ( :$passes );
write-stat ( :$label ) if $label;
write-diff ( :$wtime );
write-diff ( :$utime );
write-diff ( :$stime );
}
write $stat
}
for %curr.sort -> $stat
{
FIRST
{
note '---';
write-stat ( output => $++ );
write-stat ( :$passes );
write-stat ( :$label ) if $label;
write-diff ( :$wtime );
write-diff ( :$utime );
write-diff ( :$stime );
}
write $stat
}
for %curr.sort -> $stat
{
FIRST
{
note '---';
write-stat ( output => $++ );
write-stat ( :$passes );
write-stat ( :$label ) if $label;
write-diff ( :$wtime );
write-diff ( :$utime );
write-diff ( :$stime );
}
write $stat
}
for %curr.sort -> $stat
{
FIRST
{
note '---';
write-stat ( output => $++ );
write-stat ( :$passes );
write-stat ( :$label ) if $label;
write-diff ( :$wtime );
write-diff ( :$utime );
write-diff ( :$stime );
}
write $stat
}
Last steps
Up total count.
Store current sample for re-use.
++$passes;
%last = %sample;
once { &write = &write-diff };
Baseline usage
Bare for-loop
Shows overhead of rusage output.
#!/usr/bin/env Raku
use v6.d;
use FindBin::libs;
use ProcStats;
dump-rusage for 1 .. 1_000;
dump-rusage( :final );
Sample 0
Pass 0 as all values.
Baseline for RSS &
friends.
---
output : 0
passes : 0
wtime : 1560968261.746507
utime : 0.344793
stime : 0.020896
inblock : 0
majflt : 0
maxrss : 99732
minflt : 25039
nivcsw : 10
nvcsw : 204
oublock : 64
Sample 0
wtime is ‘real world’.
Reasonable candidate
key for sample history.
---
output : 0
passes : 0
wtime : 1560968261.746507
utime : 0.344793
stime : 0.020896
inblock : 0
majflt : 0
maxrss : 99732
minflt : 25039
nivcsw : 10
nvcsw : 204
oublock : 64
Sample 0
RSS is ~100MiB at
startup.
---
output : 0
passes : 0
wtime : 1560968261.746507
utime : 0.344793
stime : 0.020896
inblock : 0
majflt : 0
maxrss : 99732
minflt : 25039
nivcsw : 10
nvcsw : 204
oublock : 64
Output
Output 1+ are relative to %first.
Sample N ---
output : 1
passes : 1
wtime : 0.0081639
utime : 0.007295
stime : 0.000228
maxrss : 1588
minflt : 255
---
...
Output
Output 1+ are relative to %first.
maxrss & minflt cause output.
Output ---
output : 1
passes : 1
wtime : 0.0081639
utime : 0.007295
stime : 0.000228
maxrss : 1588
minflt : 255
---
...
Output
Inermediate passes.
Output #130:
minflt 1758 -> 1759.
Output ---
output : 129
passes : 812
wtime : 0.4603018
utime : 0.60607
stime : 0.000175
minflt : 1758
---
output : 130
passes : 813
wtime : 0.4636268
utime : 0.609417
stime : 0.000175
minflt : 1759
---
Output
getrulsage( :final );
Shows all fields.
About 1/8 of passes had output.
“Final” sample ---
output : 131
passes : 1000
label : Final
wtime : 0.5086002
utime : 0.654374
stime : 0.000175
inblock : 0
majflt : 0
maxrss : 6996
minflt : 1759
nivcsw : 2
nvcsw : 35
oublock : 0
Default label.
---
output : 131
passes : 1000
label : Final
wtime : 0.5086002
utime : 0.654374
stime : 0.000175
inblock : 0
majflt : 0
maxrss : 6996
minflt : 1759
nivcsw : 2
nvcsw : 35
oublock : 0
“Final” sample
Fairly low overhead.
---
output : 131
passes : 1000
label : Final
wtime : 0.5086002
utime : 0.654374
stime : 0.000175
inblock : 0
majflt : 0
maxrss : 6996
minflt : 1759
nivcsw : 2
nvcsw : 35
oublock : 0
“Final” sample
Multiple threads:
wallclock < user.
---
output : 131
passes : 1000
label : Final
wtime : 0.5086002
utime : 0.654374
stime : 0.000175
inblock : 0
majflt : 0
maxrss : 6996
minflt : 1759
nivcsw : 2
nvcsw : 35
oublock : 0
“Final” sample
RSS grew by ~7MiB
---
output : 131
passes : 1000
label : Final
wtime : 0.5086002
utime : 0.654374
stime : 0.000175
inblock : 0
majflt : 0
maxrss : 6996
minflt : 1759
nivcsw : 2
nvcsw : 35
oublock : 0
“Final” sample
Really do something...
Simulate traking userid’s on a web server:
Add a hash key.
Increment a random value.
Drop a key.
Roll your own
Random hash key via random sample.
sub random-string
(
Int() :$size = ( 1 .. 10 ).pick
--> Str
)
{
constant alpha = [ 'a' ... 'z', 'A' ... 'Z' ];
alpha.roll( $size ).join;
}
Roll your own
pick() returns a single, random value.
sub random-string
(
Int() :$size = ( 1 .. 10 ).pick
--> Str
)
{
constant alpha = [ 'a' ... 'z', 'A' ... 'Z' ];
alpha.roll( $size ).join;
}
Roll your own
roll() returns a random sample.
sub random-string
(
Int() :$size = ( 1 .. 10 ).pick
--> Str
)
{
constant alpha = [ 'a' ... 'z', 'A' ... 'Z' ];
alpha.roll( $size ).join;
}
Fake userid
Track key counts, active keys.
sub user-add
{
++%user-data{ random-string };
++$adds;
$max-keys = max $max-keys, %user-data.elems;
}
Random key selection
sub user-drop
{
%user-data or return;
++$drops;
%user-data{ %user-data.pick.key } :delete;
}
sub user-op
{
%user-data or return;
++$ops;
++%user-data{ %user-data.pick.key };
}
Randomized, weighted trial.
for 1 .. 1000
{
constant weighted_operations =
(
&user-add => 0.10,
&user-drop => 0.10,
&user-op => 0.80,
).Mix;
weighted_operations.roll( 1_000 )».();
dump-rusage(label => 'Keys: '~%user‑data.elems );
}
Define op’s and weights.
for 1 .. 1000
{
constant weighted_operations =
(
&user-add => 0.10,
&user-drop => 0.10,
&user-op => 0.80,
).Mix;
weighted_operations.roll( 1_000 )».();
dump-rusage(label => 'Keys: '~%user‑data.elems );
}
1000 iterations of trial.
for 1 .. 1000
{
constant weighted_operations =
(
&user-add => 0.10,
&user-drop => 0.10,
&user-op => 0.80,
).Mix;
weighted_operations.roll( 1_000 )».();
dump-rusage(label => 'Keys: '~%user‑data.elems );
}
Report summary
“say” is stdout, dump-rusage is stderr.
:final uses %first as reference for values.
dump-rusage( :final );
say 'Total adds: ' ~ $adds;
say 'Total drops: ' ~ $drops;
say 'Total ops: ' ~ $ops;
say 'Max keys: ' ~ $max-keys;
say 'Final keys: ' ~ %user-data.elems;
Getting stats
Easier to read with separate files.
$ ./rand-user-table >stats.out 2>stats.yaml;
Stats results
Final results from
“say”.
Total adds: 99738
Total drops: 98755
Total ops: 787133
Max keys: 213
Final keys: 144
Stats results
Final sample:
1000 iterations/pass.
Extra time from
threading.
~18MiB RSS growth.
---
output : 518
passes : 1001
label : Final
wtime : 18.668069
utime : 18.846082
stime : 0.01101
inblock : 0
majflt : 0
maxrss : 18404
minflt : 5522
nivcsw : 61
nvcsw : 83
oublock : 128
What you see is all you get.
RSS, faults.
Per-process totals.
Not per structure.
Randomized trials simple in Raku.
Monitor results after specific operations.

More Related Content

PDF
BSDM with BASH: Command Interpolation
PDF
BASH Variables Part 1: Basic Interpolation
PDF
Hypers and Gathers and Takes! Oh my!
PDF
Keeping objects healthy with Object::Exercise.
PDF
The $path to knowledge: What little it take to unit-test Perl.
PDF
Metadata-driven Testing
PDF
Findbin libs
PDF
Object Trampoline: Why having not the object you want is what you need.
BSDM with BASH: Command Interpolation
BASH Variables Part 1: Basic Interpolation
Hypers and Gathers and Takes! Oh my!
Keeping objects healthy with Object::Exercise.
The $path to knowledge: What little it take to unit-test Perl.
Metadata-driven Testing
Findbin libs
Object Trampoline: Why having not the object you want is what you need.

What's hot (20)

PDF
Short Introduction To "perl -d"
PDF
Unit Testing Lots of Perl
PDF
Smoking docker
PDF
Get your teeth into Plack
PDF
Getting Testy With Perl6
PDF
Getting testy with Perl
PDF
Effective Benchmarks
PDF
Ethiopian multiplication in Perl6
PDF
Doing It Wrong with Puppet -
PDF
Replacing "exec" with a type and provider: Return manifests to a declarative ...
PPTX
Troubleshooting Puppet
PPT
Working with databases in Perl
PDF
Utility Modules That You Should Know About
PDF
Puppet Camp Chicago 2014: Smoothing Troubles With Custom Types and Providers ...
PDF
Puppet: What _not_ to do
PDF
Perl web frameworks
PPTX
Webrtc mojo
PPTX
Puppet camp chicago-automated_testing2
PDF
PL/Perl - New Features in PostgreSQL 9.0
PDF
Smolder @Silex
Short Introduction To "perl -d"
Unit Testing Lots of Perl
Smoking docker
Get your teeth into Plack
Getting Testy With Perl6
Getting testy with Perl
Effective Benchmarks
Ethiopian multiplication in Perl6
Doing It Wrong with Puppet -
Replacing "exec" with a type and provider: Return manifests to a declarative ...
Troubleshooting Puppet
Working with databases in Perl
Utility Modules That You Should Know About
Puppet Camp Chicago 2014: Smoothing Troubles With Custom Types and Providers ...
Puppet: What _not_ to do
Perl web frameworks
Webrtc mojo
Puppet camp chicago-automated_testing2
PL/Perl - New Features in PostgreSQL 9.0
Smolder @Silex
Ad

Similar to Memory Manglement in Raku (20)

PPTX
Bioinformatica p4-io
PPTX
Creating "Secure" PHP Applications, Part 1, Explicit Code & QA
PPT
Php my sql - functions - arrays - tutorial - programmerblog.net
PDF
Tt subtemplates-caching
PDF
pg_proctab: Accessing System Stats in PostgreSQL
PDF
pg_proctab: Accessing System Stats in PostgreSQL
PPTX
Lecture 3 Perl & FreeBSD administration
PDF
pg_proctab: Accessing System Stats in PostgreSQL
PDF
pg_proctab: Accessing System Stats in PostgreSQL
PPT
Ruby on Rails Intro
DOCX
Assignment no39
PPT
11 Things About 11gr2
PDF
Shell Script Disk Usage Report and E-Mail Current Threshold Status
PDF
Clean & Typechecked JS
PPT
Php Reusing Code And Writing Functions
PDF
Apache Hacks
PDF
Introduction to reactive programming & ReactiveCocoa
PDF
Benchmarking Perl (Chicago UniForum 2006)
PPT
Unit 4
PDF
ES6, WTF?
Bioinformatica p4-io
Creating "Secure" PHP Applications, Part 1, Explicit Code & QA
Php my sql - functions - arrays - tutorial - programmerblog.net
Tt subtemplates-caching
pg_proctab: Accessing System Stats in PostgreSQL
pg_proctab: Accessing System Stats in PostgreSQL
Lecture 3 Perl & FreeBSD administration
pg_proctab: Accessing System Stats in PostgreSQL
pg_proctab: Accessing System Stats in PostgreSQL
Ruby on Rails Intro
Assignment no39
11 Things About 11gr2
Shell Script Disk Usage Report and E-Mail Current Threshold Status
Clean & Typechecked JS
Php Reusing Code And Writing Functions
Apache Hacks
Introduction to reactive programming & ReactiveCocoa
Benchmarking Perl (Chicago UniForum 2006)
Unit 4
ES6, WTF?
Ad

More from Workhorse Computing (18)

PDF
Object::Trampoline: Follow the bouncing object.
PDF
Wheels we didn't re-invent: Perl's Utility Modules
PDF
mro-every.pdf
PDF
Paranormal statistics: Counting What Doesn't Add Up
PDF
Generating & Querying Calendar Tables in Posgresql
PDF
The W-curve and its application.
PDF
Perl6 Regexen: Reduce the line noise in your code.
PDF
Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6
PDF
Neatly folding-a-tree
PDF
Light my-fuse
PDF
Paranormal stats
PDF
Shared Object images in Docker: What you need is what you want.
PDF
Putting some "logic" in LVM.
PDF
Selenium sandwich-3: Being where you aren't.
PDF
Selenium sandwich-2
PDF
Selenium Sandwich Part 1: Data driven Selenium
PDF
Docker perl build
PDF
Designing net-aws-glacier
Object::Trampoline: Follow the bouncing object.
Wheels we didn't re-invent: Perl's Utility Modules
mro-every.pdf
Paranormal statistics: Counting What Doesn't Add Up
Generating & Querying Calendar Tables in Posgresql
The W-curve and its application.
Perl6 Regexen: Reduce the line noise in your code.
Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6
Neatly folding-a-tree
Light my-fuse
Paranormal stats
Shared Object images in Docker: What you need is what you want.
Putting some "logic" in LVM.
Selenium sandwich-3: Being where you aren't.
Selenium sandwich-2
Selenium Sandwich Part 1: Data driven Selenium
Docker perl build
Designing net-aws-glacier

Recently uploaded (20)

PPTX
MYSQL Presentation for SQL database connectivity
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PPTX
Big Data Technologies - Introduction.pptx
DOCX
The AUB Centre for AI in Media Proposal.docx
PDF
Approach and Philosophy of On baking technology
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
CIFDAQ's Market Insight: SEC Turns Pro Crypto
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
PDF
Advanced Soft Computing BINUS July 2025.pdf
PDF
Modernizing your data center with Dell and AMD
PDF
Review of recent advances in non-invasive hemoglobin estimation
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
GamePlan Trading System Review: Professional Trader's Honest Take
PPT
Teaching material agriculture food technology
PPTX
Cloud computing and distributed systems.
PDF
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
MYSQL Presentation for SQL database connectivity
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Big Data Technologies - Introduction.pptx
The AUB Centre for AI in Media Proposal.docx
Approach and Philosophy of On baking technology
Dropbox Q2 2025 Financial Results & Investor Presentation
CIFDAQ's Market Insight: SEC Turns Pro Crypto
20250228 LYD VKU AI Blended-Learning.pptx
Advanced Soft Computing BINUS July 2025.pdf
Modernizing your data center with Dell and AMD
Review of recent advances in non-invasive hemoglobin estimation
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
Mobile App Security Testing_ A Comprehensive Guide.pdf
Chapter 3 Spatial Domain Image Processing.pdf
GamePlan Trading System Review: Professional Trader's Honest Take
Teaching material agriculture food technology
Cloud computing and distributed systems.
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton

Memory Manglement in Raku

  • 1. Raku Memory Manglement: Checking yourself out. Steven Lembark Workhorse Computing lembark@wrkhors.com
  • 2. Yes, size() matters. but it isn’t available in Raku. Process-level stats. Mainly “RSS”. getrusage(2). Acquiring & analyze data. Raku tools.
  • 3. RSS? “Resident Set Size” Virtual pages in physical memory. Accessible without a page fault. Non-resident VM may be swapped. Requires a page fault to access.
  • 4. Goals of memory managment: Work within RSS. Reduce page faults. Avoid hard faults & swapping.
  • 5. getrusage(2) Returns process memory stats. Aggregate values. Results constrained by system limits.
  • 6. getrusage(2) struct rusage { struct timeval ru_utime; /* user CPU time used */ struct timeval ru_stime; /* system CPU time used */ long ru_maxrss; /* maximum resident set size */ long ru_ixrss; /* integral shared memory size */ long ru_idrss; /* integral unshared data size */ long ru_isrss; /* integral unshared stack size */ long ru_minflt; /* page reclaims (soft page faults) */ long ru_majflt; /* page faults (hard page faults) */ long ru_nswap; /* swaps */ long ru_inblock; /* block input operations */ long ru_oublock; /* block output operations */ long ru_msgsnd; /* IPC messages sent */ long ru_msgrcv; /* IPC messages received */ long ru_nsignals; /* signals received */ long ru_nvcsw; /* voluntary context switches */ long ru_nivcsw; /* involuntary context switches */ }; POSIX
  • 7. getrusage(2) struct rusage { struct timeval ru_utime; /* user CPU time used */ struct timeval ru_stime; /* system CPU time used */ long ru_maxrss; /* maximum resident set size */ long ru_ixrss; /* integral shared memory size */ long ru_idrss; /* integral unshared data size */ long ru_isrss; /* integral unshared stack size */ long ru_minflt; /* page reclaims (soft page faults) */ long ru_majflt; /* page faults (hard page faults) */ long ru_nswap; /* swaps */ long ru_inblock; /* block input operations */ long ru_oublock; /* block output operations */ long ru_msgsnd; /* IPC messages sent */ long ru_msgrcv; /* IPC messages received */ long ru_nsignals; /* signals received */ long ru_nvcsw; /* voluntary context switches */ long ru_nivcsw; /* involuntary context switches */ }; Linux
  • 8. getrusage(2) struct rusage { struct timeval ru_utime; /* user CPU time used */ struct timeval ru_stime; /* system CPU time used */ long ru_maxrss; /* maximum resident set size */ long ru_minflt; /* page reclaims (soft page faults) */ long ru_majflt; /* page faults (hard page faults) */ long ru_inblock; /* block input operations */ long ru_oublock; /* block output operations */ long ru_nvcsw; /* voluntary context switches */ long ru_nivcsw; /* involuntary context switches */ }; Only max RSS.
  • 9. getrusage(2) struct rusage { struct timeval ru_utime; /* user CPU time used */ struct timeval ru_stime; /* system CPU time used */ long ru_maxrss; /* maximum resident set size */ long ru_minflt; /* page reclaims (soft page faults) */ long ru_majflt; /* page faults (hard page faults) */ long ru_inblock; /* block input operations */ long ru_oublock; /* block output operations */ long ru_nvcsw; /* voluntary context switches */ long ru_nivcsw; /* involuntary context switches */ }; Fault counts.
  • 10. Viewing RSS Telemetry module. Takes periodic snapshots. Allows inserting a label to track events. Core with nqp. Not synchronous with tasks. Raku
  • 11. Viewing RSS ProcStats Soon to be on CPAN. Exports “dump-rusage”. Differences from first sample. Only output changes. Track wallclock time. Optional label.
  • 12. :final Output all stats compared to first sample ProcStats sub dump-rusage ( Bool() :$final = False, Bool() :$first = $final, Bool() :$force = $final, Stringy() :$label = $final ?? 'Final' !! '' ) is export( :DEFAULT )
  • 13. :first Values compared to first sample (vs. last). ProcStats sub dump-rusage ( Bool() :$final = False, Bool() :$first = $final, Bool() :$force = $final, Stringy() :$label = $final ?? 'Final' !! '' ) is export( :DEFAULT )
  • 14. :force Write all stats (vs. only changed). ProcStats sub dump-rusage ( Bool() :$final = False, Bool() :$first = $final, Bool() :$force = $final, Stringy() :$label = $final ?? 'Final' !! '' ) is export( :DEFAULT )
  • 15. :label Add “label” key (default from :final). ProcStats sub dump-rusage ( Bool() :$final = False, Bool() :$first = $final, Bool() :$force = $final, Stringy() :$label = $final ?? 'Final' !! '' ) is export( :DEFAULT )
  • 16. Wallclock time Elapsed vs. CPU sub dump-rusage ( Bool() :$final = False, Bool() :$first = $final, Bool() :$force = $final, Stringy() :$label = $final ?? 'Final' !! '' ) is export( :DEFAULT ) { my $wtime = now.Num;
  • 17. Wallclock time Sample at top to avoid time-shift. sub dump-rusage ( Bool() :$final = False, Bool() :$first = $final, Bool() :$force = $final, Stringy() :$label = $final ?? 'Final' !! '' ) is export( :DEFAULT ) { my $wtime = now.Num;
  • 18. Values from RSS constant FIELDS = < maxrss ixrss idrss isrss minflt majflt nswap inblock oublock msgsnd msgrcv nsignals nvcsw nivcsw >; constant IGNORE = <ixrss idrss isrss ...>; constant REPORT = < maxrss majflt minflt inblock oublock >; constant MICRO = 10 ** -6; COMPARE avoids reporting on CPU swithes.
  • 19. Track progress Unchanged samples are not reported. $passes tracks total calls. state $passes = 0; state %last = ();
  • 20. Acquire data Times are sec + µsec, deal with them separately. “Z=>” zips fields & values into a hash. use nqp; nqp::getrusage( my int @raw ); my ( $user_s, $user_us, $syst_s, $syst_us ) = splice @raw, 0, 4; my %sample = FIELDS Z=> @raw; %sample{ IGNORE } :delete;
  • 21. Making time my $utime = ( $user_s + $user_us / 1_000_000 ).round( MICRO ); my $stime = ( $syst_s + $syst_us / 1_000_000 ).round( MICRO ); user & system time begin as two ints. Round gives reasonable precision in output.
  • 22. Store baseline values. state %last = state %first = ( |%sample, :$wtime, :$utime, :$stime, ); First is never updated. Get a working “last” value on the first pass.
  • 23. Store baseline values. Flatten %sample into pairs. state %last = state %first = ( |%sample, :$wtime, :$utime, :$stime, );
  • 24. Store baseline values. Times as pairs. state %last = state %first = ( |%sample, :$wtime, :$utime, :$stime, );
  • 25. First is last at first. After first last is last. my %prior = $first ?? %first !! %last ;
  • 26. What to compare? Force reports full sample. COMPARE limits keys compare to %prior & output. my %curr = ( $force || ! $passes ) ?? %sample !! do { my @diffs = REPORT.grep( { %sample{$_} != %prior{$_} } ); @diffs Z=> %sample{ @diffs } };
  • 27. Write out one stat heading & value. Compute column width once during execution. sub write-stat ( Pair $p ) { note sprintf '%-*s : %s', once {FIELDS».chars.max}, $p.key, $p.value ; }
  • 28. Write progressive value Numerics compared to starting baseline. Simplifies tracking code results. sub write-diff ( Pair $p ) { my $k = $p.key; my $v = $p.value - %first{ $k }; write-stat $k => $v; }
  • 29. First pass writes all stats. First pass has to report baseline values. state $write = &write-stat;
  • 30. First pass writes all stats. First pass has to report baseline values. After that report differences. state &write = &write-stat; ... write $stat; ... once { &write = &write-diff };
  • 31. for %curr.sort -> $stat { FIRST { note '---'; write-stat ( output => $++ ); write-stat ( :$passes ); write-stat ( :$label ) if $label; write-diff ( :$wtime ); write-diff ( :$utime ); write-diff ( :$stime ); } write $stat }
  • 32. for %curr.sort -> $stat { FIRST { note '---'; write-stat ( output => $++ ); write-stat ( :$passes ); write-stat ( :$label ) if $label; write-diff ( :$wtime ); write-diff ( :$utime ); write-diff ( :$stime ); } write $stat }
  • 33. for %curr.sort -> $stat { FIRST { note '---'; write-stat ( output => $++ ); write-stat ( :$passes ); write-stat ( :$label ) if $label; write-diff ( :$wtime ); write-diff ( :$utime ); write-diff ( :$stime ); } write $stat }
  • 34. for %curr.sort -> $stat { FIRST { note '---'; write-stat ( output => $++ ); write-stat ( :$passes ); write-stat ( :$label ) if $label; write-diff ( :$wtime ); write-diff ( :$utime ); write-diff ( :$stime ); } write $stat }
  • 35. for %curr.sort -> $stat { FIRST { note '---'; write-stat ( output => $++ ); write-stat ( :$passes ); write-stat ( :$label ) if $label; write-diff ( :$wtime ); write-diff ( :$utime ); write-diff ( :$stime ); } write $stat }
  • 36. Last steps Up total count. Store current sample for re-use. ++$passes; %last = %sample; once { &write = &write-diff };
  • 37. Baseline usage Bare for-loop Shows overhead of rusage output. #!/usr/bin/env Raku use v6.d; use FindBin::libs; use ProcStats; dump-rusage for 1 .. 1_000; dump-rusage( :final );
  • 38. Sample 0 Pass 0 as all values. Baseline for RSS & friends. --- output : 0 passes : 0 wtime : 1560968261.746507 utime : 0.344793 stime : 0.020896 inblock : 0 majflt : 0 maxrss : 99732 minflt : 25039 nivcsw : 10 nvcsw : 204 oublock : 64
  • 39. Sample 0 wtime is ‘real world’. Reasonable candidate key for sample history. --- output : 0 passes : 0 wtime : 1560968261.746507 utime : 0.344793 stime : 0.020896 inblock : 0 majflt : 0 maxrss : 99732 minflt : 25039 nivcsw : 10 nvcsw : 204 oublock : 64
  • 40. Sample 0 RSS is ~100MiB at startup. --- output : 0 passes : 0 wtime : 1560968261.746507 utime : 0.344793 stime : 0.020896 inblock : 0 majflt : 0 maxrss : 99732 minflt : 25039 nivcsw : 10 nvcsw : 204 oublock : 64
  • 41. Output Output 1+ are relative to %first. Sample N --- output : 1 passes : 1 wtime : 0.0081639 utime : 0.007295 stime : 0.000228 maxrss : 1588 minflt : 255 --- ...
  • 42. Output Output 1+ are relative to %first. maxrss & minflt cause output. Output --- output : 1 passes : 1 wtime : 0.0081639 utime : 0.007295 stime : 0.000228 maxrss : 1588 minflt : 255 --- ...
  • 43. Output Inermediate passes. Output #130: minflt 1758 -> 1759. Output --- output : 129 passes : 812 wtime : 0.4603018 utime : 0.60607 stime : 0.000175 minflt : 1758 --- output : 130 passes : 813 wtime : 0.4636268 utime : 0.609417 stime : 0.000175 minflt : 1759 ---
  • 44. Output getrulsage( :final ); Shows all fields. About 1/8 of passes had output. “Final” sample --- output : 131 passes : 1000 label : Final wtime : 0.5086002 utime : 0.654374 stime : 0.000175 inblock : 0 majflt : 0 maxrss : 6996 minflt : 1759 nivcsw : 2 nvcsw : 35 oublock : 0
  • 45. Default label. --- output : 131 passes : 1000 label : Final wtime : 0.5086002 utime : 0.654374 stime : 0.000175 inblock : 0 majflt : 0 maxrss : 6996 minflt : 1759 nivcsw : 2 nvcsw : 35 oublock : 0 “Final” sample
  • 46. Fairly low overhead. --- output : 131 passes : 1000 label : Final wtime : 0.5086002 utime : 0.654374 stime : 0.000175 inblock : 0 majflt : 0 maxrss : 6996 minflt : 1759 nivcsw : 2 nvcsw : 35 oublock : 0 “Final” sample
  • 47. Multiple threads: wallclock < user. --- output : 131 passes : 1000 label : Final wtime : 0.5086002 utime : 0.654374 stime : 0.000175 inblock : 0 majflt : 0 maxrss : 6996 minflt : 1759 nivcsw : 2 nvcsw : 35 oublock : 0 “Final” sample
  • 48. RSS grew by ~7MiB --- output : 131 passes : 1000 label : Final wtime : 0.5086002 utime : 0.654374 stime : 0.000175 inblock : 0 majflt : 0 maxrss : 6996 minflt : 1759 nivcsw : 2 nvcsw : 35 oublock : 0 “Final” sample
  • 49. Really do something... Simulate traking userid’s on a web server: Add a hash key. Increment a random value. Drop a key.
  • 50. Roll your own Random hash key via random sample. sub random-string ( Int() :$size = ( 1 .. 10 ).pick --> Str ) { constant alpha = [ 'a' ... 'z', 'A' ... 'Z' ]; alpha.roll( $size ).join; }
  • 51. Roll your own pick() returns a single, random value. sub random-string ( Int() :$size = ( 1 .. 10 ).pick --> Str ) { constant alpha = [ 'a' ... 'z', 'A' ... 'Z' ]; alpha.roll( $size ).join; }
  • 52. Roll your own roll() returns a random sample. sub random-string ( Int() :$size = ( 1 .. 10 ).pick --> Str ) { constant alpha = [ 'a' ... 'z', 'A' ... 'Z' ]; alpha.roll( $size ).join; }
  • 53. Fake userid Track key counts, active keys. sub user-add { ++%user-data{ random-string }; ++$adds; $max-keys = max $max-keys, %user-data.elems; }
  • 54. Random key selection sub user-drop { %user-data or return; ++$drops; %user-data{ %user-data.pick.key } :delete; } sub user-op { %user-data or return; ++$ops; ++%user-data{ %user-data.pick.key }; }
  • 55. Randomized, weighted trial. for 1 .. 1000 { constant weighted_operations = ( &user-add => 0.10, &user-drop => 0.10, &user-op => 0.80, ).Mix; weighted_operations.roll( 1_000 )».(); dump-rusage(label => 'Keys: '~%user‑data.elems ); }
  • 56. Define op’s and weights. for 1 .. 1000 { constant weighted_operations = ( &user-add => 0.10, &user-drop => 0.10, &user-op => 0.80, ).Mix; weighted_operations.roll( 1_000 )».(); dump-rusage(label => 'Keys: '~%user‑data.elems ); }
  • 57. 1000 iterations of trial. for 1 .. 1000 { constant weighted_operations = ( &user-add => 0.10, &user-drop => 0.10, &user-op => 0.80, ).Mix; weighted_operations.roll( 1_000 )».(); dump-rusage(label => 'Keys: '~%user‑data.elems ); }
  • 58. Report summary “say” is stdout, dump-rusage is stderr. :final uses %first as reference for values. dump-rusage( :final ); say 'Total adds: ' ~ $adds; say 'Total drops: ' ~ $drops; say 'Total ops: ' ~ $ops; say 'Max keys: ' ~ $max-keys; say 'Final keys: ' ~ %user-data.elems;
  • 59. Getting stats Easier to read with separate files. $ ./rand-user-table >stats.out 2>stats.yaml;
  • 60. Stats results Final results from “say”. Total adds: 99738 Total drops: 98755 Total ops: 787133 Max keys: 213 Final keys: 144
  • 61. Stats results Final sample: 1000 iterations/pass. Extra time from threading. ~18MiB RSS growth. --- output : 518 passes : 1001 label : Final wtime : 18.668069 utime : 18.846082 stime : 0.01101 inblock : 0 majflt : 0 maxrss : 18404 minflt : 5522 nivcsw : 61 nvcsw : 83 oublock : 128
  • 62. What you see is all you get. RSS, faults. Per-process totals. Not per structure. Randomized trials simple in Raku. Monitor results after specific operations.