[Copy of orginal message found on the Develop archives: Resource management]
Date: Fri, 6 Oct 2000 08:22:59 -0700Reply-To: dotnet discussion <DOTNET@DISCUSS.DEVELOP.COM>Sender: dotnet discussion <DOTNET@DISCUSS.DEVELOP.COM>From: Brian Harry <bharry@MICROSOFT.COM>Subject: Resource management
I know people have been waiting a long time for someone at Microsoft to saysomething about the issue of resource management and deterministicfinalization. Because this is such a sensitive topic I am going to try tobe as precise and complete in my explanation as I can. I apologize for thelength of the mail. The first 90% of this mail is trying to convince youthat the problem really is hard. In that last part, I'll talk about thingswe are trying to do but you need the first part to understand why we arelooking at these options. Don't get too depressed reading the first partit's all background.History---------First, let me start with some history. A few years ago when this projectfirst started we had a massive debate on this very issue. Some of the earlycontributors to this project came from the COM and VB teams, along with manyother teams across the company. One of the big problems we set out to solvewas to eliminate the issues with ref counting, including cycles and errorsdue to misuse. There were all kinds of anecdotes running around at the timeabout how team X used the last N months of their product cycle chasing downref counting bugs. I suspect some of it was overblown, but nonetheless webelieve that ref counting errors are reasonably common and providing auniform solution is a valuable addition.We initially started with the assumption that the solution would take theform of automatic ref counting (so the programmer couldn't forget) plus someother stuff to detect and handle cycles automatically. We looked at addingconservative collection or tracing to the mix, GC algorithms that couldcollect a single object without doing an entire graph trace, etc. For avariety of reasons (more on this below), we ultimately concluded that thiswas not going to work in the general case. In those days the primary focusof our effort was in maintaining VB compatibility. The solution had to becomplete and transparent with no semantic changes for VB. We eventuallyended up with a context-based model where there was a "deterministiccontext" and everything in that context used ref counting on top of GC andanything outside just used GC. This avoided some of the type bifurcationissues described below but didn't yield a particularly good story forfine-grained mixing of code between languages. Ultimately, as you all know,we decided to make a series of changes to the VB language to modernize itand make it more capable. As part of this decision we decided to drop theVB lifetime compatibility requirements. This decision also generallyended investigation into deterministic lifetime issues. In retrospect, Ithink we tied that issue a little too closely to VB semantics and shouldhave instead turned the discussion to the broader issue of resourcemanagement. We are looking at it that way now, if belatedly.In the beginning, I was one of the most vocal advocates of ref counting. Wecame up with a million arguments for why deterministic finalization wascritical. However, during this time we have watched (and helped) hundredsof thousands of lines of code be written without deterministic finalization.Now I am convinced that substantial programs can be reasonably written anddebugged without the system providing any automatic support. That said, Ifully agree that it would be better if the system/language providedadditional support. Without it, you must build the behavior into thecontract of the objects (like calling the Dispose method).on to a technical analysis of the issues...Deterministic Finalization------------------------------------The first thing I want to do is define what it is we are talking about andgive some examples. Without going into details of the implementation, whatwe refer to as "deterministic finalization" means: immediately after anobject is determined to be no longer used by the program, its terminationcode executes and releases the references it holds on other objectscascading the termination in a predictable and repeatable order through thegraph of objects. Ideally, this would work for both shared and single useobjects. The term "immediately" here is open to some interpretation. It'sactually not a promise about time at all (for example a context switch canhappen and an arbitrary amount of time can pass). It really means that thethread that discovers a reference is no longer used executes the terminationcode before it does anything else. There are a variety of cases where wecare about either timeliness or order of the execution of the terminationcode of related objects. The most common case is where the objectrepresents a physical resource (like memory or files or ...). But itgenerally applies in any case where there is contention. Some examples:Memory - freeing the memory for an object graph quickly returns the memoryto the pool for use.Window handles - the footprint of the window object in the GC does notreflect the actual cost - there is some footprint inside the OS to representthe window and there may even be a limit (other than available memory) onthe total number of window handles that can be allocated.Database connections - concurrent database connections are frequentlylicensed and therefore there may be a small (like you could actually countthat high in a few seconds) number of them available. It is important thatthese be returned to a pool promptly so they can be reused.Files - since there is a single instance of a given file and exclusiveaccess is required for many operations (deleting, writing, etc) it isimportant that file handles get closed very aggressively. The canonicalexample goes something like this (in pseudo code): File f = new File("c:\foo.txt"); byte[] b = f.Read(); File.Delete("c:\foo.txt");Window subclassing - On tear down, it's reasonable to unsub-class windowfunctions in Windows. It's important that this happen after all of themessages have been sent to the window and therefore after any othertermination code that sends messages to the window.I could go on and on, but you get the point.Ref counting collection--------------------------------Ref counting does a reasonable job providing deterministic finalization inmany cases. It is worth noting that there are quite a few where it doesnot. The most common cited example is cycles. In fact, straightforwardreference counting never collects objects that participate in cycles at all.There are techniques to manage this and we have all learned them painfullybut it is a very undesirable characteristic and a huge source of bugs. Inaddition, if you start remoting objects across apartments, you can getapartment reentrancy and thus introduce a great deal of "nondeterminism" inyour program. Some would argue that as soon as you hand a reference to anobject outside the immediate control of a tightly coupled program you havelost your deterministic finalization because you have no idea when or ifthat "foreign" code will release the reference. There are others whobelieve building a complex system that is very dependent on the order oftermination of a complex graph of objects is an inherently brittle designand is likely to create a significant maintenance problem as the codeevolves. I understand most of this is of little comfort because areasonably constrained program written by a reasonably competent programmerdoes get the benefits of deterministic finalization.Tracing collection------------------------A tracing collector definitely makes some weaker promises than ref countingdoes. It is a somewhat more "lazy" system with respect to executingtermination code. Objects have "Finalizer" methods that are executed whenthe object is no longer reachable by the program. Tracing has the advantagethat cycles are not an issue. It also has the huge advantage that assigninga reference is a very simple move operation (more on this in a minute). Theprice that you pay for this is that there is no promise about terminationcode running "immediately" after a reference is no longer used. However, Ithink there is a bunch of confusion about what IS promised (caused largelyby our docs and due to some bugs in the pre-release causing finalizers notto be called on shutdown :)). The truth is that for "well behaved"programs, the finalizers will be called for objects. An ill-behaved programis one that crashes or puts the finalizer thread in an infinite loop, etc.Our docs are overly cautious about promises in this respect. If you have anobject with a finalizer, the system will call it. This doesn't address theissue of deterministic finalization, but is important to understand thatresources will get collected and finalizers are a very valuable way ofpreventing resource leaks in a program.Performance-----------------Before I get into an explanation of the reasoning, I want to coverperformance. I have seen quite a lot of doubt about whether performance isrelevant. I strongly believe it is. I believe that we must have some kindof tracing collector to handle cycles, which necessitates a big part of thecost of a tracing collector. I also believe that code execution performancecan be substantially affected by the cost of reference counting. The goodnews is that in the context of all objects allocated in a running program,the number of those objects that really need deterministic finalization issmall. However, below I'll talk about why it is hard to isolate the cost tojust those objects. Let's look at some pseudo code for a simple referenceassignment when using a tracing collector vs. ref counting:tracing:a = b;that's it. The compiler turns that into a single move instruction and mighteven optimize the whole thing away in some circumstances.ref counting:if (a != null) if (InterlockedDecrement(ref a.m_ref) == 0) a.FinalRelease();if (b != null) InterlockedIncrement(ref b.m_ref);a = b;This code is huge. The bloat is very high - bigger working set and theexecution performance is obscenely higher, especially given the twointerlocked instructions. You can limit the code bloat by putting all ofthis stuff in a "helper" and further increasing the length of the code path.In addition code generation will ultimately suffer when you put in all ofthe necessary try blocks because the optimizer has its hands somewhat tiedin the presence of exception handling code - this is true even in unmanagedC++. It's also worth noting that every object is 4 bytes bigger due to theextra ref count field, again increasing memory usage and working set.Here are a few programs I have written that demonstrate the cost of this.This particular benchmark loops, allocating objects, doing two assignmentsand exiting the scope of one reference. As with any benchmark you can makea million arguments about whether it is valid or not. I'm sure someone willargue that in the context of this routine, most of the ref counting can beoptimized away. That's probably true, however this is intended to simplydemonstrate the effect. In a real program, those kinds of optimizations areactually very hard if not impossible to do. In fact in C++, what happens isprogrammers make those optimizations manually and that leads to lots of refcounting bugs. I would argue that in a real program the ratio of assignmentto allocation is much higher than this. <<ref_gc.cs>> - the version which relies on the tracing GC. <<ref_rm.cs>> - a ref counted version that uses interlocked operations forthread safety. Note there is only one thread and therefore no buscontention making this the "ideal" case. I'm sure with some tuning we couldmake it perform a bit better, but not a ton.It is worth noting that VB has not historically had to worry about usinginterlocked operations for its ref counting (although VC has). This isbecause VB components ran in a single threaded apartment and where"relatively" guaranteed that only a single thread would be executing in themat one time. One of the goals we had for this version was to open up VB formulti-threaded programming and to get rid of the complexity of COM'sexisting 7 or 8 threading models (I can enumerate them if you insist). So Ibelieve the multi-threaded comparison is the correct one. However, just incase there are nay-sayers, I have included the version that doesn't use lockprefixes. It is not nearly as slow as the multi-threaded version, but it isstill quite a bit slower than the GC version. <<ref_rs.cs>> - a ref counted version that assumes it is running in asingle threaded environment.Here are the numbers I got on my dual proc PIII-600:ref_gc: 531msref_rm: 3563msref_rs: 844msThe perfect solution---------------------------I think everyone agrees that the perfect solution is that every object inthe system is cheap to allocate, use and reclaim and at the same time goesaway in a deterministic, orderly fashion the instant the programmer believeshe/she is no longer using it regardless of whether there are cycles oranything else. The only way I am aware of to accomplish this is to combinesomething like a tracing GC with something like ref counting. I believe thedata shows that ref counting is too expensive to be used in a generalpurpose way for all of the objects in programming environment. The codepaths are longer, and the code and data working set are larger. If you thencombine this already high price with the additional cost of implementing atracing collector to reclaim cycles, you are spending an exorbitant pricefor memory management.We researched various techniques to improve the performance of ref counting.There have been reasonably high performance systems built on ref countingbefore. We surveyed the literature, but these systems accomplished theimproved performance by giving up some of their determinism. I don'tremember the details because it was years ago and I didn't do the researchmyself.It is worth noting that C++ programs don't do this. Most high performanceC++ programs that use COM actually use C++ classes internally where theprogrammer is required to explicitly manage the memory. C++ programmersgenerally only use COM on the boundaries where the API is exposed toclients. This is a key characteristic that allows the programs to performwell.I'm sure my argument here is not going to sway everyone in the world. All Ican say is we did a bunch of benchmarking and profiling and ultimatelyconcluded that the performance was unacceptable and therefore the solutionunattainable.The next best thing---------------------------OK, so you can't have it all, but what about if you could just havedeterministic finalization on those objects that you need it on? This iswhere we spent most of our time thinking. Again you have to remember thatmost of this was in the context of exactly duplicating VB6 semantics withour new system. Most of the analysis still applies but some ideas that wediscarded a long time ago now look more palatable as a resource managementtechnique rather than transparent VB6 lifetime semantics.Our first hope was that there would be some way to simply mark a class asrequiring "deterministic finalization" either with an attribute or byinheriting from a "special" class. This would cause the object to bereference counted. We investigated tons of different designs that includedboth subclasses of System.Object and changing the root of the classhierarchy to some other class that served to bind the ref counting world tothe non-ref counting world. There are two basic issues that we couldn'twork around:(1) composition - Any time you take an object that requires deterministicfinalization and store it in an object that does not, you have lost thedeterminism transitively. The problem is that this strikes at the heart ofthe class hierarchy. For example, what about arrays? If you want to havearrays of deterministic objects, then arrays had better be deterministic.What about collections, hash tables, .... The list goes on and on andbefore you know it, the entire class library is ref counted. The otheralternative is to bifurcate the class library - have deterministic arraysand non-deterministic arrays, etc. We thought about this, but soonconcluded you'd have two copies of the whole framework and that would beconfusing, perform horribly (you'd have 2 copies of every class loaded) andin the end wouldn't be practical. We could think of specific solutions tospecific classes (like I think, although I can't remember, that we came upwith some way to make arrays work, but the solution just didn't scale to thewhole framework). The fundamental problem is that if a non-deterministicobject contains a reference to a deterministic one, the system is notdeterministic. We also considered outlawing it; simply having that be anerror. Again we felt that you couldn't write real programs under thoseconstraints.(2) casting - A somewhat related issue is what about casting? Can I cast adeterministic object to System.Object? If so is it ref counted then? Ifthe answer is "yes" than everything is ref counted. If the answer is "no"then the object loses determinism. If the answer is "it is an error" thenit violates the fundamental premise that System.Object is the root of theobject hierarchy. And what about interfaces? If a deterministic objectimplements interfaces, is the reference typed as an interface ref counted?If the answer is "yes" then you ref count all objects that implementinterfaces (note System.Int32 implements interfaces). If the answer is "no"then you lose determinism. If the answer is "it is an error" thendeterministic objects can't implement interfaces. If the answer is "itdepends on whether the interface is marked deterministic or not" then youhave a bifurcation problem of a different sort. Interfaces aren't supposedto dictate object lifetime semantics. What if some guy implemented an APIthat takes an ICollection interface (pick your favorite) and your objectthat implements it needs determinism but the interface wasn't defined thatway? You are screwed. This leads to further bifurcation. Everybodydefines 2 interfaces, one deterministic and one not and implements everymethod twice. Believe it or not, we actually looked at what it would taketo do this automatically and to automatically generate the versions of themethods (deterministic or not) as they were used. That whole line ofthought deteriorated into immense complexity.All of this led us to conclude that a really automatic and simple typemodifier couldn't be done. That said, there are some ideas below on how wecould relax some constraints and get something that might be helpful.If not types what about references?-------------------------------------------------OK, so if we can't mark types and have it all just work, how about if I canmark a variable as ref counting the referenced object? We spent a littlewhile talking about this and it has some nice characteristics. Howeverthere are two things that cause problems.(1) construction - Since the ref counted property is associated with thereference rather than the type it means ref counting isn't initiated atconstruction. So here's a really ugly scenario. You create an object andstore it in an un-ref counted variable because you know it doesn't need refcounting. You pass it to someone else who isn't sure so he puts it in a refcounting variable which increments the ref count to 1. When his variablegoes out of scope, the count is reduced to 0 and the object is terminated.The caller never knows what hit him. This is so fragile it'll never work.(2) composition - Same kind of composition problems as above. How do youhave a ref counted member variable in a class when you have no idea whetherits users will ref count or not.What about deterministic finalization and value types (structs)?------------------------------------------------------------------------------------------I've seen a lot of questions about structs having destructors, etc. This isworth comment. There are a variety of issues for why some languages don'thave them.(1) composition - They don't give you deterministic lifetime in the generalcase for the same kinds of composition reasons described above. Anynon-deterministic class containing one would not call the destructor untilit was finalized by the GC anyway.(2) copy constructors - The one place where it would really be nice is instack allocated locals. They would be scoped to the method and all would begreat. Unfortunately, in order to get this to really work, you also have toadd copy constructors and call them every time an instance is copied. Thisis one of the ugliest and most complex things about C++. You end up gettingcode executing all over the place where you don't expect it. It causesbunches of language problems. Some language designers have chosen to stayaway from this.Let's say we created structs with destructors but added a bunch ofrestrictions to make their behavior sensible in the face of the issuesabove. The restrictions would be something like:(1) You can only declare them as local variables.(2) You can only pass them by-ref(3) You can't assign them, you can only access fields and call methods onthem.(4) You can't box them.(5) Problems using them through Reflection (late binding) because thatusually involves boxing.maybe more, but that's a good start.What use would these things be? Would you actually create a file or adatabase connection class that can ONLY be used as a local variable? Idon't believe anybody really would. What you would do instead is create ageneral purpose connection and then create an auto destructed wrapper foruse as a scoped local variable. The caller would then pick what they wantedto use. Note the caller made a decision and it is not entirely encapsulatedin the object itself. Given that you could use something like thesuggestions coming up in a couple of sections.So what then? (Summary of problem statement)--------------------------------------------------------------------I hope the above analysis convinces you that we have thought long and hardabout this and have not been able to find a "magic bullet". I wish therewere one, but I can't find it and I've watched dozens of other people wracktheir brains. What I have listed here is, by no means, an exhaustive listof everything we have considered, but should give you an idea of how hard wehave tried.In summary:* We feel that it is very important to solve the cycle problem withoutforcing programmers to understand, track down and design around thesecomplex data structure problems.* We want to make sure we have a high performance (both speed andworking set) system and our analysis shows that using reference counting forevery single object in the system will not allow us to achieve this goal.* For a variety of reasons, including composition and casting issues,there is no simple transparent solution to having just those objects thatneed it be ref counted.* We chose not to select a solution that provides deterministicfinalization for a single language/context because it inhibits interop withother languages and causes bifurcation of class libraries by creatinglanguage specific versions.The picture can't possibly be that bleak. Clearly you can implementref-counting yourself. Just add a member variable and a few methods andcall AddRef and Release where you need to. Further, messing up ref countingisn't quite as bad as it used to be because you won't leak forever(forgetting to release) and you won't crash (forgetting to add ref). The GCwill be a back stop and make the object go away when it isn't used anylonger and keep the object alive (at least the memory) as long as you haveany references whether or not you have a ref count on it.Breaking down the problem and suggesting some solutions-------------------------------------------------------------------------------------There are really several different scenarios here.First, let me go back to the statement of determinism. There is adistinction between objects that really need deterministic finalization andthose for which the GC simply can't accurately cost the footprint. Thingslike window handles, bitmaps, etc are actually fairly plentiful and the GCcould do a darn fine job collecting them if it just knew what the actualcost to the OS was instead of the portion of the footprint that lives in theGC heap. In fact, internally in WinForms we have a class calledHandleCollector which keeps track of all of the Windows handles of this typethat we use and uses some metrics to decide when it is appropriate toprovoke a GC (using the GC class) to cause collection to happen and clean upall of the unused handles. You can also simply provoke a GC in yourresource acquisition logic whenever you fail to acquire the resources youneed in the hope that the GC will free them up. In the last month, we havediscussed formalizing some GC extensibility (in fact the handle collector isnot tied into the GC in any way - you could write the same thing yourself)that would allow objects/classes to describe their cost model so that the GCcan better decide when it is appropriate to do a collection. This does notsolve the general problem of determinism because any time the number ofallowed instances is small, the GC is WAY too big of a sledge hammer totackle the problem. Doing a GC every time you recycled a databaseconnection would destroy your performance. Our current thinking is not toadd this to the product at this time because people can do it themselvespretty easily. Designing an extensible and efficient cost model will betricky and we can't really add much value over what you can do yourself.For those objects that really do need precisely timed termination logic,let's look at what you can do yourself and then let's ask ourselves what thesystem/languages can do to make it easier, less prone to error, less typing,etc.First, we are discussing formalizing a design pattern that we already usewithin the framework. All of our resource classes already support a methodcalled Dispose(). It is a public method that you can call at any time thatwill release the resources contained by the object. Think of it kind oflike the c++ delete operator but it doesn't actually free the memory, itjust runs the destructor. We are considering making this an interface tohelp codify the design pattern and facilitate some language support asdescribed below. The interface would look like:public interface IDisposable{ void Dispose();}There are a couple of interesting scenarios: scoped locals and unscopedmembers. I discuss both below:scoped locals - It is a fairly common pattern to have local variables thatallocate a resource, use it and release it before the routine returns. Thecode you would write is below (note this too is pseudo code). Notice thatyou must manually call the Dispose method and you must put it in a finallyto make it resilient to exceptions.foo(){ File f = new File("c:\tmp"); try { byte[] b = f.Read(); } finally { f.Dispose(); }}It gets uglier if you have more than one resource:foo(){ File f = new File("c:\tmp"); try { File f2 = new File("c:\tmp2"); try { byte[] b = f.Read(); } finally { f2.Dispose(); } } finally { f.Dispose(); }}or a little better:foo(){ File f2; File f = new File("c:\tmp");; try { f2 = new File("c:\tmp2"); byte[] b = f.Read(); } finally { f.Dispose(); if (f2 != null) f2.Dispose(); }}PROPOSAL - There is a proposal here to add syntax that would make this looklike the following. The compiler would effectively expand this code to tothe code above. Please understand that by telling you we are consideringthis, I am making no commitment and this is not a spec. The IDisposableinterface I mentioned earlier would help the compiler disambiguate themethod to call at the end of the scope. I would like to get your feedbackon the idea however. Obviously there are subtle variations on the syntaxyou can invent. We believe that this makes the very common design patternquite a bit more palatable.foo(){ using (File f = new File("c:\tmp")) { byte[] b = f.Read(); }}unscoped members - The scoped local variable problem is a very constrainedproblem. It does not begin to address unscoped lifetimes (like membervariables of heap allocated classes). The case above also only coverssingle ownership and not shared ownership where no one piece of code knowsdefinitively when an object is no longer used. Only something like refcounting can be used to solve this problem. Let's look at the code youmight write:public interface IRefCounted : IDisposable{ void AddRef();}// ref counted base class.class RefCountable : IRefCountable{ private m_ref; public RefCountable() { m_ref = 1; } public void AddRef() { Interlocked.Increment(ref m_ref); } public void Dispose() { if (Interlocked.Decrement(ref m_ref) == 0) OnFinalDispose(); } protected virtual void OnFinalDispose() { }}// an example ref counted class containing resources.class Foo : RefCountable{ UnmanagedResource m_resource; RefCountable m_refCounted; Foo() { m_resource = ...; // acquire an unmanaged resource m_refCounted = ...; // allocate another ref counted object. } // replace the currently contained ref counted object. public void Bar(RefCountable r) { if (m_refCounted != null) m_refCounted.Dispose(); if (r != null) r.AddRef(); m_refCounted = r; } // clean up. public void OnFinalDispose() { if (m_refCounted != null) m_refCounted.Release(); ...(m_resource); }}In theory, the compiler could hide some of the goo for you, giving you:// an example ref counted class containing resources with compiler help.class Foo : RefCountable{ UnmanagedResource m_resource; RefCountable m_refCounted; Foo() { m_resource = ...; // acquire an unmanaged resource m_refCounted = ...; // allocate another ref counted object. } // replace the currently contained ref counted object. public void Bar(RefCountable r) { m_refCounted = r; } // clean up. public void OnFinalDispose() { ...(m_resource); }}You get the basic idea. I could do some complicated shared lifetimeexample, but I won't. It is worth noting that what I have done here worksfine with the "using" construct described above so you can use ref countedobjects in both single use and unscoped lifetime patterns. This seems likea reasonable solution but it fails many of the transparency tests in theproblem statement above. If you cast Foo to Object, you no longer have anyclue that it needs to be ref counted. If you cast it to an interface, againyou would not know the underlying object needed to be ref counted.Although, you could chose to define some interfaces to be inherited fromIRefCountable and they could presumable be ref counted. The question I haveis what if we put some syntax around this with those limitations and atleast made it look good? Would that be good, or are those limitationsdamning?SUMMARY/CONCLUSION--------------------------------------In summary, we could consider adding(1) Extensions to the HandleCollector class for general use(2) Language syntax like the "using" construct for scoped locals(3) Syntax to formalize use of the RefCounted classBut none of these are substantially different or better than what developerscan do themselves. They simply make the code smaller, cleaner andpotentially less error prone (although that might be open to debate). Thefeedback we get from developers who are using the .NET Framework every day,including internal developers who have been coding on it for the last coupleof years, is that a tracing GC with finalization is an effective solution tothe vast majority of their needs.To be clear we are very strongly leaning against doing anything for #3. Weare not inclined to do #1 because it is so easy for you to do it yourself.We are closest to the border on #2 but I'd say we are slightly disinclinedto do it, again because it is really not more than syntactic sugar and weare prioritizing shipping very high.We generally hear requests for deterministic finalization from developerswho are new to the platform and who are looking for mechanisms that mirrorwhat they have used in the past. I hope you feel we have done an adequatejob laying out the thinking and we'd love to hear your feedback.There it is. It's long and you probably will disagree with lots of it, butit's a reasonable analysis (I think). I'll probably spend the next 2 weeksof my life clarifying each of the points :)Brianbegin 600 ref_gc.csM=7-I;F<@4WES=&5M.PT*#0IP=6)L:6,@8VQA<W,@1F]O#0I[#0H)<')I=F%TM92!S=&%T:6,@1F]O(&U?9CL-"@EP<FEV871E(&EN="!M7VUE;6)E<CL-"@T*M"7!U8FQI8R!S=&%T:6,@=F]I9"!-86EN*%-T<FEN9UM=(&%R9W,I#0H)>PT*M"0EI;G0@=&EC:W,@/2!%;G9I<F]N;65N="Y4:6-K0V]U;G0[#0H-"@D)1F]OM(&8R(#T@;G5L;#L-"@T*"0EF;W(@*&EN="!I/3`[(&D@/"`Q,#`P,#`P,#L@M*RMI*0T*"0E[#0H)"0E&;V\@9B`](&YE=R!&;V\H*3L-"@T*"0D)+R\@07-SM:6=N('-T871I8R!T;R!F,BX-"@D)"68R(#T@;5]F.PT*#0H)"0DO+R!!<W-IM9VX@9B!T;R!T:&4@<W1A=&EC+@T*"0D);5]F(#T@9CL-"@T*"0D)+R\@9B!GM;V5S(&]U="!O9B!S8V]P92X-"@D)?0T*#0H)"2\O($%S<VEG;B!F,B!T;R!SM=&%T:6,-"@D);5]F(#T@9C([#0H-"@D)+R\@9C(@9V]E<R!O=70@;V8@<V-OM<&4N#0H-"@D)=&EC:W,@/2!%;G9I<F]N;65N="Y4:6-K0V]U;G0@+2!T:6-KM<SL-"@D)0V]N<V]L92Y7<FET94QI;F4H(E1I8VMS(#T@>S!](BP@=&EC:W,IA.PT*"7T-"@T*"7!U8FQI8R!&;V\H*0T*"7L-"@E]#0I]`endbegin 600 ref_rm.csM=7-I;F<@4WES=&5M.PT*=7-I;F<@4WES=&5M+E1H<F5A9&EN9SL-"@T*<'5BM;&EC(&-L87-S($9O;PT*>PT*"7!R:79A=&4@<W1A=&EC($9O;R!M7V8[#0H)M<')I=F%T92!I;G0@;5]M96UB97([#0H)<')I=F%T92!I;G0@;5]R968[#0H-M"@EP=6)L:6,@<W1A=&EC('9O:60@36%I;BA3=')I;F=;72!A<F=S*0T*"7L-M"@D):6YT('1I8VMS(#T@16YV:7)O;FUE;G0N5&EC:T-O=6YT.PT*#0H)"49OM;R!F,B`](&YU;&P[#0H-"@D)9F]R("AI;G0@:3TP.R!I(#P@,3`P,#`P,#`[M("LK:2D-"@D)>PT*"0D)1F]O(&8@/2!N97<@1F]O*"D[#0H-"@D)"2\O($%SM<VEG;B!S=&%T:6,@=&\@9C(N#0H)"0EI9B`H9C(@(3T@;G5L;"D-"@D)"7L-M"@D)"0EI9B`H26YT97)L;V-K960N1&5C<F5M96YT*')E9B!F,BYM7W)E9BD@M/3T@,"D-"@D)"0D)9C(N1&ES<&]S92@I.PT*"0D)?0T*"0D):68@*&U?9B`AM/2!N=6QL*0T*"0D)"4EN=&5R;&]C:V5D+DEN8W)E;65N="AR968@;5]F+FU?M<F5F*3L-"@D)"68R(#T@;5]F.PT*#0H)"0DO+R!!<W-I9VX@9B!T;R!T:&4@M<W1A=&EC+@T*"0D):68@*&U?9B`A/2!N=6QL*0T*"0D)>PT*"0D)"6EF("A)M;G1E<FQO8VME9"Y$96-R96UE;G0H<F5F(&U?9BYM7W)E9BD@/3T@,"D-"@D)M"0D);5]F+D1I<W!O<V4H*3L-"@D)"7T-"@D)"6EF("AF("$](&YU;&PI#0H)M"0D)26YT97)L;V-K960N26YC<F5M96YT*')E9B!F+FU?<F5F*3L-"@D)"6U?M9B`](&8[#0H-"@D)"2\O(&8@9V]E<R!O=70@;V8@<V-O<&4N#0H)"0EI9B`HM26YT97)L;V-K960N1&5C<F5M96YT*')E9B!F+FU?<F5F*2`]/2`P*0T*"0D)M"68N1&ES<&]S92@I.PT*"0E]#0H-"@D)+R\@07-S:6=N(&8R('1O('-T871IM8PT*"0EI9B`H;5]F("$](&YU;&PI#0H)"7L-"@D)"6EF("A);G1E<FQO8VMEM9"Y$96-R96UE;G0H<F5F(&U?9BYM7W)E9BD@/3T@,"D-"@D)"0EM7V8N1&ESM<&]S92@I.PT*"0E]#0H)"6EF("AF,B`A/2!N=6QL*0T*"0D)26YT97)L;V-KM960N26YC<F5M96YT*')E9B!F,BYM7W)E9BD[#0H)"6U?9B`](&8R.PT*#0H)M"2\O(&8R(&=O97,@;W5T(&]F('-C;W!E+@T*"0EI9B`H26YT97)L;V-K960NM1&5C<F5M96YT*')E9B!F,BYM7W)E9BD@/3T@,"D-"@D)"68R+D1I<W!O<V4HM*3L-"@T*"0ET:6-K<R`]($5N=FER;VYM96YT+E1I8VM#;W5N="`M('1I8VMSM.PT*"0E#;VYS;VQE+E=R:71E3&EN92@B5&EC:W,@/2?0T*K#0H)<'5B;&EC('9I<G1U86P@=F]I9"!$:7-P;W-E*"D-"@E[#0H)?0T*?0==`endbegin 600 ref_rs.csM=7-I;F<@4WES=&5M.PT*#0IP=6)L:6,@8VQA<W,@1F]O#0I[#0H)<')I=F%TM92!S=&%T:6,@1F]O(&U?9CL-"@EP<FEV871E(&EN="!M7VUE;6)E<CL-"@EPM<FEV871E(&EN="!M7W)E9CL-"@T*"7!U8FQI8R!S=&%T:6,@=F]I9"!-86ENM*%-T<FEN9UM=(&%R9W,I#0H)>PT*"0EI;G0@=&EC:W,@/2!%;G9I<F]N;65NM="Y4:6-K0V]U;G0[#0H-"@D)1F]O(&8R(#T@;G5L;#L-"@T*"0EF;W(@*&ENM="!I/3`[(&D@/"`Q,#`P,#`P,#L@*RMI*0T*"0E[#0H)"0E&;V\@9B`](&YEM=R!&;V\H*3L-"@T*"0D)+R\@07-S:6=N('-T871I8R!T;R!F,BX-"@D)"6EFM("AF,B`A/2!N=6QL*0T*"0D)>PT*"0D)"6EF("@M+68R+FU?<F5F(#T](#`IM#0H)"0D)"68R+D1I<W!O<V4H*3L-"@D)"7T-"@D)"6EF("AM7V8@(3T@;G5LM;"D-"@D)"0DK*VU?9BYM7W)E9CL-"@D)"68R(#T@;5]F.PT*#0H)"0DO+R!!M<W-I9VX@9B!T;R!T:&4@<W1A=&EC+@T*"0D):68@*&U?9B`A/2!N=6QL*0T*M"0D)>PT*"0D)"6EF("@M+6U?9BYM7W)E9B`]/2`P*0T*"0D)"0EM7V8N1&ESM<&]S92@I.PT*"0D)?0T*"0D):68@*&8@(3T@;G5L;"D-"@D)"0DK*V8N;5]RM968[#0H)"0EM7V8@/2!F.PT*#0H)"0DO+R!F(&=O97,@;W5T(&]F('-C;W!EM+@T*"0D):68@*"TM9BYM7W)E9B`]/2`P*0T*"0D)"68N1&ES<&]S92@I.PT*M"0E]#0H-"@D)+R\@07-S:6=N(&8R('1O('-T871I8PT*"0EI9B`H;5]F("$]M(&YU;&PI#0H)"7L-"@D)"6EF("@M+6U?9BYM7W)E9B`]/2`P*0T*"0D)"6U?M9BY$:7-P;W-E*"D[#0H)"7T-"@D):68@*&8R("$](&YU;&PI#0H)"0DK*V8RM+FU?<F5F.PT*"0EM7V8@/2!F,CL-"@T*"0DO+R!F,B!G;V5S(&]U="!O9B!SM8V]P92X-"@D):68@*"TM9C(N;5]R968@/3T@,"D-"@D)"68R+D1I<W!O<V4HM*3L-"@T*"0ET:6-K<R`]($5N=FER;VYM96YT+E1I8VM#;W5N="`M('1I8VMSM.PT*"0E#;VYS;VQE+E=R:71E3&EN92@B5&EC:W,@/2?0T*K#0H)<'5B;&EC('9I<G1U86P@=F]I9"!$:7-P;W-E*"D-"@E[#0H)?0T*?0==`end