Front page | perl.perl5.porters |
Postings from December 2007
optimising opcodes
Thread Next
From:
Nicholas Clark
Date:
December 24, 2007 11:25
Subject:
optimising opcodes
Message ID:
[email protected]
I was thinking about something Jim Cromie mailed me privately, and wondered:
"How many null ops do we have, and how many cache misses do they cause?"
For example:
$ perl -MO=Concise -e 'print foreach @ARGV'
g <@> leave[1 ref] vKP/REFC ->(end)
1 <0> enter ->2
2 <;> nextstate(main 1 -e:1) v ->3
3 <;> nextstate(main 1 -e:1) v ->4
f <2> leaveloop vK/2 ->g
8 <{> enteriter(next->c last->f redo->9) lKS ->d
- <0> ex-pushmark s ->4
- <1> ex-list lKM ->7
4 <0> pushmark sM ->5
6 <1> rv2av[t1] sKRM/1 ->7
5 <$> gv(*ARGV) s ->6
7 <$> gv(*_) s ->8
- <1> null vK/1 ->f
e <|> and(other->9) vK/1 ->f
d <0> iter s ->e
- <@> lineseq vK ->-
b <@> print vK ->c
9 <0> pushmark s ->a
- <1> ex-rv2sv sK/1 ->b
a <$> gvsv(*_) s ->b
c <0> unstack v ->d
lots of ex-ops. Pining for the fjords. Taking up space.
So I wondered:
0: How hard would it be to write some code to take an optree, and copy out all
the live ops into a new optree, skipping any nulled out ops?
1: Would that break the runtime?
2: How much would it break B::Deparse?
3: If you configured perl to use a slab allocator, and happened to do two
parses (first to determine the total size of ops, second to copy them in)
and thereby allocated a slab of the exact size, would that help more?
4: If you copied them in execution order, would that help more?
$ perl -MO=Concise,-exec -e 'print foreach @ARGV'
1 <0> enter
2 <;> nextstate(main 1 -e:1) v
3 <;> nextstate(main 1 -e:1) v
4 <0> pushmark sM
5 <$> gv(*ARGV) s
6 <1> rv2av[t1] sKRM/1
7 <$> gv(*_) s
8 <{> enteriter(next->c last->f redo->9) lKS
d <0> iter s
e <|> and(other->9) vK/1
9 <0> pushmark s
a <$> gvsv(*_) s
b <@> print vK
c <0> unstack v
goto d
f <2> leaveloop vK/2
g <@> leave[1 ref] vKP/REFC
[note, by doing things with a slab allocator, I believe that even though the
transformed ops are contiguous in memory, they are also indistinguishable from
regular ops created by the perl compiler]
5: How do you spot code executed only once, and avoid doing this?
(main program, eval blocks, top level code from do/require/use,
BEGIN/UNITCHECK/CHECK/INIT/END - ie basically anything not a "real"
subroutine definition)
6: Is the op_opt flag bit free at this point? Does having a bit help?
#ifdef BASEOP_DEFINITION
#define BASEOP BASEOP_DEFINITION
#else
#define BASEOP \
OP* op_next; \
OP* op_sibling; \
OP* (CPERLscope(*op_ppaddr))(pTHX); \
MADPROP_IN_BASEOP \
PADOFFSET op_targ; \
unsigned op_type:9; \
unsigned op_opt:1; \
and for bonus points - why are there two nextstate ops in that example, and
can we optimise one out? How often does it happen?
Nicholas Clark
Thread Next
-
optimising opcodes
by Nicholas Clark