ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Faster/Faster.pm
(Generate patch)

Comparing Faster/Faster.pm (file contents):
Revision 1.22 by root, Fri Mar 10 22:41:47 2006 UTC vs.
Revision 1.27 by root, Sat Mar 11 23:06:59 2006 UTC

34=over 4 34=over 4
35 35
36=cut 36=cut
37 37
38package Faster; 38package Faster;
39
40no warnings;
39 41
40use strict; 42use strict;
41use Config; 43use Config;
42use B (); 44use B ();
43#use Digest::MD5 (); 45#use Digest::MD5 ();
44use DynaLoader (); 46use DynaLoader ();
45use File::Temp (); 47use Digest::MD5 ();
48use Storable ();
46 49
47BEGIN { 50BEGIN {
48 our $VERSION = '0.01'; 51 our $VERSION = '0.01';
49 52
50 require XSLoader; 53 require XSLoader;
51 XSLoader::load __PACKAGE__, $VERSION; 54 XSLoader::load __PACKAGE__, $VERSION;
52} 55}
56
57my $CACHEDIR = $ENV{FASTER_CACHE} || do {
58 require File::Temp;
59 File::Temp::tempdir (CLEANUP => 1)
60};
53 61
54my $COMPILE = "$Config{cc} -c -I$Config{archlibexp}/CORE $Config{optimize} $Config{ccflags} $Config{cccdlflags}"; 62my $COMPILE = "$Config{cc} -c -I$Config{archlibexp}/CORE $Config{optimize} $Config{ccflags} $Config{cccdlflags}";
55my $LINK = "$Config{ld} $Config{ldflags} $Config{lddlflags} $Config{ccdlflags}"; 63my $LINK = "$Config{ld} $Config{ldflags} $Config{lddlflags} $Config{ccdlflags}";
56my $LIBS = "$Config{libs}"; 64my $LIBS = "$Config{libs}";
57my $_o = $Config{_o}; 65my $_o = $Config{_o};
65my $verbose = $ENV{FASTER_VERBOSE}+0; 73my $verbose = $ENV{FASTER_VERBOSE}+0;
66 74
67our $source; 75our $source;
68 76
69our @ops; 77our @ops;
78our $insn;
70our $op; 79our $op;
71our $op_name; 80our $op_name;
72our @op_loop; 81our @op_loop;
73our %op_regcomp; 82our %op_regcomp;
74 83
84# ops that cause immediate return to the interpreter
75my %f_unsafe = map +($_ => undef), qw( 85my %f_unsafe = map +($_ => undef), qw(
76 leavesub leavesublv return 86 leavesub leavesublv return
77 goto last redo next 87 goto last redo next
78 eval flip leaveeval entertry 88 eval flip leaveeval entertry
79 formline grepstart mapstart 89 formline grepstart mapstart
80 substcont entereval require 90 substcont entereval require
81); 91);
82 92
83# pushmark extend=0 93# ops with known stack extend behaviour
84# padsv extend=1 94# the values given are maximum values
85# padav extend=1 95my %extend = (
86# padhv extend=1 96 pushmark => 0,
87# padany extend=1 97 nextstate => 0, # might reduce the stack
88# const extend=1 98 unstack => 0,
99 enter => 0,
89 100
101 stringify => 0,
102 not => 0,
103 and => 0,
104 or => 0,
105 gvsv => 0,
106 rv2gv => 0,
107 preinc => 0,
108 predec => 0,
109 postinc => 0,
110 postdec => 0,
111 aelem => 0,
112 helem => 0,
113 qr => 1, #???
114 pushre => 1,
115 gv => 1,
116 aelemfast => 1,
117 aelem => 0,
118 padsv => 1,
119 const => 1,
120 pop => 1,
121 shift => 1,
122 eq => -1,
123 ne => -1,
124 gt => -1,
125 lt => -1,
126 ge => -1,
127 lt => -1,
128 cond_expr => -1,
129 add => -1,
130 subtract => -1,
131 multiply => -1,
132 divide => -1,
133 aassign => 0,
134 sassign => -2,
135 method => 0,
136 method_named => 1,
137);
138
139# ops that do not need an ASYNC_CHECK
90my %f_noasync = map +($_ => undef), qw( 140my %f_noasync = map +($_ => undef), qw(
91 mapstart grepstart match entereval 141 mapstart grepstart match entereval
92 enteriter entersub leaveloop 142 enteriter entersub leaveloop
93 143
94 pushmark nextstate 144 pushmark nextstate
100 rv2av rv2cv rv2gv rv2hv refgen 150 rv2av rv2cv rv2gv rv2hv refgen
101 gv gvsv 151 gv gvsv
102 add subtract multiply divide 152 add subtract multiply divide
103 complement cond_expr and or not 153 complement cond_expr and or not
104 defined 154 defined
105 method_named 155 method method_named bless
106 preinc postinc predec postdec 156 preinc postinc predec postdec
107 aelem aelemfast helem delete exists 157 aelem aelemfast helem delete exists
108 pushre subst list join split concat 158 pushre subst list join split concat
109 length substr stringify ord 159 length substr stringify ord
110 push pop shift unshift 160 push pop shift unshift
111 eq ne gt lt ge le 161 eq ne gt lt ge le
112 regcomp regcreset regcmaybe 162 regcomp regcreset regcmaybe
113); 163);
114 164
115my %callop = ( 165my %callop = (
116 entersub => "(PL_ppaddr [OP_ENTERSUB]) (aTHX)", 166 entersub => "(PL_op->op_ppaddr) (aTHX)",
117 mapstart => "Perl_pp_grepstart (aTHX)", 167 mapstart => "Perl_pp_grepstart (aTHX)",
118); 168);
119 169
120sub callop { 170sub callop {
121 $callop{$op_name} || "Perl_pp_$op_name (aTHX)" 171 $callop{$op_name} || "Perl_pp_$op_name (aTHX)"
178 228
179if ($Config{useithreads} ne "define") { 229if ($Config{useithreads} ne "define") {
180 # disable optimisations on ithreads 230 # disable optimisations on ithreads
181 231
182 *op_const = sub { 232 *op_const = sub {
183 $source .= " { dSP; XPUSHs ((SV *)${$op->sv}L); PUTBACK; }\n"; 233 $source .= " { dSP; PUSHs ((SV *)${$op->sv}L); PUTBACK; }\n";
234
235 $ops[0]{follows_const}++ if @ops;#d#
184 236
185 out_next; 237 out_next;
186 }; 238 };
187 239
188 *op_gv = \&op_const; 240 *op_gv = \&op_const;
208 if (!($op->flags & B::OPf_MOD)) { 260 if (!($op->flags & B::OPf_MOD)) {
209 $source .= " if (SvGMAGICAL (sv)) sv = sv_mortalcopy (sv);\n"; 261 $source .= " if (SvGMAGICAL (sv)) sv = sv_mortalcopy (sv);\n";
210 } 262 }
211 263
212 $source .= " dSP;\n"; 264 $source .= " dSP;\n";
213 $source .= " XPUSHs (sv);\n"; 265 $source .= " PUSHs (sv);\n";
214 $source .= " PUTBACK;\n"; 266 $source .= " PUTBACK;\n";
215 $source .= " }\n"; 267 $source .= " }\n";
216 268
217 out_next; 269 out_next;
218 }; 270 };
219 271
220 *op_gvsv = sub { 272 *op_gvsv = sub {
221 $source .= " {\n"; 273 $source .= " {\n";
222 $source .= " dSP;\n"; 274 $source .= " dSP;\n";
223 $source .= " EXTEND (SP, 1);\n";
224 275
225 if ($op->private & B::OPpLVAL_INTRO) { 276 if ($op->private & B::OPpLVAL_INTRO) {
226 $source .= " PUSHs (save_scalar ((GV *)${$op->sv}L));\n"; 277 $source .= " PUSHs (save_scalar ((GV *)${$op->sv}L));\n";
227 } else { 278 } else {
228 $source .= " PUSHs (GvSV ((GV *)${$op->sv}L));\n"; 279 $source .= " PUSHs (GvSV ((GV *)${$op->sv}L));\n";
288 out_next; 339 out_next;
289} 340}
290 341
291sub op_padsv { 342sub op_padsv {
292 my $flags = $op->flags; 343 my $flags = $op->flags;
293 my $targ = $op->targ; 344 my $padofs = "(PADOFFSET)" . $op->targ;
294 345
295 $source .= <<EOF; 346 $source .= <<EOF;
296 { 347 {
297 dSP; 348 dSP;
298 XPUSHs (PAD_SV ((PADOFFSET)$targ)); 349 SV *sv = PAD_SVl ($padofs);
350EOF
351
352 if (($flags & B::OPf_MOD) && ($op->private & B::OPpLVAL_INTRO)) {
353 $source .= " SAVECLEARSV (PAD_SVl ($padofs));\n";
354 $ops[0]{follows_padsv_lval_intro}++ if @ops;#d#
355 }
356
357 $source .= <<EOF;
358 PUSHs (sv);
299 PUTBACK; 359 PUTBACK;
300EOF 360EOF
301 if ($op->flags & B::OPf_MOD) { 361
302 if ($op->private & B::OPpLVAL_INTRO) { 362 if (($flags & B::OPf_MOD) && ($op->private & B::OPpDEREF)) {
303 $source .= " SAVECLEARSV (PAD_SVl ((PADOFFSET)$targ));\n"; 363 $source .= " if (!SvROK (sv)) vivify_ref (sv, " . $op->private . " & OPpDEREF);\n";
304 } elsif ($op->private & B::OPpDEREF) {
305 my $deref = $op->private & B::OPpDEREF;
306 $source .= " Perl_vivify_ref (aTHX_ PAD_SVl ((PADOFFSET)$targ), $deref);\n";
307 }
308 } 364 }
365 $source .= " }\n";
366
367 out_next;
368}
369
370sub op_sassign {
371 $source .= <<EOF;
372 {
373 dSP;
374 dPOPTOPssrl;
375EOF
376 $source .= " SV *temp = left; left = right; right = temp;\n"
377 if $op->private & B::OPpASSIGN_BACKWARDS;
378
379 if ($insn->{follows_padsv_lval_intro} && !($op->private & B::OPpASSIGN_BACKWARDS)) {
380 # simple assignment - the target exists, but is basically undef
381 $source .= " SvSetSV (right, left);\n";
382 } else {
383 $source .= " SvSetMagicSV (right, left);\n";
384 }
385
309 $source .= <<EOF; 386 $source .= <<EOF;
387 SETs (right);
388 PUTBACK;
310 } 389 }
311EOF 390EOF
312 391
313 out_next; 392 out_next;
314} 393}
315 394
316# pattern const+ (or general push1) 395# pattern const+ (or general push1)
317# pattern pushmark return(?)
318# pattern pushmark gv rv2av pushmark padsv+o.ä. aassign 396# pattern pushmark gv rv2av pushmark padsv+o.ä. aassign
319 397
320# pattern const method_named
321sub op_method_named { 398sub op_method_named {
399 if ($insn->{follows_const}) {
322 $source .= <<EOF; 400 $source .= <<EOF;
401 {
402 dSP;
403 static SV *last_cv;
404 static U32 last_sub_generation;
405
406 /* simple "polymorphic" inline cache */
407 if (PL_sub_generation == last_sub_generation)
408 {
409 PUSHs (last_cv);
410 PUTBACK;
411 }
412 else
413 {
414 PL_op = nextop; nextop = Perl_pp_method_named (aTHX);
415
416 SPAGAIN;
417 last_sub_generation = PL_sub_generation;
418 last_cv = TOPs;
419 }
420 }
421EOF
422 } else {
423 $source .= <<EOF;
323 { 424 {
324 static HV *last_stash; 425 static HV *last_stash;
325 static SV *last_cv; 426 static SV *last_cv;
326 static U32 last_sub_generation; 427 static U32 last_sub_generation;
327 428
334 435
335 /* simple "polymorphic" inline cache */ 436 /* simple "polymorphic" inline cache */
336 if (stash == last_stash 437 if (stash == last_stash
337 && PL_sub_generation == last_sub_generation) 438 && PL_sub_generation == last_sub_generation)
338 { 439 {
339 XPUSHs (last_cv); 440 PUSHs (last_cv);
340 PUTBACK; 441 PUTBACK;
341 } 442 }
342 else 443 else
343 { 444 {
344 PL_op = nextop; nextop = Perl_pp_method_named (aTHX); 445 PL_op = nextop; nextop = Perl_pp_method_named (aTHX);
354 /* error case usually */ 455 /* error case usually */
355 PL_op = nextop; nextop = Perl_pp_method_named (aTHX); 456 PL_op = nextop; nextop = Perl_pp_method_named (aTHX);
356 } 457 }
357 } 458 }
358EOF 459EOF
460 }
359 461
360 out_next; 462 out_next;
361} 463}
362 464
363sub op_grepstart { 465sub op_grepstart {
406 local @op_loop; 508 local @op_loop;
407 local %op_regcomp; 509 local %op_regcomp;
408 510
409 my %opsseen; 511 my %opsseen;
410 my @todo = $cv->START; 512 my @todo = $cv->START;
513 my %op_target;
411 514
412 while (my $op = shift @todo) { 515 while (my $op = shift @todo) {
413 for (; $$op; $op = $op->next) { 516 for (; $$op; $op = $op->next) {
414 last if $opsseen{$$op}++; 517 last if $opsseen{$$op}++;
415 push @ops, $op;
416 518
417 my $name = $op->name; 519 my $name = $op->name;
418 my $class = B::class $op; 520 my $class = B::class $op;
419 521
522 my $insn = { op => $op };
523
524 push @ops, $insn;
525
526 if (exists $extend{$name}) {
527 my $extend = $extend{$name};
528 $extend = $extend->($op) if ref $extend;
529 $insn->{extend} = $extend if defined $extend;
530 }
531
532 push @todo, $op->next;
533
420 if ($class eq "LOGOP") { 534 if ($class eq "LOGOP") {
421 unshift @todo, $op->other; # unshift vs. push saves jumps 535 push @todo, $op->other;
536 $op_target{${$op->other}}++;
422 537
423 # regcomp/o patches ops at runtime, lets expect that 538 # regcomp/o patches ops at runtime, lets expect that
539 if ($name eq "regcomp" && $op->other->pmflags & B::PMf_KEEP) {
540 $op_target{${$op->first}}++;
424 $op_regcomp{${$op->first}} = $op->next 541 $op_regcomp{${$op->first}} = $op->next;
425 if $name eq "regcomp" && $op->other->pmflags & B::PMf_KEEP; 542 }
426 543
427 } elsif ($class eq "PMOP") { 544 } elsif ($class eq "PMOP") {
545 if (${$op->pmreplstart}) {
428 unshift @todo, $op->pmreplstart if ${$op->pmreplstart}; 546 unshift @todo, $op->pmreplstart;
547 $op_target{${$op->pmreplstart}}++;
548 }
429 549
430 } elsif ($class eq "LOOP") { 550 } elsif ($class eq "LOOP") {
431 push @op_loop, [$op->nextop, $op->lastop->next, $op->redoop->next];
432 push @todo, $op->nextop, $op->lastop->next, $op->redoop->next; 551 my @targ = ($op->nextop, $op->lastop->next, $op->redoop->next);
552
553 push @op_loop, \@targ;
554 push @todo, @targ;
555
556 $op_target{$$_}++ for @targ;
557 } elsif ($class eq "COP") {
558 $insn->{bblock}++ if defined $op->label;
433 } 559 }
434 } 560 }
435 } 561 }
562
563 $_->{bblock}++ for grep $op_target{${$_->{op}}}, @ops;
436 564
437 local $source = <<EOF; 565 local $source = <<EOF;
438OP *%%%FUNC%%% (pTHX) 566OP *%%%FUNC%%% (pTHX)
439{ 567{
440 register OP *nextop = (OP *)${$ops[0]}L; 568 register OP *nextop = (OP *)${$ops[0]->{op}}L;
441EOF 569EOF
442 570
443 while (@ops) { 571 while (@ops) {
444 $op = shift @ops; 572 $insn = shift @ops;
573
574 $op = $insn->{op};
445 $op_name = $op->name; 575 $op_name = $op->name;
446 576
577 my $class = B::class $op;
578
579 $source .= "\n/* start basic block */\n" if exists $insn->{bblock};#d#
447 $source .= "op_$$op: /* $op_name */\n"; 580 $source .= "op_$$op: /* $op_name */\n";
448 #$source .= "fprintf (stderr, \"$$op in op $op_name\\n\");\n";#d# 581 #$source .= "fprintf (stderr, \"$$op in op $op_name\\n\");\n";#d#
449 #$source .= "{ dSP; sv_dump (TOPs); }\n";#d# 582 #$source .= "{ dSP; sv_dump (TOPs); }\n";#d#
450 583
451 $source .= " PERL_ASYNC_CHECK ();\n" 584 $source .= " PERL_ASYNC_CHECK ();\n"
452 unless exists $f_noasync{$op_name}; 585 unless exists $f_noasync{$op_name};
453 586
454 if (my $can = __PACKAGE__->can ("op_$op_name")) { 587 if (my $can = __PACKAGE__->can ("op_$op_name")) {
455 # handcrafted replacement 588 # handcrafted replacement
589
590 if ($insn->{extend} > 0) {
591 # coalesce EXTENDs
592 # TODO: properly take negative preceeding and following EXTENDs into account
593 for my $i (@ops) {
594 last if exists $i->{bblock};
595 last unless exists $i->{extend};
596 my $extend = delete $i->{extend};
597 $insn->{extend} += $extend if $extend > 0;
598 }
599
600 $source .= " { dSP; EXTEND (SP, $insn->{extend}); PUTBACK; }\n"
601 if $insn->{extend} > 0;
602 }
603
456 $can->($op); 604 $can->($op);
457 605
458 } elsif (exists $f_unsafe{$op_name}) { 606 } elsif (exists $f_unsafe{$op_name}) {
459 # unsafe, return to interpreter 607 # unsafe, return to interpreter
460 assert "nextop == (OP *)$$op"; 608 assert "nextop == (OP *)$$op";
461 $source .= " return nextop;\n"; 609 $source .= " return nextop;\n";
462 610
463 } elsif ("LOGOP" eq B::class $op) { 611 } elsif ("LOGOP" eq $class) {
464 # logical operation with optionaö branch 612 # logical operation with optional branch
465 out_callop; 613 out_callop;
466 out_cond_jump $op->other; 614 out_cond_jump $op->other;
467 out_jump_next; 615 out_jump_next;
468 616
469 } elsif ("PMOP" eq B::class $op) { 617 } elsif ("PMOP" eq $class) {
470 # regex-thingy 618 # regex-thingy
471 out_callop; 619 out_callop;
472 out_cond_jump $op->pmreplroot if ${$op->pmreplroot}; 620 out_cond_jump $op->pmreplroot if $op_name ne "pushre" && ${$op->pmreplroot};
473 out_jump_next; 621 out_jump_next;
474 622
475 } else { 623 } else {
476 # normal operator, linear execution 624 # normal operator, linear execution
477 out_linear; 625 out_linear;
489 637
490 $source 638 $source
491} 639}
492 640
493my $uid = "aaaaaaa0"; 641my $uid = "aaaaaaa0";
642my %so;
494 643
495sub source2ptr { 644sub func2ptr {
496 my (@source) = @_; 645 my (@func) = @_;
497 646
498 my $stem = "/tmp/Faster-$$-" . $uid++; 647 #LOCK
648 my $meta = eval { Storable::retrieve "$CACHEDIR/meta" } || { version => 1 };
499 649
650 for my $f (@func) {
651 $f->{func} = "F" . Digest::MD5::md5_hex ($f->{source});
652 $f->{so} = $meta->{$f->{func}};
653 }
654
655 if (grep !$_->{so}, @func) {
656 my $stem;
657
658 do {
659 $stem = "$CACHEDIR/$$-" . $uid++;
660 } while -e "$stem$_so";
661
500 open FILE, ">:raw", "$stem.c"; 662 open my $fh, ">:raw", "$stem.c";
501 print FILE <<EOF; 663 print $fh <<EOF;
502#define PERL_NO_GET_CONTEXT 664#define PERL_NO_GET_CONTEXT
665#define PERL_CORE
503 666
504#include <assert.h> 667#include <assert.h>
505 668
506#include "EXTERN.h" 669#include "EXTERN.h"
507#include "perl.h" 670#include "perl.h"
508#include "XSUB.h" 671#include "XSUB.h"
509 672
510#define RUNOPS_TILL(op) \\ 673#define RUNOPS_TILL(op) \\
511while (nextop != (op)) \\ 674 while (nextop != (op)) \\
512 { \\ 675 { \\
513 PERL_ASYNC_CHECK (); \\ 676 PERL_ASYNC_CHECK (); \\
514 PL_op = nextop; nextop = (PL_op->op_ppaddr)(aTHX); \\ 677 PL_op = nextop; nextop = (PL_op->op_ppaddr)(aTHX); \\
515 }
516
517EOF
518 for (@source) {
519 my $func = $uid++;
520 $_ =~ s/%%%FUNC%%%/$func/g;
521 print FILE $_;
522 $_ = $func;
523 } 678 }
524 679
525 close FILE; 680EOF
681 for my $f (grep !$_->{so}, @func) {
682 next if $f->{so} = $meta->{$f->{func}}; # some cv's alias others
683
684 warn "compiling $f->{name} to $stem$_so:$f->{func}\n" if $verbose > 1;
685 my $source = $f->{source};
686 $source =~ s/%%%FUNC%%%/$f->{func}/g;
687 print $fh $source;
688 $meta->{$f->{func}} = $f->{so} = $stem;
689 }
690
691 close $fh;
526 system "$COMPILE -o $stem$_o $stem.c"; 692 system "$COMPILE -o $stem$_o $stem.c";
527 #d#unlink "$stem.c"; 693 #d#unlink "$stem.c";
528 system "$LINK -o $stem$_so $stem$_o $LIBS"; 694 system "$LINK -o $stem$_so $stem$_o $LIBS";
529 unlink "$stem$_o"; 695 unlink "$stem$_o";
696 }
530 697
698 for my $f (@func) {
699 my $stem = $f->{so};
700
531 my $so = DynaLoader::dl_load_file "$stem$_so" 701 my $so = ($so{$stem} ||= DynaLoader::dl_load_file "$stem$_so")
532 or die "$stem$_so: $!"; 702 or die "$stem$_so: $!";
533 703
534 #unlink "$stem$_so"; 704 #unlink "$stem$_so";
535 705
536 map +(DynaLoader::dl_find_symbol $so, $_), @source 706 $f->{ptr} = DynaLoader::dl_find_symbol $so, $f->{func}
707 or die "$f->{func} not found in $stem$_so: $!";
708 }
709
710 Storable::nstore $meta, "$CACHEDIR/meta";
711 # UNLOCK
537} 712}
538 713
539my %ignore; 714my %ignore;
540 715
541sub entersub { 716sub entersub {
543 718
544 my $pkg = $cv->STASH->NAME; 719 my $pkg = $cv->STASH->NAME;
545 720
546 return if $ignore{$pkg}; 721 return if $ignore{$pkg};
547 722
548 warn "compiling ", $cv->STASH->NAME, "\n" 723 warn "optimising ", $cv->STASH->NAME, "\n"
549 if $verbose; 724 if $verbose;
550 725
551 eval { 726 eval {
552 my @cv; 727 my @func;
553 my @cv_source;
554 728
555 # always compile the whole stash 729 # always compile the whole stash
556 my %stash = $cv->STASH->ARRAY; 730 my %stash = $cv->STASH->ARRAY;
557 while (my ($k, $v) = each %stash) { 731 while (my ($k, $v) = each %stash) {
558 $v->isa (B::GV::) 732 $v->isa (B::GV::)
561 my $cv = $v->CV; 735 my $cv = $v->CV;
562 736
563 if ($cv->isa (B::CV::) 737 if ($cv->isa (B::CV::)
564 && ${$cv->START} 738 && ${$cv->START}
565 && $cv->START->name ne "null") { 739 && $cv->START->name ne "null") {
740
566 push @cv, $cv; 741 push @func, {
742 cv => $cv,
743 name => $k,
567 push @cv_source, cv2c $cv; 744 source => cv2c $cv,
745 };
568 } 746 }
569 } 747 }
570 748
571 my @ptr = source2ptr @cv_source; 749 func2ptr @func;
572 750
573 for (0 .. $#cv) { 751 for my $f (@func) {
574 patch_cv $cv[$_], $ptr[$_]; 752 patch_cv $f->{cv}, $f->{ptr};
575 } 753 }
576 }; 754 };
577 755
578 if ($@) { 756 if ($@) {
579 $ignore{$pkg}++; 757 $ignore{$pkg}++;
604adds 1-3 C<assert>'s per perl op, to ensure that opcode order and C 782adds 1-3 C<assert>'s per perl op, to ensure that opcode order and C
605execution order are compatible. 783execution order are compatible.
606 784
607=item FASTER_CACHE 785=item FASTER_CACHE
608 786
609NOT YET IMPLEMENTED 787NOT YET IMPLEMENTED CORRECTLY, SHARING BEETWEEN INSTANCES IS IMPOSSIBLE
610 788
611Set a persistent cache directory that caches compiled code 789Set a persistent cache directory that caches compiled code
612fragments. Normally, code compiled by Faster will be deleted immediately, 790fragments. Normally, code compiled by Faster will be deleted immediately,
613and every restart will recompile everything. Setting this variable to a 791and every restart will recompile everything. Setting this variable to a
614directory makes Faster cache the generated files for re-use. 792directory makes Faster cache the generated files for re-use.

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines