[Pharo-project] Ideas for CompiledMethod proxies?

Eliot Miranda eliot.miranda at gmail.com
Fri Nov 19 23:03:47 CET 2010


Hi Mariano,

    it strikes me that the class and selector are implicit in method
dictionaries.  So if you use a zero-sized object to replace a method (one
word in memory if you make it an instance of a compact class) you can find
its class and selector by searching all method dictionaries in the class
hierarchy.  In an image with approximately 90,000 methods on a 2.66GHz Core
i7 it takes Cog 15 milliseconds on average to locate a method in the
hierarchy:

| cms |
cms := CompiledMethod allInstances.
cms := (1 to: cms size by: 100) collect: [:i| cms at: i].
{ Time millisecondsToRun:
[cms do:
[:cm|
[:exit|
ProtoObject withAllSubclasses do:
[:c|
c selectorsAndMethodsDo: [:s :m| m == cm ifTrue: [exit value]]]]
valueWithExit]].
 cms size } #(13371 917)
13371 / 917.0 14.58124318429662
CompiledMethod instanceCount 91696

So one thing might be to use a zero-sized object.

Of course you could use a one instance variable object whose instance
variable was the class and then things would be /much/ faster, with only one
method dictionary to search.

Or perhaps you could use a 4-byte byte array and keep the identity hash of
the class and the identity hash of the selector in two 16-bit values in the
4-byte array, but this wouldn't get you anything over and above the one
instance variable object pointing to the class.  However, if you are talking
abut optimizing a closed world then this does hold water.  One thing you can
do if you *really* want to save space is eliminate symbols, replacing them
by integers.  The lookup machinery should be able to deal with a
SmallInteger selector, and if it doesn't it has a bug.  This is a space
saving technique I first heard about in the ActiveBook which used my
BrouHaHa VM and ParcPlace's Smalltalk-80 v2.3 back in the 80's to produce an
early tablet computer.  So the idea is to number classes and selectors and
use the selector numbers to replace symbols.  Now the byte array makes sense
because if you have < 64k symbols and < 64k classes you have the class and
the selector directly. If you were clever you could put the selector
numbering in the compiler and keep the symbol table externally in a file,
mapping selector number to symbol there-in.  Obviously the tricky thing is
in eliminating or implementing asSymbol in perform: usage.  Sometimes you
see horror show code like
    self perform: (self opPrefix, self opSuffix) asSymbol
which makes it /really/ hard to find implementors and senders (and IMO
people who write this kind of thing should be shot, and IIRC there is stuff
like this in the change notification code, ARGH!!!).

HTH
Eliot

On Fri, Nov 19, 2010 at 12:47 PM, Mariano Martinez Peck <
marianopeck at gmail.com> wrote:

> Hi. I am developing kind of proxies for unused compiled methods. I want to
> detect them, put a proxy instead of them, and swap them to disk. Then, in
> case they are used, I put back the original ones. With this, I want to
> realease memory. This is why I need the proxy objects to be as small as
> possibles.
>
> Right now, I do something like this:
>
> aProxy := CompiledMethodProxyReferenceStreamWithFileName installOn:
> ClassWith1Var selector: #foo.
>
> this will instantiate the proxy, do the ClassWith1Var methodAt: #foo put:
> proxy, write the original compiledMethod that was in #foo into a file,
> etc....
>
> and then if I do "ClassWith1Var new foo", then using the #run:with:in  I
> load the file from disk and I put back the original method. To know WHICH
> file I have to load, I have to store the fileName in the proxy object.
>
> In addition, if I DON'T execute the compiled method, but instead I send a
> message, for example:
>
> (ClassWith1Var >> #foo) literals
>
> this has to do the same: load and put back the original compiled method. To
> do this, I use the doesNotUnderstand:.
>
> This is easy to do if I store the fileName in the proxy instance. Right
> now, I am using as fileName this:  CLASS >> SELECTOR. For example, in this
> case, the file would be named "ClassWith1Var >> #foo"
>
> Now.....with this solution, I need create an proxy instance for each
> compiled method, and these instances have also another instance for the
> filename. If I could use the same proxy instance for everybody, the amount
> of memory released would be muuuuch more. So I thought that maybe from the
> context I can "guess" the filename if I use conventions.
>
> For example, if you do "ClassWith1Var new foo" then in the #run:with:in I
> receive all the information to KNOW that the fileName should be
> "ClassWith1Var >> #foo". So, no problem here.
>
> But for the second case, suppose "(ClassWith1Var >> #foo) literals", I
> don't see a way to know this.  In this case, #literals is intercepted by the
> doesNotUnderstand: of my proxy, and here I should load back the original
> compiled method from disk. The problem is that I cannot know in which CLASS
> and SELECTOR this proxy was put. The only way I know to do this, is to store
> the class + selector in the proxy instance....but it is the same...I will
> need an instance per compiled method. In such case is even easier to
> directly store the fileName.
>
> Anyway, if someone has a solution or advice on this, I would be appreciated
> that. I tried with thisContext, but it didn't help me.
>
> Thanks
>
> Mariano
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.gforge.inria.fr/pipermail/pharo-project/attachments/20101119/4aabd9ce/attachment.htm>


More information about the Pharo-project mailing list