trimming OPs

--------------050401020503090904070402
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit


Im toying (once again) with the notion of combining op_next and op_sibling
into a union, which could save non-trivial memory for optrees.
A naive estimate is 20%

Its not clear what the value of this savings is - theres generally
a lot less code than data, so the total memory saved may not be worth 
fussing for.
OTOH, maybe mod-perl servers could materially benefit.
If you have a use-case where code-memory savings could matter, please 
say so.
Im just scratching an itch, but I'll be damned if Im gonna create a rash.


* optrees are created using op-siblings to tie the OPs together,
so op-siblings are integral to this.

* Perl_linklist traverses this 'syntax' tree, and sets op-next pointers
to give the execution order for that optree.  op-sibling pointers are
basically unneeded after that.

* I just lied - theyre needed for uninit-var chasing, and perhaps other 
things.
But - theyre rarely needed, and could be fetched from elsewhere.  
Further, the
cost of that fetch is nearly irrelevant, since its an error-reporting / 
diagnostic feature.



Anyway, the 1st patch in this series is pretty straightforward, the 
interesting bits are here:
(patch is compressed, cuz its ~ 90k otherwize)

diff -ruNp -X exclude-diffs ../bleadperl/op.h ns-abstract/op.h
--- ../bleadperl/op.h    2006-08-31 03:10:53.000000000 -0600
+++ ns-abstract/op.h    2006-09-02 10:40:17.000000000 -0600
@@ -46,19 +46,63 @@
 #define BASEOP BASEOP_DEFINITION
 #else
 #define BASEOP                \
-    OP*        op_next;        \
-    OP*        op_sibling;        \
+    OP_NEXT_SIBL_REP            \
     OP*        (CPERLscope(*op_ppaddr))(pTHX);        \
     MADPROP_IN_BASEOP            \
     PADOFFSET    op_targ;        \
     unsigned    op_type:9;        \
     unsigned    op_opt:1;        \
     unsigned    op_static:1;        \
-    unsigned    op_spare:5;        \
+    unsigned    op_nextset:1;        \
+    unsigned    op_spare:4;        \
     U8        op_flags;        \
     U8        op_private;
 #endif
 
+#ifndef OP_NEXT_SIBL_IMP
+#define OP_NEXT_SIBL_IMP 0    /* override with -Accflags */
+#endif
+
+#if (OP_NEXT_SIBL_IMP == 0)   /* original formulation */
+
+#  define OP_NEXT_SIBL_REP    \
+      OP*     op_next;        \
+      OP*     op_sibling;
+
+#  define PERL_OP_NEXT(o)             (o)->op_next
+#  define PERL_OP_SIBLING(o)          (o)->op_sibling
+#  define PERL_OP_SETNEXT(o,what)     ((o)->op_next) = (what)
+#  define PERL_OP_SETSIBLING(o,what)  ((o)->op_sibling) = (what)
+#  define PERL_OP_SIBLING_ADDR(o)     &((o)->op_sibling)
+
+#else /* nested struct/union op rep */
+
+#if (OP_NEXT_SIBL_IMP == 1)   /* nested struct op rep */
+
+#  define OP_NEXT_SIBL_REP    \
+   struct {                   \
+      OP*     next;           \
+      OP*     sibling;        \
+   } op;
+
+#elif (OP_NEXT_SIBL_IMP == 2) /* nested union op rep */
+
+#  define OP_NEXT_SIBL_REP    \
+   union {            \
+       OP*     next;        \
+       OP*     sibling;        \
+    } op;
+#endif
+
+#  define PERL_OP_NEXT(o)             (o)->op.next
+#  define PERL_OP_SIBLING(o)          (o)->op.sibling
+#  define PERL_OP_SETNEXT(o,what)     ((o)->op.next) = (what)
+#  define PERL_OP_SETSIBLING(o,what)  ((o)->op.sibling) = (what)
+#  define PERL_OP_SIBLING_ADDR(o)     &((o)->op.sibling)
+
+#endif
+
+
 #define OP_GIMME(op,dfl) \


Depending on the value of *OP_NEXT_SIBL_IMP*, we get
0 - original code
1 - a sub-struct op {OP* next,  sibling}
2 - a sub-union
along with  4 macros which hide those variations.
( building both 0,1 uses the compiler to prove that abstraction is 
complete )
    Configure -des ... -Accflags="-DOP_NEXT_SIBL_IMP=1"

With patch applied, bleadperl builds fine and tests pretty good for both 
0 and 1;
2 failures;
ext/B/t/bytecode.....................................FAILED at test 1
ext/B/t/concise......................................ok
ext/B/t/concise-xs...................................# Looks like you 
planned 1588 tests but ran 1 extra.
FAILED--expected 1588 tests, saw 1589

    concise-xs.t   (trivial, due to extra B constant inadvertently 
created (probly some patch cruft))
    bytecode      I dont get this one.. tested verbosely, it gets SEGV.
          I think its fixable, but Im looking ahead ATM.


WRT the RARELY NEEDED bits ..  (heres where it gets tricky/speculative)

it should be possible to:

-  collect op-siblings in  Perl-linklist, stuff them into temporary storage
    Im trying to use PTR_TBL code, exported from sv.c,
    this seems non-interfering, since its compile-time, whereas the 
threads use of that table
    is run-time. (speculating;-)
    IIRC, I tried to stuff the data directly into PL_compiling, but 
couldnt convince myself
    that it was workable ( recollection of confusion is all that remains )

- catch COPs in Perl-linklist, at which point (I hope) the op-next chain 
is complete
    for that statement, and could be walked.  So, walk the chain, and 
lookup the sibling
    for each that was previously collected (we'd also delete those entries).

- attach the collected siblings to the COP (with a newly added field)
    Im envisioning an array, with siblings stored at indexes 
corresponding to the walk-steps

Im stuck somewhere in the middle of the above, and could use some advice ..


Continuing on speculative path ( we have to get further for the above to 
be valuable)

- with sibling info stored off the COP, it should be possible to fix 
find_uninit_var()
to get the sibling data it needs, assuming that closest_cop will work 
there (no obvious reason it shouldnt)
There look to be 5 calls to PERL_OP_SIBLING that need to be replaced.

[jimc@harpo ns-cop-sibl]$ egrep -n 'OP_SIBLING|find_uninit_var' sv.c
11855:=for apidoc find_uninit_var
11875:S_find_uninit_var(pTHX_ OP* obase, SV* uninit_sv, bool match)
11913:          return find_uninit_var(cUNOPx(obase)->op_first,
11985:  return find_uninit_var(cBINOPo->op_last, uninit_sv, match);
11991:      return find_uninit_var(cBINOPx(obase)->op_last, uninit_sv, 
match);
12062:  return find_uninit_var(cBINOPx(obase)->op_first, uninit_sv, match);
12067:      o = PERL_OP_SIBLING(o);
12069:  if (!PERL_OP_SIBLING(o)) {
12108:      o = PERL_OP_SIBLING(PERL_OP_SIBLING(o));
12137:  for (kid=o; kid; kid = PERL_OP_SIBLING(kid)) {
12153:      return find_uninit_var(o2, uninit_sv, match);
12157:      sv = find_uninit_var(o, uninit_sv, 1);
12160:      o = PERL_OP_SIBLING(o);
12183:      varname = find_uninit_var(PL_op, uninit_sv,0);



I for one would find a lot of guidance/value if someone knowledgeable would
add a handful of assertions which would document the subtle invariants 
buried in the optrees.
    op_assert( o->op_sibling == o)
    op_assert( o->op_next == o)
        This occurs a lot.  its also counterintuitive.
 
Id expect some of the properties thus exposed to be representable by flags
(or some small set of states), with the useful ones reserved out of OP's 
spare-bits


Once siblings are out of OP (and reachable via the relevant COP),
further optimizations might be possible, but I'll refrain from too much 
more speculation.




The 2nd patch hacks at Perl_linklist, adding printfs, and yielding this:

[jimc@harpo ns-cop-sibling]$ ./perl -Ilib -e 'print"foo",$_+2; 
print"bar",$_-3'
  90f3210 add s:0 n:0
   90f31d0 rv2sv s:90f31f0 n:0
    90f31b0 gv s:0 n:90f31b0
    90f31d0 rv2sv s:90f31f0 n:90f31b0
   90f31f0 const s:0 n:90f31f0
   90f3210 add s:0 n:90f31b0
  90f5c00 subtract s:0 n:0
   90f5bc0 rv2sv s:90f5be0 n:0
    90f5ba0 gv s:0 n:90f5ba0
    90f5bc0 rv2sv s:90f5be0 n:90f5ba0
   90f5be0 const s:0 n:90f5be0
   90f5c00 subtract s:0 n:90f5ba0
  90f5ae8 leave s:0 n:0
   90f5c90 enter s:90f5ab0 n:90f5c90
   COP 90f5ab0 -> 90f3160 print
   90f5ab0 nextstate s:90f3160 n:90f5ab0
    COP 90f5ab0 -> 90f3160 print
   90f3160 print s:90ec070 n:0
    90f5a98 pushmark s:90f3190 n:90f5a98
    90f3190 const s:90f3210 n:90f3190
    90f3210 add s:0 n:90f31b0
    90f3160 print s:90ec070 n:90f5a98
   COP 90ec070 -> 90ed078 print
   90ec070 nextstate s:90ed078 n:90ec070
    COP 90ec070 -> 90ed078 print
   90ed078 print s:0 n:0        
    90f5b08 pushmark s:90f5b80 n:90f5b08
    90f5b80 const s:90f5c00 n:90f5b80
    90f5c00 subtract s:0 n:90f5ba0
    90ed078 print s:0 n:90f5b08
   90f5ae8 leave s:0 n:90f5c90


if you suppress these prints, the code passes all but the 2 tests id'd 
above.

op_assert( o->op_next == o)
This occurs a lot above, I dont get it.  Is there another, final step ?
Also - its obvious that this could be replaced by a flag

NORMAL is o->op_next..




--------------050401020503090904070402
Content-Type: application/gzip;
 name="diff.ns.abstract-test-ok-99.gz"
Content-Transfer-Encoding: base64
Content-Disposition: inline;
 filename="diff.ns.abstract-test-ok-99.gz"

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
--------------050401020503090904070402
Content-Type: text/plain;
 name="diff.ns-cop-sibling-proto"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="diff.ns-cop-sibling-proto"

diff -ruNp -X exclude-diffs ns-abstract/op.c ns-cop-sibling/op.c
--- ns-abstract/op.c	2006-09-04 13:00:26.000000000 -0600
+++ ns-cop-sibling/op.c	2006-09-05 12:29:11.000000000 -0600
@@ -527,17 +527,191 @@ Perl_op_refcnt_unlock(pTHX)
     OP_REFCNT_UNLOCK;
 }
 
+/* 
+=head1 Implementation: out-of-OP op-siblings
+
+In the bottom up up complier pass, perly.y emits op-twigs, setting
+op-type/class specific pointers in each OP to link them together
+according to the expression just parsed.  Sequences of
+
+optrees are generated using op_sibling and 
+represent both sequential and expression dependencies.  The execution
+order pass relinks the tree of OPs for fast execution, using the
+op_next pointer.
+
+Our premise is that sibling pointers are used rarely after op_next is
+set, so, at listlink()-age time, we copy op_sibling to an associative
+array, and intercept later reads.  This allows us to instrument that
+read, find out who is using the value, and optimize later, starting
+with removing in-OP storage for the pointer and getting it from the
+hash.  This shrinks the optree by ~20%, and may have other benefits.
+
+=head2 Implementation: patch development
+
+ 1.abstract all refs to op_next, op_sibling with macros
+ .  PERL_OP_NEXT,    PERL_OP_SIBLING
+ .  PERL_OP_SETNEXT, PERL_OP_SETSIBLING
+ . set OP_NEXT_SIBL_IMP to 0,1,2: to choose pair, struct, union reps
+
+ 2.extend COP, adding struct OpPair {next; sibl} ops[30]
+ .  populate it in linklist
+
+This wont work as is; in linklist(), assert(PL_curcop == &PL_compiling),
+so theres no COP context
+
+
+ 3.compile vs runtime (TBC)
+ .  PERL_OP_SIBLING should call instrumentation
+ .  use IN_PERL_RUNTIME to toggle PERL_OP_SIBLING defn
+
+=head2 Future Optimizations
+
+At this point (patch 2), the patch increases total memory use, but
+we're looking forward to several optimizations:
+
+1 not all ops have siblings, only those that do get added
+
+2 we hope that op-shrink, and segregating hot fields from cold, will
+  improve cache performance, though cache arguably works much better
+  on C code, and pp_code probably dwarfs the 20% opcode savings.
+
+3 analysis of the uninit-var feature may reveal that only some ops
+  need to be hashed to support the reporting for the vars in a given
+  code block.
+
+4 we can add a feature to allow the coder to suppress the hashing of
+  siblings entirely, for use on well-tested code.
+
+5 OpPair storage can be unioned with op_siblings vector, which its
+  converted to if/when linklist(op) acts upon COPs.  At this point,
+  the op can be shrunk to fit the op_sibling vector actually needed.
+
+=cut
+*/
+
+#if 0
+
+/* borrowed verbatim from util.c */
+
+STATIC COP*
+S_closest_cop(pTHX_ COP *cop, OP *o)
+{
+    /* Look for PL_op starting from o.  cop is the last COP we've seen. */
+
+    if (!o || o == PL_op) return cop;
+
+    if (o->op_flags & OPf_KIDS) {
+	OP *kid;
+	for (kid = cUNOPo->op_first; kid; kid = PERL_OP_SIBLING(kid))
+	{
+	    COP *new_cop;
+
+	    /* If the OP_NEXTSTATE has been optimised away we can still use it
+	     * the get the file and line number. */
+
+	    if (kid->op_type == OP_NULL && kid->op_targ == OP_NEXTSTATE)
+		cop = (COP *)kid;
+
+	    /* Keep searching, and return when we've found something. */
+
+	    new_cop = S_closest_cop(aTHX_ cop, kid);
+	    if (new_cop) return new_cop;
+	}
+    }
+
+    /* Nothing found. */
+
+    return 0;
+}
+#endif
+
+/* during compilation (pre-cop linklist phase), we save sibling
+   pointers into a global 'hash', then when bottom up linklist-ing
+   reaches up to the COP, we can sweep those hash entries into
+   cop-private storage.
+
+   With that data in place, we should be able to find it in the COP,
+   which allows us to pull it out of the OPs, and save ~20% storage in
+   the OPs themselves.
+
+   Use of op-sibling ptr is rare once the code is linked for running,
+   so a higher fetch-cost is mostly irrelevant, since its needed only
+   to support uninit-var reporting, which is purely a debug-friendly
+   feature.
+
+   Eventually, we may be able to eliminate the entries that are
+   representable by a flag instead, forex op->next == op, or some
+   other knowable (and useful) properties.  For some 'production'
+   situations, where the code-base is well-known, it may be possible
+   to drop the hash entirely.
+*/
+
 /* Contextualizers */
 
-#define LINKLIST(o) (PERL_OP_NEXT(o) ? PERL_OP_NEXT(o) : linklist((OP*)o))
+#define LINKLIST(o) Perl_linklist(aTHX_ o)
+/* was
+   #define LINKLIST(o) 	(PERL_OP_NEXT(o) ? PERL_OP_NEXT(o) : linklist((OP*)o))
+
+   removing the shortcut means that Perl_linklist sees COPs, which
+   lets us collect and attach sibling info to the COP, where its
+   reasonably close at hand for (revamped) uninit-var use.
+*/
+
+static PTR_TBL_t *siblings;
+static char indent[] = "                                         ";
+static int ipi = -1;
 
 OP *
 Perl_linklist(pTHX_ OP *o)
 {
     OP *first;
+    COP *cop;
+    int type;
 
-    if (PERL_OP_NEXT(o))
+    assert(PL_curcop == &PL_compiling);
+
+    /*
+    cop = S_closest_cop(aTHX_ PL_curcop, PERL_OP_SIBLING(PL_curcop));
+    if ((COP*)o == cop)
+	PerlIO_printf(Perl_debug_log, "linklist closest cop\n");
+    */
+
+    type = o->op_type;
+    if (type == OP_NULL)
+	type = (OPCODE)o->op_targ;
+
+    if (!siblings) siblings = ptr_table_new();
+    if (ipi == -1) ipi = strlen(indent)-1;
+
+    if (type == OP_NEXTSTATE || type == OP_SETSTATE || type == OP_DBSTATE) {
+	PTR_TBL_ENT_t *ent;
+	OP *next = PERL_OP_SIBLING(o);	// next not set yet
+	/* cop's next points back at itself !! */
+
+	if (next && 0)
+	    PerlIO_printf(Perl_debug_log, "%s COP %p -> %p %s\n", 
+			  indent+ipi, o, next,
+			  PL_op_name[next->op_type]);
+#if 0
+	for (; next && next != PERL_OP_NEXT(next) && next != o;
+	     next=PERL_OP_NEXT(next))
+	{
+	    // follow chain,
+	    ent = ptr_table_fetch(siblings, next);
+	    if (ent && ent->newval == PERL_OP_SIBLING(next))
+		PerlIO_printf(Perl_debug_log, "woohoo\n");
+	}
+#endif	
+    }
+    if (10)
+	PerlIO_printf(Perl_debug_log, "%s %p %s s:%p n:%p\n", indent+ipi--,
+		      o, PL_op_name[o->op_type],
+		      PERL_OP_SIBLING(o), PERL_OP_NEXT(o));
+    
+    if (PERL_OP_NEXT(o)) {
+	ipi++;
 	return PERL_OP_NEXT(o);
+    }
 
     /* establish postfix order */
     first = cUNOPo->op_first;
@@ -546,7 +720,20 @@ Perl_linklist(pTHX_ OP *o)
 	PERL_OP_SETNEXT(o, LINKLIST(first));
 	kid = first;
 	for (;;) {
+
 	    if (PERL_OP_SIBLING(kid)) {
+
+		ptr_table_store(siblings, (SV*)o, PERL_OP_SIBLING(o));
+
+		/* save op_sibling in COP before setting op_next,
+		   which would overwrite storage if shared/optimized */
+
+		/* current op entry for siblings
+		   int opincop = cop->opct++;
+		   cop->ops[opincop].sibl = PERL_OP_SIBLING(kid);
+		   cop->ops[opincop].self = kid;
+		*/
+
 		PERL_OP_SETNEXT(kid, LINKLIST(PERL_OP_SIBLING(kid)));
 		kid = PERL_OP_SIBLING(kid);
 	    } else {
@@ -555,8 +742,15 @@ Perl_linklist(pTHX_ OP *o)
 	    }
 	}
     }
-    else
+    else {
+	ptr_table_store(siblings, (SV*)o, PERL_OP_SIBLING(o));
 	PERL_OP_SETNEXT(o,o);
+    }
+
+    if (10)
+	PerlIO_printf(Perl_debug_log, "%s %p %s s:%p n:%p\n", indent+ipi++,
+		      o, PL_op_name[o->op_type],
+		      PERL_OP_SIBLING(o), PERL_OP_NEXT(o));
     
     return PERL_OP_NEXT(o);
 }
@@ -3346,7 +3540,7 @@ Perl_pmruntime(pTHX_ OP *o, OP *expr, bo
 	    PERL_OP_SETNEXT(repl, (OP*)rcop);
 
 	    pm->op_pmreplroot = scalar((OP*)rcop);
-	    pm->op_pmreplstart = (OP*) LINKLIST(rcop);
+	    pm->op_pmreplstart = LINKLIST((OP*)rcop);
 	    PERL_OP_SETNEXT((OP*)rcop, 0);
 	}
     }

--------------050401020503090904070402--
0
jim
9/5/2006 7:50:42 PM
perl.perl5.porters 46742 articles. 0 followers. Follow

12 Replies
4644 Views

Similar Articles

[PageSpeed] 34

On 9/5/06, Jim Cromie <jim.cromie@gmail.com> wrote:

> Im toying (once again) with the notion of combining op_next and op_sibling
> into a union, which could save non-trivial memory for optrees.
> A naive estimate is 20%

Gosh, no replies. Well, from my point of view this adds a lot of
complexity for no given reason. Is it actually any better?

Leon
0
acme
9/8/2006 2:31:18 PM
--------------070701040305010506010003
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

Leon Brocard wrote:
> On 9/5/06, Jim Cromie <jim.cromie@gmail.com> wrote:
>
>> Im toying (once again) with the notion of combining op_next and 
>> op_sibling
>> into a union, which could save non-trivial memory for optrees.
>> A naive estimate is 20%
>
> Gosh, no replies. Well, from my point of view this adds a lot of
> complexity for no given reason. Is it actually any better?
>
> Leon
>
Thanks for responding Leon,

Its certainly a fair question, and one Ive been pondering myself.

Your question prompted me to look for my own estimate of code-memory size,
specifically at process size, b4 and after loading Phalanx100.

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
b4
  531 jimc      15   0  9496 5976 1528 S  0.0  1.2   0:00.37 perl
after
  531 jimc      23   0  100m  90m 4128 S  0.0 17.9   0:06.91 perl


Thats 90 MB of growth, enough I think to warrant further discussion.
Ive attached my script, hopefully its repeatable.

thanks again,
jimc



--------------070701040305010506010003
Content-Type: application/x-perl;
 name="usephalanx.pl"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="usephalanx.pl"

#!/usr/local/bin/perl -w

require Test::Harness;
require Test::Reporter;
require Test::Tester;
require Test::Builder;
require Test::Builder::Tester;
require Sub::Uplevel;
require Test::Exception;
require Test::Tester;
require Test::NoWarnings;
require Test::Tester;
require Pod::Escapes;
require Pod::Simple;
require Test::Pod;
require YAML;
require Cwd;
require Archive::Tar;
require Module::Build;
require Devel::Symdump;
require Pod::Coverage;
require Test::Pod::Coverage;
require Compress::Zlib;
require IO::Zlib;
require Archive::Zip;
require Archive::Tar;
require Storable;
require Digest::MD5;
require URI;
require HTML::Tagset;
require HTML::Parser;
require LWP;
require IPC::Run;
require CPANPLUS;
require DBI;
#require DBD::mysql;
require GD;
require MIME::Base64;
require Net::SSLeay;
#require Net::LDAP;
require XML::Parser;
require Apache::ASP;
require CGI;
require Date::Manip;
#require DBD::Oracle;
require DBD::Pg;
require Digest::SHA1;
require Digest::HMAC;
require HTML::Tagset;
require HTML::Template;
require Net::Cmd;
require Mail::Mailer;
require MIME::Body;
require Net::DNS;
require Time::HiRes;
require Apache::DBI;
require Apache::Session;
require Apache::Test;
require AppConfig;
#require App::Info;
require Authen::PAM;
require Authen::SASL;
require BerkeleyDB;
require Bit::Vector;
require Carp::Clan;
#require Chart::Bars;
#require Class::DBI;
require Compress::Zlib::Perl;
require Config::IniFiles;
require Convert::ASN1;
#require Convert::TNEF;
require Convert::UUlib;
require CPAN;
require Crypt::CBC;
require Crypt::DES;
require Crypt::SSLeay;
require Data::Dumper;
require Date::Calc;
require DateTime;
#require DBD::DB2;
#require DBD::ODBC;
require DBD::SQLite;
#require DBD::Sybase;
#require Device::SerialPort;
require Digest::SHA;
require Encode;
require Event;
require Excel::Template;
require Expect;
require ExtUtils::MakeMaker;
require File::Scan;
require File::Spec;
require File::Tail;
require File::Temp;
require GD::Graph;
require GD::Text;
require Getopt::Long;
require HTML::Mason;
require Image::Size;
require IMAP::Admin;
require Parse::RecDescent;
require Inline;
require IO;
require Spiffy;
require IO::All;
require IO::Socket::SSL;
#require IO::String;
require IO::Stringy;
require XML::SAX2Perl;
#require Mail::Audit;
#require Mail::ClamAV;
#require Mail::Sendmail;
#require Math::Pari;
require Digest::MD5;
require MIME::Lite;
require MP3::Info;
#require Net::Daemon;
#require Net::FTP::Common;
require Net::Ping;
require Net::Server;
require Net::SNMP;
#require Net::SSH::Perl;
require Net::Telnet;
#require OLE::Storage_Lite;
require Params::Validate;
#require Image::Magick;
#require RPC::PlServer;
require Pod::Parser;
require POE;
#require SNMP;
require SOAP::Lite;
require Spreadsheet::ParseExcel;
require Spreadsheet::WriteExcel;
require Spreadsheet::WriteExcelXML;
require Storable;
require Template;
require Term::ReadKey;
#require Term::ReadLine::Perl;
require Text::Iconv;
require Date::Parse;
require Time::Timezone;
require Unicode::String;
require Unix::Syslog;
#require Verilog::Parser;
require WWW::Mechanize;
require XML::DOM;
require XML::Generator;
require XML::LibXML;
require XML::NamespaceSupport;
require XML::SAX;
require XML::Simple;
require XML::Writer;

$DB::single = 1;
print "hello world";
$DB::single = 1;

--------------070701040305010506010003--
0
jim
9/12/2006 1:58:30 AM
On Mon, Sep 11, 2006 at 07:58:30PM -0600, Jim Cromie wrote:
> Your question prompted me to look for my own estimate of code-memory size,
> specifically at process size, b4 and after loading Phalanx100.
> 
>  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
> b4
>  531 jimc      15   0  9496 5976 1528 S  0.0  1.2   0:00.37 perl
> after
>  531 jimc      23   0  100m  90m 4128 S  0.0 17.9   0:06.91 perl
> 
> 
> Thats 90 MB of growth, enough I think to warrant further discussion.
> Ive attached my script, hopefully its repeatable.

I don't really see what point you are trying to make here. Yes, if you
load tons of  modules you will use tons of memory. What you haven't shown,
is
(a) how much does that 100m shrink by if op_sibling and op_next are
combined?
(b) what proportion of total memory would that 100m be if the modules
were actually used in anger - normally you only load modules to use them
for something, at which point you're creating lots of SVs atc.

-- 
I've often wanted to drown my troubles, but I can't get my wife to go
swimming.
0
davem
9/12/2006 1:25:58 PM
Dave Mitchell wrote:
> On Mon, Sep 11, 2006 at 07:58:30PM -0600, Jim Cromie wrote:
>   
>> Your question prompted me to look for my own estimate of code-memory size,
>> specifically at process size, b4 and after loading Phalanx100.
>>
>>  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
>> b4
>>  531 jimc      15   0  9496 5976 1528 S  0.0  1.2   0:00.37 perl
>> after
>>  531 jimc      23   0  100m  90m 4128 S  0.0 17.9   0:06.91 perl
>>
>>
>> Thats 90 MB of growth, enough I think to warrant further discussion.
>> Ive attached my script, hopefully its repeatable.
>>     
>
> I don't really see what point you are trying to make here. Yes, if you
> load tons of  modules you will use tons of memory. What you haven't shown,
> is
> (a) how much does that 100m shrink by if op_sibling and op_next are
> combined?
> (b) what proportion of total memory would that 100m be if the modules
> were actually used in anger - normally you only load modules to use them
> for something, at which point you're creating lots of SVs atc.
>
>   

a) I cant answer, since Ive not been able to s/struct/union/ and get 
something working.
To demonstrate 20 MB of savings (the 20% WAG), Id have to be further along.
You may infer that Im looking for clues, none heard yet :-(

b) is a low-effort existential proof.  For more reality, substitute a 
large mod-perl app, with 30 or 40
processes running simultaneously.  I dont have such a setup, but surely 
someone here does,
and could run top on the box and send results.
Besides, the point was to separate data (which has already been 
squeezed, thanks NickC)
from code, not to give a real-world example.


If this is seen as a purely hand-waving non-solution to a non-problem,
then Ill look elsewhere for my entertainments.
OTOH,
If you/someone could look at 2nd patch, and help me understand why the 
prints I added to Perl_linklist
show next == self, I might be able to make some progress towards a).

Im resending patches:
1- drops the bytecode bits that were recently dropped from bleadperl.
2- disables the PerlIO_printfs added into Perl_linklist, so that make 
test passes.

thanks
0
jim
9/13/2006 4:12:15 PM
--------------030506010206090808080803
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

Dave Mitchell wrote:
> On Mon, Sep 11, 2006 at 07:58:30PM -0600, Jim Cromie wrote:
>   
>> Your question prompted me to look for my own estimate of code-memory size,
>> specifically at process size, b4 and after loading Phalanx100.
>>
>>  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
>> b4
>>  531 jimc      15   0  9496 5976 1528 S  0.0  1.2   0:00.37 perl
>> after
>>  531 jimc      23   0  100m  90m 4128 S  0.0 17.9   0:06.91 perl
>>
>>
>> Thats 90 MB of growth, enough I think to warrant further discussion.
>> Ive attached my script, hopefully its repeatable.
>>     
>
> I don't really see what point you are trying to make here. Yes, if you
> load tons of  modules you will use tons of memory. What you haven't shown,
> is
> (a) how much does that 100m shrink by if op_sibling and op_next are
> combined?
> (b) what proportion of total memory would that 100m be if the modules
> were actually used in anger - normally you only load modules to use them
> for something, at which point you're creating lots of SVs atc.
>
>   

a) I cant answer, since Ive not been able to s/struct/union/ and get 
something working.
To demonstrate 20 MB of savings (the 20% WAG), Id have to be further along.
You may infer that Im looking for clues, none heard yet :-(
I'll concede that merely relocating op-siblings into the closest-cop 
will not, by itself,
save any memory.  It should improve cache-locality, which may not be 
visible,
except perhaps with cachegrind.  But eventually, it should be possible 
to add
a 'no uninit-var-reporting' feature, which drops all op-siblings, for 
use where its warranted.

b) is a low-effort existential proof.  Substitute a large mod-perl app, 
with 30 or 40
processes running simultaneously for more reality.  I dont have such a 
setup,
but surely someone here does, and could run top on the box.
I dont think we need to go about 'proving' that no case exists.
Besides, the point was to separate data (which has already been 
squeezed, thanks NickC)
from code, not to give a real-world example.


If this is seen as a purely hand-waving non-solution to a non-problem,
then I'll look elsewhere for my entertainments.
OTOH,
If you could look at 2nd patch, and help me understand why the prints 
added to Perl_linklist
show next == self, I might be able to make some progress towards a).

Im resending patches:
1- drops the bytecode chunks, since theyve been purged from bleadperl.
2- disables the prints I added to Perl_linklist, so that 'make test' passes.

thanks

--------------030506010206090808080803
Content-Type: application/x-gzip;
 name="diff.ns-abstract.test-ok-99a.gz"
Content-Transfer-Encoding: base64
Content-Disposition: inline;
 filename="diff.ns-abstract.test-ok-99a.gz"

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
TQEA
--------------030506010206090808080803
Content-Type: text/plain;
 name="diff.ns-cop-sibling-proto"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="diff.ns-cop-sibling-proto"

diff -ruNp -X exclude-diffs ns-abstract/op.c ns-cop-sibling/op.c
--- ns-abstract/op.c	2006-09-04 13:00:26.000000000 -0600
+++ ns-cop-sibling/op.c	2006-09-05 12:29:11.000000000 -0600
@@ -527,17 +527,191 @@ Perl_op_refcnt_unlock(pTHX)
     OP_REFCNT_UNLOCK;
 }
 
+/* 
+=head1 Implementation: out-of-OP op-siblings
+
+In the bottom up up complier pass, perly.y emits op-twigs, setting
+op-type/class specific pointers in each OP to link them together
+according to the expression just parsed.  Sequences of
+
+optrees are generated using op_sibling and 
+represent both sequential and expression dependencies.  The execution
+order pass relinks the tree of OPs for fast execution, using the
+op_next pointer.
+
+Our premise is that sibling pointers are used rarely after op_next is
+set, so, at listlink()-age time, we copy op_sibling to an associative
+array, and intercept later reads.  This allows us to instrument that
+read, find out who is using the value, and optimize later, starting
+with removing in-OP storage for the pointer and getting it from the
+hash.  This shrinks the optree by ~20%, and may have other benefits.
+
+=head2 Implementation: patch development
+
+ 1.abstract all refs to op_next, op_sibling with macros
+ .  PERL_OP_NEXT,    PERL_OP_SIBLING
+ .  PERL_OP_SETNEXT, PERL_OP_SETSIBLING
+ . set OP_NEXT_SIBL_IMP to 0,1,2: to choose pair, struct, union reps
+
+ 2.extend COP, adding struct OpPair {next; sibl} ops[30]
+ .  populate it in linklist
+
+This wont work as is; in linklist(), assert(PL_curcop == &PL_compiling),
+so theres no COP context
+
+
+ 3.compile vs runtime (TBC)
+ .  PERL_OP_SIBLING should call instrumentation
+ .  use IN_PERL_RUNTIME to toggle PERL_OP_SIBLING defn
+
+=head2 Future Optimizations
+
+At this point (patch 2), the patch increases total memory use, but
+we're looking forward to several optimizations:
+
+1 not all ops have siblings, only those that do get added
+
+2 we hope that op-shrink, and segregating hot fields from cold, will
+  improve cache performance, though cache arguably works much better
+  on C code, and pp_code probably dwarfs the 20% opcode savings.
+
+3 analysis of the uninit-var feature may reveal that only some ops
+  need to be hashed to support the reporting for the vars in a given
+  code block.
+
+4 we can add a feature to allow the coder to suppress the hashing of
+  siblings entirely, for use on well-tested code.
+
+5 OpPair storage can be unioned with op_siblings vector, which its
+  converted to if/when linklist(op) acts upon COPs.  At this point,
+  the op can be shrunk to fit the op_sibling vector actually needed.
+
+=cut
+*/
+
+#if 0
+
+/* borrowed verbatim from util.c */
+
+STATIC COP*
+S_closest_cop(pTHX_ COP *cop, OP *o)
+{
+    /* Look for PL_op starting from o.  cop is the last COP we've seen. */
+
+    if (!o || o == PL_op) return cop;
+
+    if (o->op_flags & OPf_KIDS) {
+	OP *kid;
+	for (kid = cUNOPo->op_first; kid; kid = PERL_OP_SIBLING(kid))
+	{
+	    COP *new_cop;
+
+	    /* If the OP_NEXTSTATE has been optimised away we can still use it
+	     * the get the file and line number. */
+
+	    if (kid->op_type == OP_NULL && kid->op_targ == OP_NEXTSTATE)
+		cop = (COP *)kid;
+
+	    /* Keep searching, and return when we've found something. */
+
+	    new_cop = S_closest_cop(aTHX_ cop, kid);
+	    if (new_cop) return new_cop;
+	}
+    }
+
+    /* Nothing found. */
+
+    return 0;
+}
+#endif
+
+/* during compilation (pre-cop linklist phase), we save sibling
+   pointers into a global 'hash', then when bottom up linklist-ing
+   reaches up to the COP, we can sweep those hash entries into
+   cop-private storage.
+
+   With that data in place, we should be able to find it in the COP,
+   which allows us to pull it out of the OPs, and save ~20% storage in
+   the OPs themselves.
+
+   Use of op-sibling ptr is rare once the code is linked for running,
+   so a higher fetch-cost is mostly irrelevant, since its needed only
+   to support uninit-var reporting, which is purely a debug-friendly
+   feature.
+
+   Eventually, we may be able to eliminate the entries that are
+   representable by a flag instead, forex op->next == op, or some
+   other knowable (and useful) properties.  For some 'production'
+   situations, where the code-base is well-known, it may be possible
+   to drop the hash entirely.
+*/
+
 /* Contextualizers */
 
-#define LINKLIST(o) (PERL_OP_NEXT(o) ? PERL_OP_NEXT(o) : linklist((OP*)o))
+#define LINKLIST(o) Perl_linklist(aTHX_ o)
+/* was
+   #define LINKLIST(o) 	(PERL_OP_NEXT(o) ? PERL_OP_NEXT(o) : linklist((OP*)o))
+
+   removing the shortcut means that Perl_linklist sees COPs, which
+   lets us collect and attach sibling info to the COP, where its
+   reasonably close at hand for (revamped) uninit-var use.
+*/
+
+static PTR_TBL_t *siblings;
+static char indent[] = "                                         ";
+static int ipi = -1;
 
 OP *
 Perl_linklist(pTHX_ OP *o)
 {
     OP *first;
+    COP *cop;
+    int type;
 
-    if (PERL_OP_NEXT(o))
+    assert(PL_curcop == &PL_compiling);
+
+    /*
+    cop = S_closest_cop(aTHX_ PL_curcop, PERL_OP_SIBLING(PL_curcop));
+    if ((COP*)o == cop)
+	PerlIO_printf(Perl_debug_log, "linklist closest cop\n");
+    */
+
+    type = o->op_type;
+    if (type == OP_NULL)
+	type = (OPCODE)o->op_targ;
+
+    if (!siblings) siblings = ptr_table_new();
+    if (ipi == -1) ipi = strlen(indent)-1;
+
+    if (type == OP_NEXTSTATE || type == OP_SETSTATE || type == OP_DBSTATE) {
+	PTR_TBL_ENT_t *ent;
+	OP *next = PERL_OP_SIBLING(o);	// next not set yet
+	/* cop's next points back at itself !! */
+
+	if (next && 0)
+	    PerlIO_printf(Perl_debug_log, "%s COP %p -> %p %s\n", 
+			  indent+ipi, o, next,
+			  PL_op_name[next->op_type]);
+#if 0
+	for (; next && next != PERL_OP_NEXT(next) && next != o;
+	     next=PERL_OP_NEXT(next))
+	{
+	    // follow chain,
+	    ent = ptr_table_fetch(siblings, next);
+	    if (ent && ent->newval == PERL_OP_SIBLING(next))
+		PerlIO_printf(Perl_debug_log, "woohoo\n");
+	}
+#endif	
+    }
+    if (0)
+	PerlIO_printf(Perl_debug_log, "%s %p %s s:%p n:%p\n", indent+ipi--,
+		      o, PL_op_name[o->op_type],
+		      PERL_OP_SIBLING(o), PERL_OP_NEXT(o));
+    
+    if (PERL_OP_NEXT(o)) {
+	ipi++;
 	return PERL_OP_NEXT(o);
+    }
 
     /* establish postfix order */
     first = cUNOPo->op_first;
@@ -546,7 +720,20 @@ Perl_linklist(pTHX_ OP *o)
 	PERL_OP_SETNEXT(o, LINKLIST(first));
 	kid = first;
 	for (;;) {
+
 	    if (PERL_OP_SIBLING(kid)) {
+
+		ptr_table_store(siblings, (SV*)o, PERL_OP_SIBLING(o));
+
+		/* save op_sibling in COP before setting op_next,
+		   which would overwrite storage if shared/optimized */
+
+		/* current op entry for siblings
+		   int opincop = cop->opct++;
+		   cop->ops[opincop].sibl = PERL_OP_SIBLING(kid);
+		   cop->ops[opincop].self = kid;
+		*/
+
 		PERL_OP_SETNEXT(kid, LINKLIST(PERL_OP_SIBLING(kid)));
 		kid = PERL_OP_SIBLING(kid);
 	    } else {
@@ -555,8 +742,15 @@ Perl_linklist(pTHX_ OP *o)
 	    }
 	}
     }
-    else
+    else {
+	ptr_table_store(siblings, (SV*)o, PERL_OP_SIBLING(o));
 	PERL_OP_SETNEXT(o,o);
+    }
+
+    if (0)
+	PerlIO_printf(Perl_debug_log, "%s %p %s s:%p n:%p\n", indent+ipi++,
+		      o, PL_op_name[o->op_type],
+		      PERL_OP_SIBLING(o), PERL_OP_NEXT(o));
     
     return PERL_OP_NEXT(o);
 }
@@ -3346,7 +3540,7 @@ Perl_pmruntime(pTHX_ OP *o, OP *expr, bo
 	    PERL_OP_SETNEXT(repl, (OP*)rcop);
 
 	    pm->op_pmreplroot = scalar((OP*)rcop);
-	    pm->op_pmreplstart = (OP*) LINKLIST(rcop);
+	    pm->op_pmreplstart = LINKLIST((OP*)rcop);
 	    PERL_OP_SETNEXT((OP*)rcop, 0);
 	}
     }

--------------030506010206090808080803--
0
jim
9/13/2006 4:47:46 PM
On Wed, Sep 13, 2006 at 10:47:46AM -0600, Jim Cromie wrote:
> Dave Mitchell wrote:
> >(a) how much does that 100m shrink by if op_sibling and op_next are
> >combined?

> a) I cant answer, since Ive not been able to s/struct/union/ and get 
> something working.

what you could do is temporarily add an extra ptr field to BASEOP, and see
how much the 100m grows by; we can then assume it would shrink by the same
amount.

-- 
Nothing ventured, nothing lost.
0
davem
9/13/2006 5:42:05 PM
On 9/13/06, Jim Cromie <jim.cromie@gmail.com> wrote:
> Dave Mitchell wrote:
> > On Mon, Sep 11, 2006 at 07:58:30PM -0600, Jim Cromie wrote:
> >
> b) is a low-effort existential proof.  Substitute a large mod-perl app,
> with 30 or 40
> processes running simultaneously for more reality.  I dont have such a
> setup,
> but surely someone here does, and could run top on the box.

B::Deparse and thus Data::Dumper and Data::Dump::Streamer both care
about ->sibling existing in normal, not debugging runtime. Do your
processes need to be able to deparse perl?

Josh
0
twists
9/13/2006 7:02:58 PM
Dave Mitchell wrote:
> On Wed, Sep 13, 2006 at 10:47:46AM -0600, Jim Cromie wrote:
>   
>> Dave Mitchell wrote:
>>     
>>> (a) how much does that 100m shrink by if op_sibling and op_next are
>>> combined?
>>>       
>
>   
>> a) I cant answer, since Ive not been able to s/struct/union/ and get 
>> something working.
>>     
>
> what you could do is temporarily add an extra ptr field to BASEOP, and see
> how much the 100m grows by; we can then assume it would shrink by the same
> amount.
>
>   

with maintperl (threads)
b4
30231 jimc      15   0  9868 6264 1900 S  0.0  1.2   0:00.60 perl
after
30231 jimc      18   0 99.5m  88m 4596 S  0.0 17.7   0:10.81 perl

with extra pointer added
b4
 1877 jimc      15   0  9976 6400 1900 S  0.0  1.2   0:00.62 perl

 1877 jimc      16   0 99.9m  89m 4540 S  0.0 17.7   0:06.31 perl


IOW - trivial  growth.

This may be due to the 99m being dominated by XS code,
which is probably the point you were making earlier..  :-[

As time permits, I'll construct a use-pure-perl-mods script, and see if 
it shows anything more.

thanks
0
jim
9/14/2006 4:34:27 PM
Dave Mitchell wrote:
> On Wed, Sep 13, 2006 at 10:47:46AM -0600, Jim Cromie wrote:
>   
>> Dave Mitchell wrote:
>>     
>>> (a) how much does that 100m shrink by if op_sibling and op_next are
>>> combined?
>>>       
>
>   
>> a) I cant answer, since Ive not been able to s/struct/union/ and get 
>> something working.
>>     
>
> what you could do is temporarily add an extra ptr field to BASEOP, and see
> how much the 100m grows by; we can then assume it would shrink by the same
> amount.
>
>   
Rewrote the script to build then eval a big&dumb chunk of math (attached)
It shows real growth in mem of 10%, 12%, 8%

.../maintperl/perl -I ../maintperl/lib usepp.pl 50
PID 30959
b4 string build         Size => 1783
after string build      Size => 2429
after eval              Size => 17027

ns-extra/perl -I ns-extra/lib usepp.pl 50
PID 30960
b4 string build         Size => 2052
after string build      Size => 2697
after eval              Size => 18773

.../maintperl/perl -I ../maintperl/lib usepp.pl 150
PID 30962
b4 string build         Size => 1785
after string build      Size => 3630
after eval              Size => 47611

ns-extra/perl -I ns-extra/lib usepp.pl 150
PID 30964
b4 string build         Size => 2050
after string build      Size => 3894
after eval              Size => 53303

.../maintperl/perl -I ../maintperl/lib usepp.pl 250
PID 31120
b4 string build         Size => 1783
after string build      Size => 4827
after eval              Size => 76158

ns-extra/perl -I ns-extra/lib usepp.pl 250
PID 31122
b4 string build         Size => 2049
after string build      Size => 5092
after eval              Size => 82863


Yes, the hard parts remain, but now theres cause to look more closely.

0
jim
9/15/2006 11:20:04 PM
On Fri, Sep 15, 2006 at 05:20:04PM -0600, Jim Cromie wrote:
> Rewrote the script to build then eval a big&dumb chunk of math (attached)
> It shows real growth in mem of 10%, 12%, 8%

A couple of comments. First, I'm not sure you meant this: your eval string
gets compiled as:

    $sum[\"$i"] = $in1[\"$i"] + $in2[\"$j"];
    ... etc

    rather than 

    $sum[3] = $in1[3] + $in2[7];
    ... etc

Second, its not a very realistic example, since you just have one
extremely big sub that contains no lexical vars.

-- 
"Do not dabble in paradox, Edward, it puts you in danger of fortuitous
wit." -- Lady Croom - Arcadia
0
davem
9/16/2006 12:17:00 AM
--------------000006040307070409040004
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

Joshua ben Jore wrote:
> On 9/13/06, Jim Cromie <jim.cromie@gmail.com> wrote:
>> Dave Mitchell wrote:
>> > On Mon, Sep 11, 2006 at 07:58:30PM -0600, Jim Cromie wrote:
>> >
>> b) is a low-effort existential proof.  Substitute a large mod-perl app,
>> with 30 or 40
>> processes running simultaneously for more reality.  I dont have such a
>> setup,
>> but surely someone here does, and could run top on the box.
>
> B::Deparse and thus Data::Dumper and Data::Dump::Streamer both care
> about ->sibling existing in normal, not debugging runtime. Do your
> processes need to be able to deparse perl?
>
> Josh
>
Once, long ago, I worked on an app that data-dumped code. 
Not since then.  Its not a must-have for me.

But I dont want to get the dialog hung on absense/presence of any given 
data.
My hope is that we can, in discrete steps;
-abstract it
-move it into cop
-compress it
-optionally toss it - for 99% of time when its not needed.



For curiosity, I wrote attached script to instrument B::OP::sibling,
and collect the siblings found in &foo() into %seen.  Most are seen 
once, some 2 or more times.
I wanted to see if there were any that werent traversed.

I am puzzled that Im counting 27 siblings, and only 16 ops
(The late redef of B::OP::sibling cut it from ~240 siblings)

.....
&$x();
print Dumper(\%seen);
print "got $opcount OPs\n";
print 'got ', scalar keys %seen, " siblings\n";
'???';
B::Concise::compile(CODE(0x9fcf5d8))
9  0x9f6ed38 <1> leavesub[1 ref] K/REFC,1 ->(end) 0
-  0x9f6ee20    <@> lineseq KP ->9 0x9f6ed38
1  0x9ce93f8       <;> nextstate(main 300 deparseit:27) v ->2 0x9f6edf0
8  0x9f6ee48       <@> print sK ->9 0x9f6ed38
2  0x9f6edf0          <0> pushmark s ->3 0x9f6ed08
7  0x9fe2338          <2> add[t4] sK/2 ->8 0x9f6ee48
5  0x9ce9438             <2> add[t2] sK/2 ->6 0x9f6ef18
-  0x9f9ccc8                <1> ex-aelem sK/2 ->4 0x9f6ef68
-  0x9f6f038                   <1> ex-rv2av sKR/1 ->- 0x9f6ee70
3  0x9f6ed08                      <#> aelemfast[*_] s/1 ->4 0x9f6ef68
-  0x9f6ee70                   <0> ex-const s ->- 0x9f9ccc8
4  0x9f6ef68                <$> const[IV 42] s ->5 0x9ce9438
-  0x9cd3100             <1> ex-aelem sK/2 ->7 0x9fe2338
-  0x9f6ef38                <1> ex-rv2av sKR/1 ->- 0x9f6eee8
6  0x9f6ef18                   <#> aelemfast[*_] s ->7 0x9fe2338
-  0x9f6eee8                <0> ex-const s ->- 0x9cd3100
$VAR1 = {
          'B::OP=SCALAR(0x9ff548c)' => 1,
          'B::NULL=SCALAR(0x9fefc64)' => 3,
          'B::UNOP=SCALAR(0x9ffecc0)' => 1,
          'B::NULL=SCALAR(0x9fe5600)' => 1,
          'B::LISTOP=SCALAR(0x9ffaf7c)' => 1,
          'B::UNOP=SCALAR(0x9fefd84)' => 1,
          'B::SVOP=SCALAR(0x9feb5d8)' => 1,
          'B::NULL=SCALAR(0x9ffea50)' => 1,
          'B::BINOP=SCALAR(0x9feb8a8)' => 1,
          'B::OP=SCALAR(0x9ffb1c8)' => 1,
          'B::OP=SCALAR(0x9ff599c)' => 1,
          'B::NULL=SCALAR(0x9ff5abc)' => 1,
          'B::NULL=SCALAR(0x9fef7c0)' => 1,
          'B::NULL=SCALAR(0x9ffadd8)' => 2,
          'B::NULL=SCALAR(0x9ce8468)' => 2,
          'B::NULL=SCALAR(0x9ffef60)' => 1,
          'B::NULL=SCALAR(0x9ffaf34)' => 1,
          'B::NULL=SCALAR(0x9fe5504)' => 1,
          'B::SVOP=SCALAR(0x9fff020)' => 1,
          'B::BINOP=SCALAR(0x9ff8a88)' => 1,
          'B::NULL=SCALAR(0x9ff4d84)' => 1,
          'B::LISTOP=SCALAR(0x9ffec90)' => 1,
          'B::NULL=SCALAR(0x9ffd708)' => 1,
          'B::NULL=SCALAR(0x9fe9520)' => 1,
          'B::NULL=SCALAR(0x9fddbc4)' => 1,
          'B::OP=SCALAR(0x9ff4d84)' => 1,
          'B::NULL=SCALAR(0x9fdde64)' => 1
        };
got 16 OPs
got 27 siblings

Any comments ?

thx
jimc

--------------000006040307070409040004
Content-Type: text/plain;
 name="deparseit"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="deparseit"

#!/usr/local/bin/perl
# -w

use B;
use B::Deparse;
use B::Concise 'add_style';
use Data::Dumper;

add_style("c2" =>
          "#hyphseq2 #addr (*(   (x( ;)x))*)<#classsym> "
          . "#exname#arg(?([#targarglife])?)~#flags(?(/#private)?)"
          . "(x(;~->#next)x) #nextaddr\n"
          , "  (*(    )*)     goto #seq\n",
          "(?(<#seq>)?)#exname#arg(?([#targarglife])?)");

%seen;

package B;
my $op_sibling_base;
BEGIN {
    $op_sibling_base = \&B::OP::sibling;
}

package main;

sub foo {
    print $_[1]+ 42 + $_[0];
    # print $_[1]+ 23 + $_[0];
}

print "hello world\n";
my $x;

$x = B::Deparse::compile(\&foo);
$x->();

eval q{
package B::OP;
sub sibling {

    my $sibling; 
    # print "caught B::Opnext invoke @_\n";
    # dump @_;
    $sibling = $op_sibling_base->(@_);
    $::seen{$sibling}++;
    $sibling;
}
};
my $opcount;
$x = B::Concise::compile('-c2', \&foo);
B::Concise::add_callback( sub{ $opcount++; });
#B::Concise::walk_output(\my $out);

$x->();
print Dumper \%seen;
print "got $opcount OPs\n";
print "got ", scalar keys %seen, " siblings\n";

if (0) {
    my $deparse = B::Deparse->new("-p", "-sC");
    $body = $deparse->coderef2text(\&foo);
    eval "sub func $body"; # the inverse operation
}

--------------000006040307070409040004--
0
jim
9/19/2006 4:58:59 AM
On 9/18/06, Jim Cromie <jim.cromie@gmail.com> wrote:
> I am puzzled that Im counting 27 siblings, and only 16 ops
> (The late redef of B::OP::sibling cut it from ~240 siblings)

You forgot to count all the trailing NULL siblings. Those B::NULL ops
are really just null pointers prettied up into objects.

9  0x9f6ed38 <1> leavesub[1 ref] K/REFC,1 ->(end) 0
-  0x9f6ee20    <@> lineseq KP ->9 0x9f6ed38
1  0x9ce93f8       <;> nextstate(main 300 deparseit:27) v ->2 0x9f6edf0
8  0x9f6ee48       <@> print sK ->9 0x9f6ed38
2  0x9f6edf0          <0> pushmark s ->3 0x9f6ed08
7  0x9fe2338          <2> add[t4] sK/2 ->8 0x9f6ee48
5  0x9ce9438             <2> add[t2] sK/2 ->6 0x9f6ef18
-  0x9f9ccc8                <1> ex-aelem sK/2 ->4 0x9f6ef68
-  0x9f6f038                   <1> ex-rv2av sKR/1 ->- 0x9f6ee70
3  0x9f6ed08                      <#> aelemfast[*_] s/1 ->4 0x9f6ef68
                                  NULL
-  0x9f6ee70                   <0> ex-const s ->- 0x9f9ccc8
                               NULL
4  0x9f6ef68                <$> const[IV 42] s ->5 0x9ce9438
                            NULL
-  0x9cd3100             <1> ex-aelem sK/2 ->7 0x9fe2338
-  0x9f6ef38                <1> ex-rv2av sKR/1 ->- 0x9f6eee8
6  0x9f6ef18                   <#> aelemfast[*_] s ->7 0x9fe2338
                               NULL
-  0x9f6eee8                <0> ex-const s ->- 0x9cd3100
                            NULL
                         NULL
                      NULL
                   NULL
                NULL
             NULL

Josh
0
twists
9/19/2006 5:26:57 PM
Reply: