On Fri, Nov 19, 2010 at 11:03 PM, Eliot Miranda <eliot.miranda@gmail.com> wrote:
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.


Thanks Eliot for the idea. I didn't think about searching the method in the whole hierarchy because I thought it was going to be much slower. And its true, storing a pointer to the class will be fast enought.

So...taking into account all the answers I think I should discard the idea of using the SAME proxy instance for everybody and using fileName patterns to guess the file. It seems there is no way. So...if I need an instance per proxy, now let's see how to make this object as small as possible. In the case you told me, the object will be zero sized and if the class is compact, then it would be just one word.
But the "search" will take some time.....if I store the class pointer, then it would be 8 bytes per proxy...

This is why I liked the Levente solution (I answer after to him).
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. �

Exactly. The only thing here is that since I have both, class and selector, and don't need to do the search in the class method dict.
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.

exactly!


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.

ahhhhh� ok, I got the idea :)

Thanks Eliot for the help.
�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@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