Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!iuvax!cica!gatech!udel!mmdf From: V61%DHDURZ1.BITNET@cunyvm.cuny.edu (Ronald Lamprecht) Newsgroups: comp.os.minix Subject: huge preformance increase for ST and PC Message-ID: <20058@louie.udel.EDU> Date: 19 Jul 89 20:10:24 GMT Sender: mmdf@udel.EDU Lines: 594 This is a collection of patches that provid a general performance increase of 11% to 35% for the ST. One important patch should also be valid for the PC and I would guess, that the result is a speedup of 3 to 7%. The Improvements: - Elimination of unnecessary multiplications in proc.c message stuff: (This is common to ST and PC !) Did you know that every message causes 4 pointer multiplications during the phase where all interrupts are blocked ? And 3 of them could be avoided ! The multiplications are hidden in the statements "proc_addr(caller)" what expands to "&proc[NR_TASKS+caller]" causing a pointer multiplication that cannot be avoided. But there is no reason to repeat this calculation in every subroutine. Instead of transfering only the caller number the related pointer to the process structure could and should be passed, too. Furthermore even to 'sys_call' the interrupt handler can pass not only the variable caller (extracted from the external cur_proc) but also the related ptr which can be copied from 'proc_ptr'. By this means all 3 occurences of "proc_addr(caller)" in proc.c (sys_call,mini_send, mini_rec) can be avoided. Only "proc_addr(dest)" remains. You may be astonished, but on the ST this 4 multiplications costs more than 50% of the message passing time (appr. 300 us)! And while the processor executes these multiplications all interrupts are disabled ! On a PC it will not be that expensive because here you have only 16 bit multiplications. But due to my results (see below) for the ST I expect a more than noticable effect for the PC, too. I think this is a good example for "The Book" to show that it is not important to program everthing in assembler, but that you should take care what C statements you choose in time-critical parts. Effected by this patch are: kernel/proc.c - new arguments for sys_call,mini_send,mini_rec elimination of "proc_addr(caller)" changed references for mini_send,mini_rec (notice: "proc_addr(HARDWARE)" will be evaluated by the compiler and causes no multiplication!) kernel/system.c - changed reference to mini_* (PC: please check *.c for further mini_*,sys_call references !) kernel/stmpx.s - changed reference to sys_call (PC: change reference in klib88.s to sys_call !) (PC: please note that the provided ST patches cannot be applied directly, because of different line numbering and additional ST stuff in the old routines. Therefore I would propose, that the first one who applies these patches to the PC should post the appropriated PC cdiffs) There are more hidden multiplications and divisions (ptr differences) in proc.c. But they are in less time critical parts and more difficult to eliminate. So I thinks it's not worth changing them. All further patches apply only to 680x0 processors: - Reduction of 32bit multiplications to 16bit multiplications in _mli.s: The multiplications caused by "&proc[n]" statements will be compiled to _mli.s subroutine calls. 32 bit multiplications are necessary because both the element size and the index n could be greater than 64k (16 bit). But the 680(0,1)0 can only handle 16 bit multiplications. Therefore 4 mulu calls are necessary to realize the 32 bit multiplication. What I have done besides a slight code improvment is introducing a check before executing the multiplications. When both arguments are less 64k I will execute only the low word x low word multiplication and guess the upper LW result. But nevertheless I will execute all 4 multiplications if necessary. Effected by this patch is: lib/_mli.s - which should be substituted by the new one. (don't forget to recompile the lib and copy it to /usr/lib) Of cause this patch affects not only the kernel but all other programs that are linked with the new library. I haven't measured the speedup for average other programs yet. - improved phys_copy routine in optimized assembler: This improvement is based on Dale Schumacher's posting [1],[2] and Klamer Schutte's bug fix [3]. Indeed it's Klamer's version with a few further improvments (more registers used for copy). I didn't apply the patches for the FS as posted in [1] and rejected in [2] by Dale. Effected by this patch are: kernel/copy68k.s - new file containing phys_copy,... kernel/system.c - #if 0 #endif elimination of old C phys_copy I have been disappointed by the results of this patch. It's contribution to the total resulting speed increase is very low. Even though Dale wrote: >This version is >optimized for the typical copy block sizes used most by Minix (determined >by actual profiling, thanks Todd!), he couldn't detect with profiling the time that is wasted during the message handling with all interrupts disabled. In [2] Dale asked: > Also keep in mind that there is a factor of 16 >improvement in floppy performance just around the corner if someone can >find another way of feeding data to the floppy task more efficiently! Measuring a performance increase with synchronized floppy access doesn't make much sense. But you could speed up the floppy access by simply using another interleave factor (see results below). - improved shadow routines in optimized assembler: I rewrote the flipclicks,copyclicks and zeroclicks subroutines in stshadow. Effected by this patch are: kernel/stshadow.c - #if 0 #endif elimination of old C routines kernel/copy68k.s - new file containing assembler routines The speed increase by this patch is quite impressive (see below). - improved message_copy routine in optimized assembler: Instead of using the struct copy statement "*dst_m = *src_m" that is compiled by the ACK compiler to very bad code, I made a phys_copy call, where a special fast copy for messages is provided. This patch can be en/disabled by defining FSTRUCOPY in const.h. Other compilers may produce better structure copying code. Effected by this patch are: kernel/const.h - FSTRUCOPY defined for ST-ACK kernel/proc.c - phys_copy call introduced kernel/copy68k.s - phys_copy optimized for message copies Of course a further perfomance increase could be achieved by writing a special assembler message copy routine. But this routine would be dependent on the message size. My patches would work even if the message size is changed without adapting copy68k.s (only a slight performamce derease would be the consequence) Perfomance report: I have tested the following commands: test for: - img copy : cp minix.img /dev/fd0 phys_copy - vi -> flex: calling flex from vi (both with more than 300k) shadow - flex -? : a page of output to stderr messages - cp : cp /usr/bin/flex /tmp all - make : make make (a realistic application) all Results: old +mli +phys_cpy +shadow +caller_ptr +FSTRUCOPY perf.increase img copy 40"5 39"9 39"9 39"3 39"5 39"3 ? vi->flex 2"85 2"65 2"60 2"30 2"15 2"11 +35.1% flex -? 10"65 9"70 9"66 9"53 8"85 8"50 +25.3% (*) cp 5"48 5"14 5"04 5"05 4"41 4"39 +24.8% make 4'40" 4'12" +11.1% (*) after linking FS,MS with the _mli enhanced library version a further performance increase to 8"26 = 28.9% could be measured! (The time are measured by hand for 1 to 3 test calls -- thus giving 1/100 s values does not always make sense) Besides the floppy copy the results are quite impressive. Even make make that spends a lot of time in usermode shows a speed increase of 11%. Floppy access is overlapped by the rotation synchronization and therefor not a good test object. But floppy access depends on the used interleave factor. Because Dale's results [1] for the old kernel are extremly high: >1:30 of wall-clock time under my older kernel (around 0:55 with a >GCC-compiled kernel), but it only takes 0:12 seconds now! I made some experiments with different interleave factors (all disks formatted with 11 sec./track,1 sided for minix.img): Interleave factor cp minix.img /dev/fd0 cp minix.img /dev/fd0 from harddisk from RAM disk 1 1'01"5 1'02" 2 39"5 35" 3 37"2 36" 4 38"1 41" 5 50"8 53" 6 45"4 44" So I guess Dale uses an interleave of 1. Interesting is also the fact that sometimes the harddisk is faster than the RAM disk due to the DMA usage instead of a 68000 copy operation. MANIFEST: kernel/const.h_cdiff - FSTRUCOPY added kernel/copy68k.s - new file kernel/Makefile_cdiff - copy68k.s added kernel/proc.c_cdiff - message speedup (caller_ptr, FSTRUCOPY) kernel/stmpx.s - changed sys_call arguments kernel/stshadow.c_cdi - copy routines disabled kernel/system.c_cdiff - phys_copy disabled, mini_send arguments changed lib/_mli.s - new version README - this file [1] Fast phys_copy() and direct FS copying by Dale Schumacher from 13 Jun 89 [2] Fast kernel copy BUG! by Dale Schumacher from 15 Jun 89 [3] Fix for phys_copy by Dale Schumacher by Klamer Schutte from 16 Jun 89 All patches have been tested thoroughly within the last few days and should be free of bugs. Wish you a good performance increase and resulting more spare time to make own contributions. The cdiffs and additional files are shared, compressed and uuencoded (Sorry, but I'm on EBCDIC-Bitnet): Bitnet: V61@DHDURZ1 Ronald Lamprecht UUCP: ...!unido!DHDURZ1.bitnet!V61 Theoretische Physik ARPAnet: V61%DHDURZ1.BITNET@CUNYVM.CUNY.EDU (Heidelberg, West Germany) -------------------------------cut here-------------------------------------- table !"#$%&'()*+,-./0123456789:;<=>? @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ begin 644 fcopy_sh.Z M'YV-9<:@>0,"#X@6()J$65/&3!HV9;Z,(9/&C!D%<\J0 7'BA18V-G# &"T8!!,BBD&\J#-'S@LV:<2\)E-ZB)74JUN_COUBC)W2;'"S=@U;v M3-VYR(^#P,%"*0B]6 8_$:(D]1PZ;>#@<4'P>ILP:=QP!P%'SILQX^?DN5ZFu M37HZ<]"$(?/FSG@NI1,,ID,GS_@Q;)RWQGAMM/>&'/YU1P<9W[UG1H!PP)$@t M"-?=$5X:]^4WV'5CO"&>@FN(0<9[=I!1AX-NT/%>>>&I2!!^6(0 @GX@T!&&s M&!"]QR :?=>]^%-YZ@Z'6W'AWMO1??p M?/5E&"F-A1($H( $&HB@C@TJ^. ;$4Y8X86FEKDA'5B^%^*("I9XHJPIKBA'o MBZ;*B.J-.2JXXW^TYA':@&_X"*20;8Z!I))BZ6'?8K[PI=A)GKJn M8&?.D69_<[8YQYMQLA%OE'7>.::&C)&KHI^ .LDIH? ZF6^B+R@0T$ %'02"m M%&7,5R!&&LWT44@CT632PQ$S6 9,,G54$Q9"I3$'"":#$$:_209$1QH>@O#&l M2W"$P2L:99Q,AWQTD&>>'6ELM/(99;CQ)9+D?6G&@=^Y,<;'X8TA1\09R7P1k M%C^54"-!,]2@]=)RU(@S")FY(*G1*&=WH(TIDF>S0!0.-);0;,Q!D!@?VX%Dj MT"" +?;'4 QA4QAN;)0$"'>\,3<(9]21\QPLB&WSWR!,/<=8/:>\\AQV:E0'i M'%:#,,/6(-Q0@@LVV234QTED]W-[1<.G0^I8(%0$;&V$9S/,;H1>AQM&/SW'h M'&$@"$(;F*<]EV/%E'L=[G/4^IBW6Y P$2X'#f M&UKTI>,E;WEI:![O3F:B8[GA#/#;S]C@()^JW0%G4U-9DJ '*C!]#CXJ"R&.e M!%2Q'QPF?:.;V=_:T*_%X2V#*K/# LE0L1#@<'4.9,/+("C!YQ7O8V@(&@][d M%Q[*7<=FL$O1R43 J2_,APQR0(&V6B:'%(@ AQ^<7!GP4+/"Z8P@(C !I[;@c M!"E\@3)36,(45K!%B,BA"R)@3!A8$YXSJ(P\"TQ1 Y$G1.4Q;W<>PN'.)JS\"%I#&-!I!MPV,3^a M_8]"=1"#>2X9GCN!( E9 M5\0D<'.1!N#F;X4A]EY@8V #"3>N0B"-Q0AS;@z M+6R9Q.'4V #%C2B0@= D"#(Y]3CLR:$.8\A>"#NT.,)M)#Z*8\-&(EFSX6DDy M*2"+=&PI2 M$K#2$O*D2HF5 XGJ.0QDWBR OU2A&S v M80P;<\^B"2]T5#3/&*Q(!BQJD:-=S&,3I0<"% 1TH$EB0>[1#(,.@T;)LQD3!%D]#U>]RL/K>-&3F"&L! :S)$)O6(s M=[UVSNN"-4I#@9@8/G&^2)P\.5o MT@UK$%O*-,>X&6]DC&'(#D1<'#81 %$(,UY#'O^)SCOL#V69X^0C6:DV?!(.n MDP01U!E\24-7XC.)N&TB1#/B3#MB5J;Q!0%_XY;.C=B((3C45@C#V+,A8 ^*m M!9*BFNTGD!E7K8DO*U +I*9>[)*E9OBLD2R?TBJ T[%L%2@9FB$"4@#@;0XY5h MX +'^,T,]\PGP]GZ!16P -7;DC?$&8ML32[[9/U^>*936H9.6VH[)W.80 Q>g ML98K^VFD0WF25+[RLDZ\X!>T:4-<#O0FKJ$X.,"!"\Y(H?6@E2PQE[7$AT#Qf MY5'M8^XM,2DW^#.^;N1\DQ;( 1OIWI^J+$*PJ1A%ZDH'8\95YH/!&WX_)D.*e ML%C9/8,-VICI3&'BMMY7++2'D':^ZVCO)8 >VQO4&>Q9OLQHB@;!ZI"=VELBd MKLT^H]4;,@)/GE'.(7*X#C$_]D&"]!:".0LV=3^6=IQ1'<9V1N?B%*AZ9+[>c M/"S"Z?I:8I$YN$_FF__8$8_7^21VM6CYOFX12UMOB@!M7L]#P5%!X/>$O_SXb MF!ZK5CNN8$SB#(#+;R)$(AIH AX+Q4A#- KK36+NNR2"F%,D06JN6US;<@H$a M86-@M@;@0PI1W&T@Q 08R+AQ#NA,P,R( ;J%7U$E%VD(V PV#/6-8.D!3Z]y M\P5M !M3QST_-#8YB&#/LW>0!EAJI%5;X 9X=&>@4FHG4VYN!RYPL!*T(7/_x MY(- >#*7(TN*LF@R=P6F=SC^]3'T@39X,R^%]4=S !MG@ 8]TR$\E#:"t M8@>EAC(\V$MOH(+#M#)JIW%QV!"=!UVH='E^)%MI.'V9!XS05X<[DT*D%CNOs M" +K=S*-N 8UAC@/01;B*%T4U6[RJ#^)(P<;81 8]GD"28J\>%[ITSB/4U/"r M%2$-Q 17X$F7(T24:#3^4X'SZ'E4N(^@ E4(A)#HR$HO$3QOZ!^$@06,YFB@q MDH2>Y3:49C)"B'>#41POP(48DG,(@5QPHWN%&$E?>!WJ)5TN.3:AMGJ+572Sp MA@+%> (] S9$@V6>9(7)A4S%@3X;T2$2 F5;0W38EAO$$1O'5UXOL7n M-XQNX)0!!%%5Y69CPSD59B)P@$-^HU='8$GLV%C8P13H#)H.7HMN9K,i M*9WU-C5JX#+IV3O0*9WL)C_!Z8N8UFC!]&@P%6EH^3;T.36T26MOV6FG.2U3h MYS!MZ&0\ 50I AYLA5L<9)K1P@)#DI2S=J,!QQ[NX1@\.@(5P10@, *M-J7\g MMU:J%#J3MQ%V5J0"=9JT@VG":)D_!7WA]7J!M*$GBDP1)D0G$UM,!9,"89X&f M>#)8TDNQ04N)I(7:-#;E8R-D\:;D2"&=LQ%14XM_=C(9"4 89C9%X#_^)#?-e MB*)#=@?F 2J7QE@^8)OPR8(F@T,^0"N!E@:O26244R>&UB];N4(:1R&F>D %d MRGRJ=Z)-<"$&P92?^25KI1&B>J(AE3WQ9QX\T4>E1S@$J'G92 ;M8-C;Dc MI$X-RGV[&D[XV(Z"4JRXA4SM]V0I5#>b M97_B!1$C4F,D&IV6JGQSX)>;&C\G(8%VK.2NUDC1R #9- W1JP!I[.4M_"*T'8C1A,Z5V4R >LE&+B&D^z MX!#I0SB9B3AA $ R%$P:,4QD8#,K\T_(1+%;:2/YRGS\9!$1)%0I8DP^)'--y M0#7WM(T6B[&$TW1.HZBLIUY.HAY.@P;FP5:GRK/HUQC=1!\YTZ!RE1(.U)/Tx M94N6U#-K%JWG=*@/>7H0LI4A-8(O"JM"!J$RQT>'YU[ZB4T69U\*&T[.16%*w M5Y$H=&'U,8FA";&C&3>DXF265TL\R)JE>JICUA[C)0KN/VW$.(Z4O$8)66CA8>FMQDFLRU*4@8&>S&WLTR:0YB@,#HI,]t MVC?)E:="NHVQF;F5DYJ8M[OQ T20.:)2\W5U^Y)IF3)Q4 ?J!36N\SAI8%^$s M^U,8IK@J63O[63&S-4 2$2WG2TMHTT2DZIH5<[FRJ;GJ.QBXQ!Z[)$-Z6XZ/r MV4O@](P(_$11&#N I0)DUE\2]T3[n MBFG:ZR];@A!%D1EM_,9Q3*TL.U+.E1DM8,-+<&U,^FF8;,147$'#S&88J<[.MB5O;j MV[UEU8F;PXS.N%S)K$]!!G0\)+7VO$ZAVR]94@;UJW!CB050D,P9JW0*A$\Si M6:;V!2KLX9BB.WD8ELA3TC0E/,E*>=,XC6TL_90'DEP@&;.@BY1f MS5B^O,e MM#="Z1@9F)!O/6^6;1,08[AS,+RS1KV#L0(_F 8@L *JO)4K,"JY6]N^)@#,0,Y( )L,=W5?=V,1=TBc M,#K;7=W2/6O<[=W"]@,V 30ML+)U/1@R( (X$-[M;0/P+0(V -6,U=YLC6GMb M'0/AK=\B\!.UM@)=XP(Q4 (V =EW/1@Q -WR'3_5?0/V7=TV,&"8!M[D/1@Xa MX-[]S5@9[EH!+@,UX (SH#4HH +'-P:3C6DU( (T@ /QL^+.,1@K#@-+H>(Bz M ,;3@,L3A6TIN/4_>$TX (X8.!8T-F81@,G\-QY1-DWC>0QT-X!_A,$3N0Vy M4>(O%09FD(A]J<93P )-<*'LBDPX^5QNL%!/LQ&$63P ]*DQ4Y_''&\/=]Q.x MNVS0\[X71SH9+@,#MA@R('4YH#7/'$E.=3E3PZQ5#D3=NGR#WIAU^XFH2A7_w M-#H[G4QU\Q=BPQJ,L[_#% ,OL. AN,=ZPP:.0T%O<$!MAR3@NA['0[9OE1$Av M+00YPU[^BJ;_^ ZF2NNJ:W=DVD6:0:49_%"":Q<^Zu MQZ-L^%%3\X, E$3-R#T^$ ,ZD-\R!*Y)r M,F@!\JK=^CL7?3PRJTX-A/$H4#R*DSXPH!9^P:YA8!,^< 1#, 2#-B57.,,Wq MF@)GECF:54S'E!(G<_-/3B$!X2$#GS]W<+1"/!\ /24?\V'")(77ZB3=YW*9p M(TAR\+>BJ_ 3MEWAE:Q.RYU9R*X GA'H\0(@I7'- :M;7,6XFMQM< 8IL+FXo M5$)FWS<*'S\H/G)I@-1]K]3]T]1/;?A&O?=)3<:-[]0PX)4W'=8T)!\"N?:Tn MIOD/$P1- *_V&,#8QN,0%P,G,!71'=6JWQ1?1)/W?=/&TO^)$=^$0-P,Wm M( (R0-DS8 .QCVTU7G0SD.&HG],T$ /#/VRVCVVNY=Z470,ST/S"1N%%AQ8Ll M3MDTH..T X">QY 1]? ?*+A0,5IQ-$B++[()#] )) $C4 0RPR2K9+FD24B$[A,_,)EP;R8%W)$5Z(!BT.? AKMD')T@1BN">0BX*8#(T"R[X@P DH4:CC#$&+f M8 ?)PRT[8@"DC,FKR.%L6HT[:B;]:: I0<9BDW 2#L0V.K ,.!DVE[+BAQ0He M D' 1:!FX;)7A(/#& V@>(9@7!E"(V4A$ !5RY]S)T68Z$.V3"I6^2/;!"Gd M[Z!VP@;H@PIAX7P).^G"[?21JJ%8BT1B(1L2b M0YL@@0Z-C#H@P^A,^1-\5S%VQH%0',WHK\RGR/,QJ(GJL4_<)V:QKG,B-R@/a M%H@D)*7"A([OE(XJRYXA"D/#QS0MIK',ZAQ&^U"XKI#@EOK#.8Y(L6MVI,/(z MU8?>D:>.169),+1#"*@7HT$'B)H5L $Q @0@1]1!:2 %DA^3(ZQ2('%4XB8y M0) I#PN##MB$*N#&H !18Q=#(@3\CJ#Q!D+ 3B0"/?$GNH ;1!-#P$T$BL)Fx M=:26ES$'U([ZN&5IH/15!BA@7#X3Q?F*)> J9L7V)P22 !5P D6 "@"!(5 %w MG$ 6L )-P 7P1;_H HH 5J0UW:8,\!4V\$S.0.2X O@.!!P!7D4X\L!82A@+v M@R#8-1!@"?-21M@('>%BB 22L#$Z(\B8"2-#>:&QP] 9;(*,0 &DK UI!-95u MI2K":L0;J>CTI('4X\':0#7*.C$".0C'X4@- .)\DCV<6/#))($HVIo MIBZ47%D$@BR3RB+W1&(\6Dn MG;R39!*7_+6:&#]BQA/1.-QC.ZZ<-B #7 !9X%&[J)%LA ]YT]I ^SN4F&P7m M?8GE*&QFW@UX*>D-!20:+&@H,:7*$ .99VJ(D^C12^:)=I04?NV2$+6G9BCMl MH,=S$@>BY1$J2EEKR,"CM(,%*58VD%E9((4-&9 !+" ,8*(PP.,0@L/1CG$)R3[ !)C$Mj MN8\,(#K1\DY,2[,"!ZPE"["5M69;?B:$U%$<92QT,Y&QP(1+F+(\9HW1 ))6i M9 T2G;:#:7[ERI$14\IHG('=80)1(6T$A3DPR\ 9:)7QPU;:A'])h M!P+F&7B6F"9=8DQJV2ZOY>.C-?(2#162&90^4 "AO)?W3O7H2VPC!OHEIB$>g M=@#66,R!.5$*YN^#. BS)%E,ANDP!21 HQ?3B6+N2X4Y,KG/Y<,".%-G?LSXf MD2[=PZ$D S.@!9 !&L "6@"[/&V#04;@S/+W&3U)@&(/J2='0DMQ-S)MY;NDe M >B2;4Y+MXDUB]LF78#"%T@AA" &/_\$J..$%A)42N\N7P9)FLDZ"5O0@)WE4EMZSDKU. F2a M_@B=/. DA*#T1G0 !+ED+"=SWL@(P:DKPP:AZIQ/,R-TS(@),AF+\IPUOY+Hz MU,SL2>929ZUAFN528RY-B[D=YN;3;#7UDVJ*.ZLY&+ZFRQZy MCL444'J,Z*B![+"4A.5+\9*Y@PQ@SP0J+L]GQMR;LM-Q_CZ#(#A#)_PLG^.Rx M::9/;+,^C6?WW)Z@DW$R!?WI2V:-0>R8A,0FN,XS,$(+IZ@[G.\2A3*6OCDZw M&N?W7*$X='#:3;P9/('F\ 2<4910&E&B@#Z!Y0Q%F4ZS<[90*-I")>W!.*1DZU>3.I*/H$B[+3=#:0@&0!_Y&3:9\424G5FBN"u M/V? NY2D@)[0=^D\9XV,(*3L"@^T@%$D0W*E0-J5t M(7%Z#AM"6C"YI@#%-K\S;R9.]>E'9<8G=9^A%(:&C5+*2%4IW-R7D;1MFM)*s MJCTQ*=4J")LT!G52_0%(>27-[*$W\S.Y%7IA$YJ7WU$ F+'/- R$0,=4X!.Dr M&*#1(X"$T:@Q3D(U10.#\"7$!-2H'+ZI3X@!8> K$ 6Q0!9B %MPIVHA!^"\q MMD ,)^0W5:?!L)V^4WE: ]("B*.GQ\$X*@<;P"AN0%Z "XH"!!!4%G #;@"%p MA!0&4DNBC2G@;8C $[@"7R *8)L9D !>@ JH'+\C3HR7H^>X!8)Y6E8UV/:C)^V)DGH#Y @!QKC@/&k MQ7D&LW&A7N2\J8X@X#H>CNQ(*P=#=YP7'H<54K$\6$'-'8&DSR/_Y$%!<@#$2$IY8&,'PGR8P1%!ND;'>1@@Y#+<4)F2*X*5@ML@?60g MM#)$SH 162*O GCL&> 5%P:G=3@-]5-.DPPRLE>"@-\G8!BLB8Q+X"GQ8:#Tf M\<-N4QO(32(,'5ZG!B(-N1-CD2.T4" \)NT&6#7Ze M)Q'6$P:!#+L=EX)2H+$?=KAY)/LD5>22BI)=G$D]V62c M+)+L@DGN/#O[9MD%J-QC;79(5LDG$6=-7YE$*9MF,'P!)5M=[2RB=9%?H,DVb M6FSC AZMHGVRJPM)6EK."%Z]9)I=LZ 6H:+98?,2T"1Y8 S/D;8YAAR37$OKa MK &N9Y!T2-9z M=%?L^D_$E(9=.0H+B\@ '%L'0:86E6]509:CI.!/ 4EQ(FVEM,8VEQ)0^0%/"!T[)!CSEC0B5G^F>D$IP>2J?0*KDDT[Sx MD&++8%DLG\.,S+4(U^$#*AP504IKS*<,?9K8AG+&#XX+();N%XT?t ME[2K;-L-.\_;IRMYO\OK$K2\\NWSVZQ=9TN5VOs M.W.;B;*UN5"6=,!:GD-T\*[5]:)>5W,"UHVP% ($K: Z.9<;P2ZMRT#-I=:\r M 2\@#,B %A &;$#7?+?:EHKJMJM/'1E8)I6@WLWKL]M1$&W:F+,q MF0<#W*6MU)HB%XP&B%T"^A@NC[,!>& IL!OT.CD7Z/ EEL87]:I>UIM\X>X_p M8KY$A>'^3.@K?<\K]DJ[UO=J:M_5VWJ[I@R@ <07;(9-[&%8"<)!"[>#@?R^o M*.J+?G,3QCR^9,#UMH#W._-B@/Q=HO0WA/R3^SM^H^_^/;_#1O@>ROAK,F, n M\J6AW]= A5\L\GP=\/2%P,)& C=0[&LRUR_W3:%O /R*-?&;?SVP^35L$3C]m M6DX3W'X',/SUH)8T 2^<^]N!RR__C<'^]VI:X #L?F^P =:<#W,'LV"%ZH)_l M< B6P;RE^%9@K3EQ<_#RU< KF ,/!AK0A$%PK1'!O,4 7\OM&P:>'P*^PNZ0k MABQA+NR#O3"M <-DN 9T30$3?Y$P!5W SG<+=V$8_(2#L.45<7Z5BNPA;;Z(%V/&ZCH+E%e M&NT8U[ @>(Q%&M*S[:"7$JO)6X$R=NNMP[BW9D-4ZMMO:2J7H[^E _"Q1(_TN!X'L(6,[QQNRZTU$C>G]4WX%[TF9DXWII+72/O/X&N03F 1=HOP5RA;- UNP$4[0+A.\$\:3*'4+IAX%*:b MXAP /HWV6ET/4Qz M#ZC*W$4Y\PGUS);SEB;2&!I :P!HYCXVX#0;8[X[-^\"L63-&=,U(\_^NW7_y M<+!\I'FY;;IFT;R5=7.P-,VYN34'R]0\=2/&K %Q-B#^8DM66CY Q\Q"K,]Hx M].[+1_QJ"P=A7J[(.?A"80"L?2UP#:[#"!BZ0H5$7)C_[Q#NSMSW]>+@\$R5w MD\(,(,^TV3"?Y]3KG1?S>O:^XOD]MV%@O#;],/>9S\0R/7O-^SQ_\S-\WLKRv M&3$'Z#*LGHNO]A3/,N! "QN+,VMJP)-3SG,9 S?GT,$QT:TWP@*M9KDV8NJ\u M1IDL54;.K-02;SP1EHGIP"96/9UXQGSB^'Q]+R4Q5M"*68+BNV5\02&S[7W*t ME'E?3N/L&)-E\JD4-HXARZ@9;PP=P7&KOH4U9(>,%L\M.K[2!*1+5V1YW&X9"^S-E)NR(]M;q M4!F2\VVBQ-7_3)MCIK_QP<3(>Z+Z5>$OGo M&HH\=X,U%A@#:KI81UP\$'/I2ZF)4!7F32OT<-K^&JUn M7Y:=AQKH .6H:WPYI7m M@&6Q_';G+Y_.->MZ2F?:J?NNK6Z\ALW-LR6/7&IEEW,-7J;-U19:]]9SG8,!l MP^4R*[3XCHV1[69&TK:@Y"Z:+FWk M.O_*&D"*">Q!j M;W33)L/(&)PA[?T,M$DVT6Z_W0]J=^T1++29]C'6GD<[!B3M8+RT2[;3;K]1i M1VR79ZI=MMTVUD;;"&QK_^*Q;7JOMF)N#BY(;H]LLFU\[7:.-MIY6VUS[;EMh M.0UW>HX!-R Y8U^^38&'MM]FO1.WXB(P$) #UG9_GMJ-^VMS31D@ V" X+Z[g MQEF_\6RH37 5<>'XV?L2%4/BB.VS>7:*#@@KNFVTZ!>]E]*')X;8_WDL"\^-f M=[,;B-[1>IQ$$[IB4:HZ/1K^/%FZ.JV%; GA0O+'T<(VYF0KMN4/- .,P"$5e MGH:A13.?C="(*F]KT-[Q8P2XWH+]/,-WS>L9UF<7Y^S=6SC*-RJ-G"@'7=D.ZBG*;8[1MB_&E/?b M-#BPPE8X"R_42"-=*Y\8OJ\+.&_)R)!ZWDKJCTRIN^5(QM0F65-'[)7LJ1W&a M2RZ0H=H%4!R$(+OFY(1Z,7.*E:7JF;:JOS7[2,@(X255<04&I&P$!51&3PQIz MI"NOYERX[#<[3:92V"0!?D:$_AFLND4^*2O%VDZ"+@[$% (]YP&59,&X9'[Hy MI%2;:"4MHTVK2*++D$9E_$=)Y[OOYSK5'1A4LD,+QG2KOG-.Z6D]L!WZ6w M%_7),)0V 0YT\ 2^RD$N'H!2UMIC$]8$OJW#*S"7RN!5.\=EXU;#>\G2M=?:v MTS"@X5=^3<@>\*[EU^&H7',\*B.T>0K>"+P\>JRI21FUH2RT-*u M=,K @4#?MAF>#QLQ<#^WM_"VHMU\0#D0894DT \S,AIDP+Q-Y^HI7O/ 99:=t M*^";#W.6QK&SN?YTWO1[RD[L*06M>T9\I%(FP 0 $FQR,LR:S.@J7(+LSAI^s MKGK\^2H5G@&=9>^-\7A9"/E)3^@7/0[,FFZT!BBQ\,P??!W?G7"!N]U_S: _!5)SJR&XG.]KR.@*,Z)G[?O'LZ_>[1/HQOl M^]L^[3LZM1_V*K9 77L\C\UR_7D2[_#0\HXW]?A>4_U9XW5I?B>$NDI^Z&"4k MKH_2PH'/JY1:1_ Y6,&[:R9AX*L4YK76$CX6=_;XT6INJ2?^[NU\J+. HNZZj MM^:(WO !]\,+3_/.HM%[S.S=:$Y& V^ZF7EEYWX/1&'#OYLZY1W@CREV[Y]Fi M5$:+/,8NXDE\=Q_RL9M$1V)/#.$--NZ6ZBY^TL5H6D%TAC2V$=;1HJRWMJ :h MXJ.PNRSJ&)C+4K$O6SVN6%G1<[\X:K-M>EVYP?PM+BOJK-7$J=X1RF]1^Z!.g MO7E=VG8C?]^+/ D&WFW^SW?Y0"^&R;6?+_1"&CY@>0F!Y/L\>9CH.6/Q8C[/f M.G_%-UZQ,&]B![QX@A>#KG>>3]\"YGO?ZX^>+N. IN?-2!JNW]'\KCF)I_3NGF^KNTOGMV>7]3U;R-L d M6V_@";VU)/)>GJ@+>HS=[/?\M._-U5YH\WEL'S^T1/[;O]NG_VY=[=:WMT+[75_;QG]]S^T,/[=J_OO_U9-_?2'M^Gb M>X _X@5]JZGUX%W)TVYEO]3%@"C& Z08<=9X]K&\'$_G ?9H';WVN.E$\D&^F5^IJz M3-74$4T9QF:4'N$4FUJ,M4H:3P+0?X)QE28H!Z#_$]*I4" *1D&HN[BF@//.y M0HBT"O5T*T@/?,I.D\* 40I2(2K !F+]0-J<50.U(\%K(7G(&H-9,[/JHQEx MJU*!6=0='"$@*B$(!0?9<9TU-OC[ONF'$&5 B%4-VP@+1[?Y-N'F$\*3P,\(w ML6 *V %P?Z@&(JI*&2P#9J "&62KSO$_XK-R3 *S/]1YSX'@GH?BYGA_1,2u M0,K\P@%$%*%Y /Y\-?M9ZG8L_&3@\$L!<"-N(L>ND;O2G_I_0J-7(QC_\7#\t MD'_WM]2*,/D/0^6_JIC_X6C^"]C]?];G#_WO!?J4?J$B-%"_ 5']KW](.<;7s MGZID_^"GK3:+-#^2-$B*,25_@+M> [:/#B1\J( $D@ 20 -)@ D@ "8!&m MQ(^/"C]4@'MUK^[5O=I'VE\@H4 5$ ),( GD'M.T+5 R'$:I"5R]+;((0?@l M0.1885^@'3?^HW)\$&;; #E.@[3L@ 20 &HJV2@"3H (1(Y/. 2*0!*P D4@k M#E1V0 !) 3=TI!"%E9#9R=:K) NTOD"L[; RR#B:"j MGH,<@ ,[( '4U&]).E JQ28T":6-.Y?>-O@'0XN@$%?Z"[3C'9 $D!-7>! i MAQ:YH?TENHB*=$X- 05H9HVM. :OY9(7[( ="Q&*Z_5^EMCS^#3"FSf MAC;P1Q"!'0 ?X /4\DI_@79L$A9#&_D"4$ */($A<*%".H6Xe MTE^@';./Q1 $_.)+>2E[(#_( 3B0WN" U-P"GY *?(%/> 2DTV(H E] "(1"d MS" %AD#X+Q,B>=.$_PEY Q@%#G"HN/\FM,>;D!-R -=<< ,&.G#YN-_[-TCQc M:$\"??F\ <%/!.@?I6O^&61*H0"0XS1RC4B? D7 "1"!E[('EF,"Z!V+ ?I_b M%2X2.5;8%VC'C?^H//Z::LR<1N>< D7 "1 !5PH"A, 3H )(8/"7B2F% D".a MT\@U'WT*% $G0 1 !_@ X,H!"R&)[ $7HH<@ /I#0Y(S2WP":G %_B$1T Zz M+08W$/[+Q)1" ;TC!"R&)[ $7HI(WC0[H*:2C2+@!(@ X<-"@Y^Q]('(/QBFy M% H .4XCUXCT3S@$BD 2L +C!@3L@>68 'K'8G@U=>6K<)'(L<*^0#MN_$?Ex M\<^:K0IRG$;G_(1#H @D 2LP"IV+$'@"5 )#/XR(0?@0'J# U)S"WQ"*O %w M/N$1D$Z+P0V$?\;2!TI#'TBH.<"0+CA' 1@B_V"0^QND>+0G@;Y\WH#@)P+Tv MC](U_PPRI5 R'$:N4:D3X$BX 2(P$O9 \LQ ?2.Q0#]OPH7B1QR Y$CA7Vu M!=IQXS\JC[^F&C.GT3FG0!%P D3 E8( (? $J 2&/QE8DJA )#C-'+-1Y\"t M1< )$ $0P ?XP" * 8OA"2R!ER('X$!Z@P-2@#9I\X*H<%QQQ^@E^ #H/!H[I7]^I>W:M]=#CQ0P4Do M@ 20 !( ]$\ "2 !T(CX\5'AAPIPK^[5O;I7^TC["R04J )"@ DD 3O3(A ?n MJS'\O2URM./&?U2.#\+4W6!E!R2 !%!3=PI!2!F]!)%MJ[J/^GV9"Z']D:)%m MN/ =D 20$TE':V'AJ2,;X8WALE;H0,NA/9'BF^F MH %MP!"2 !U-213O92l MHAV/*7]G\ ^&/9!0%]P-: [7$/=K517@7MVK>W6O]M'AQ \5D 20 )( - _k M 22 !$ CXL='A1\JP+VZ5_?J7NTC[2^04* *" $FD 3L3(M ?*S&\/>VR'$%j MCTKD:,>-_Z@<'X2IN\'*#D@ ":"F[A2"D#)Z"2+;5G4?]?LR%T+["^3*#AN#i MK(.)H.=P!8_*#D@ -?5;D@Z42K$)34)IX\ZEMPW^P= B7/@.2 )H*:2CM9#h M0U+&-\,;P^2MT $70OLCQ3=3 6T "^Z !) :NI()WLIT8['E+\S^ ?#'C#[g MQ%$Y_ 3F\.0.*DZ 'Q\57%DS/S(YE)@K15C@RIJ!)^?BLD2.#H\f MEE3@!^T7_]MQ98>-0=;!1-!SN()')7*0,'R'!2.'"N"86##\,Q:@X=L\^%@3e M SL@ 22 FEK\:8N%>3P6:)V'#8@RUG;C%_#@@Y^Q W?AES'6H"(''8@B>R d M!%!3Q]IN).@>.EF)KPLQ$O=^W4\?M/_A W?AESE4='8 0F@ILJCHI$F8"7Mc MQ%E0[PSH)H6=K&A_[> +,8 Y/+FE@/OIR4<%5];,CTP.)>9*$1:XLF;@R;FXb M+)&CPR-'3_T8)Z"W)17X0?O%_W9!+NPU:'"[X,.!P8*(0XMx M2=0'R0S\W0Q!J(7UI@D$QL$P!*IZQ6,JS3^#3*FOHDQ\P&)@ A?U"U2!*5 $w MI (".EP/C*&@,5@H;X %) "3V (?($V @)"N@MG'XNA"32!+P %I, 3& )?v MH(VD "^8 $3RID$!1> +"(%0^ 6(0 FD H\_*Q@^L((%.^?U]RJ][?'7U)%.u M]H(.K^Y.6>P\D+E> D!H_\-IQUBPEDKX$K:]Z!G#@%F>LEt M (3V/YPII:M,V%%);W"@YUJ/DR'2H0!F8 )/@ I\ 2/P"<>-$=\T** (? $As M$ J_ !$H@53@\==4:K5+VO'@Y_(3\LFY7JA %7#_36B/-\$NY !#6KI#$'08r M#![5O;I7]^I>[:/#B1\J( $D@ 20 %Y-74D "2 !T(CX\5'AAPIPK^[5O;I7q M^TC["R04L Q68#*,PA:!^'3F&/@J7"1RK+#&?U2.#\+4W6!E!R2 !%!315 Wp MZ26(;%L1!&[YS6A_I&@1%.)*[X $D !JZDZ)(*#3CK_R;W5E*@8*<"Y!P"^^o M%-H?*;Z9"F@#6' ')( $4%-'.ME+B78\IOR=P3\8]D!"=:? LG2/3\!0&CRJn M>W6O[M6]VD>'$S]40 )( D@ ;R:NI( $D "H!'QXZ/"#Q7@7MVK>W6O]I'Vm M%T@H8!FLP&08A2T"\>G,,?!5N$CDN()')7*LL,9_5(X/PM3=8&4')( $4%-%l M4#?I)8AL6Q$$;OG-:'^!7-EA8Y!U,!'T'*[@4=D!":"F?DO2@5(I-J%)*&W*;Z8"j MV@ 6W $)( '4U)%.]E*B'8\I?V?P#X8]8/:)HW(HW7)8!C@'Z! _/NH0V!83i MBJAPJ\ /.1)"$X@<]:86+A\.UM*4%.T7_]MQ98>-0=;!1-!SN()')7*HL%;Sh M);!@Y% !2#%G'I7POQU;!/"_:.%?1EA7L((%.^?U]RJ][?'7U)%.]DHX6$%.g MG,*;=#!#1OO%/V.94BC@JW 1+)C>X$#/M1XG0Z1/@2+@!(A $G "1^"E?'0'f MO0>"S93Z*LI$)H2@D-[^UX"6GP-;8-" E2[P4O9 0BW=]!X+-E/HJRD0FA*"0WO[7@):? UM@T("5+O!2]H#9)X[*H?O)b M8;1 %:!#F0 0.:8%+(82UC'A0'J# SZHMVP!(E">"D0;:"FW+/R7B1(&1#NGa MR[3C)@X%:!'.]QQ20 D#HK6MK&B?0^C!4P" R#'C= @P@230&3 #$AB!5" %z M//X,T@=H)0DS830DA:TP'Q3^MV,) Z+IK0VX%5[],2K%E0[_,J(/+,>=7SNVy M0O/P9I \LQ\$:6HN 86RJ<>R[DMEH85:NM ]ZER:L#8QX(^;!7TH\FQDNv M\Y^E@,>_'*U4W3#D@X&$F3 :DL)6F \*_S)BYVO8$@9$TUL;<"N\^F-4BBL=u M_F5$'UB..[_G:T:'<7.F3M OFVH58Q1][)$=<$XX3?HC RY@C^R <\ZITP<+t M41_#,06GT$_/@I$M^[5C*]R[P)=@$'<,T8s MX&"APX2, 3B :[K3\0D8\H,:: T)8&PF@ 20 /HF)OK/TVDNMYW(7A'H?WX0r MF<&!)7O-:ID $H",: -8,#]0S@3@-KUFM4P "4!L7NDOT(Z%:@(0F<&AI6@5q M+/AZJV4"2 R8OLT6[""G#@%Q28T"<6YY ?*F0 D DOA%*^W6B: !%!QP4I^p MH)P) -=[S6J9 !* C)@"1< )$($7\ F'0!%( E:@"+P (? $J 2R ]JH#4Do M +=BFK9% D@ 8M,T;0L4 '*W6O[M4^TOZ;T!YO0DZ0 3& .9#N$ 0=j M)J0,<*@#N=]Y78VD^O9[OF9T&(!PX 3]LJE6,4;1KSI.i M2A B_7$J5V]"3@"$ ^><.GVP$/4Q'%-P"OWT+'S]LE\[M@(@'#AH( 4> )#0(>B@!:1 C0EZ"BWg M6L53MI$W@ACD2/@O$PG /4R!)' $OL #A *=4P[ @?0&!UK*E;H@X;],) #Qf MV%*TRAS0"N%?622 *>4R5\VSR?Z)OPA$CB;0!+X %) "3V (?($V$CE,0!MXe M*2%@,3R!)?!!3T4"2 !E)(*@ !'02!13>,!8":$)B(#(X02>P!=P E6@"3S^d M_) Y <2&]PH*5<*=RR&&# #JBIE(,WC9"40?564Q-H LB)G."3P5\F$H 4 a M'$AO<*"E7*D+$O[+1 (0CRU%J\P!K1#^E44"F%(N<]4\F^R?^(N ]=)OIG_C'3>1H DW@"T !*? $AL 7:".1PP2T@9<2 A;#$U@"'_14)( $4$8By M" H0 8U$,84'C)40FH (B!Q.X E\ 2=0!9K X\\/"4 .P('T!@=:RI7"+8L!x M!NR FDHY>-,(21E4;S4U@2: G,@)/AG\92(!R $XD-[@0,^U'B?#!"P&/P %w MBH 3( ))P D<@?!/6V]"3K !,N NV T#QTFI V0 7?!!L@ AXK[(T4?B/R#v MU?Q;U45O:H[M6]NE?W:A\=3OQ0 0D@ 22 ! !>$T "2 T(GY\5/BAu M MRK>W6O[M4^TOZ;T!YO0DYH1(Q"OAU4G)!0Y1LFDF\#!O<_'.0R9%" 5N@=t M/F Q%-_\H )0@!;A?,\A!9 !:VD84A@*T"*<[SFD ..S E9 ^!\,?>"IZGXNs J/Q@\JGMUK^[5O=I'AQ,_5$ "2 )( '$E(<@0L5C D@ "8!&Q(^/.E8!r q end -------------------------------------end-------------------------------------