[ENH] Interpreter, ObjectMemory activate

John M McIntosh johnmci at smalltalkconsulting.com
Wed Jan 2 06:46:02 UTC 2002


I'll throw this out for comment. I've been looking at why sends are 
slower than say 2.8. As part of this work I cleaned up a few methods 
to improve performance of message sends. So perhaps someone can 
review and let me know if this makes a difference? Of course letting 
me know if I've not broken anything would be cool too, mind usually 
at this level mistakes are very fatal...


'From Squeak3.2alpha of 1 November 2001 [latest update: #4599] on 1 
January 2002 at 10:38:25 pm'!
"Change Set:		SlightlyFasterActivate-JMM
Date:			1 January 2002
Author:			johnmci at smalltalkconsulting.com

A few minor changes to clean up the code for method activation. In 
some places too many if statements are executed, and in others we 
have a common sub expression that is evaulated as we stuff values 
into the context header. Most compilers mostly factor that out, but 
if we explicitly do it then we get much better assembler code for the 
for loops. This improves messages sends by about a percent or so"!


!ObjectMemory methodsFor: 'allocation' stamp: 'JMM 12/29/2001 19:18'!
allocate: byteSize headerSize: hdrSize h1: baseHeader h2: classOop 
h3: extendedSize doFill: doFill with: fillWord
	"Allocate a new object of the given size and number of header 
words. (Note: byteSize already includes space for the base header 
word.) Initialize the header fields of the new object and fill the 
remainder of the object with the given value."

	| newObj remappedClassOop end i |
	self inline: true.
	"remap classOop in case GC happens during allocation"
	hdrSize > 1 ifTrue: [ self pushRemappableOop: classOop ].
   	newObj _ self allocateChunk: byteSize + ((hdrSize - 1) * 4).
	hdrSize > 1 ifTrue:
		[remappedClassOop _ self popRemappableOop.
		hdrSize = 2
			ifTrue:
				[self longAt: newObj     put: 
(remappedClassOop bitOr: HeaderTypeClass).
				self longAt: newObj + 4 put: 
(baseHeader bitOr: HeaderTypeClass).
				newObj _ newObj + 4]
			ifFalse:
				[self longAt: newObj     put: 
(extendedSize bitOr: HeaderTypeSizeAndClass).
				self longAt: newObj + 4 put: 
(remappedClassOop bitOr: HeaderTypeSizeAndClass).
				self longAt: newObj + 8 put: 
(baseHeader bitOr: HeaderTypeSizeAndClass).
				newObj _ newObj + 8]]
		ifFalse: [self longAt: newObj put: (baseHeader bitOr: 
HeaderTypeShort)].

	"clear new object"
	doFill ifTrue:
		[end _ newObj + byteSize.
		i _ newObj + 4.
		[i < end] whileTrue:
			[self longAt: i put: fillWord.
			i _ i + 4]].

	DoAssertionChecks ifTrue: [
		self okayOop: newObj.
		self oopHasOkayClass: newObj.
		(self objectAfter: newObj) = freeBlock
			ifFalse: [ self error: 'allocate bug: did not 
set header of new oop correctly' ].
		(self objectAfter: freeBlock) = endOfMemory
			ifFalse: [ self error: 'allocate bug: did not 
set header of freeBlock correctly' ].
	].

	^ newObj! !

!ObjectMemory methodsFor: 'allocation' stamp: 'JMM 12/29/2001 19:05'!
allocateOrRecycleContext: needsLarge
	"Return a recycled context or a newly allocated one if none 
is available for recycling."
	| cntxt |
	needsLarge = 0
	ifTrue: [freeContexts ~= NilContext ifTrue:
				[cntxt _ freeContexts.
				freeContexts _ self fetchPointer: 0 
ofObject: cntxt.
				^ cntxt].
			cntxt _ self instantiateContext: (self 
splObj: ClassMethodContext)
				sizeInBytes: SmallContextSize]
	ifFalse: [freeLargeContexts ~= NilContext ifTrue:
				[cntxt _ freeLargeContexts.
				freeLargeContexts _ self 
fetchPointer: 0 ofObject: cntxt.
				^ cntxt].
			cntxt _ self instantiateContext: (self 
splObj: ClassMethodContext)
				sizeInBytes: LargeContextSize].

	"Required init -- above does not fill w/nil.  All others get written."
	self storePointerUnchecked: 4 "InitialIPIndex" ofObject: cntxt
					withValue: nilObj.
	^ cntxt
! !

!ObjectMemory methodsFor: 'allocation' stamp: 'JMM 12/29/2001 17:43'!
recycleContextIfPossible: cntxOop
	| header |
	"If possible, save the given context on a list of free 
contexts to be recycled."
	"Note: The context is not marked free, so it can be reused 
with minimal fuss.  The recycled context lists are cleared at every 
garbage collect."

	self inline: true.
	"only recycle young contexts (which should be most of them)"
	cntxOop >= youngStart ifTrue:
		[header _ self baseHeader: cntxOop.
		(self isMethodContextHeader: header) ifTrue:
			["It's a young context, alright."
			(header bitAnd: SizeMask) = SmallContextSize
				ifTrue:
				["Recycle small contexts"
				self storePointerUnchecked: 0 
ofObject: cntxOop withValue: freeContexts.
				freeContexts _ cntxOop]
				ifFalse:
					[(header bitAnd: SizeMask) = 
LargeContextSize
						ifTrue:
						["Recycle large contexts"
						self 
storePointerUnchecked: 0 ofObject: cntxOop withValue: 
freeLargeContexts.
						freeLargeContexts _ cntxOop]]]]
! !


!Interpreter methodsFor: 'message sending' stamp: 'JMM 12/29/2001 15:39'!
activateNewMethod
	| newContext methodHeader initialIP tempCount nilOop where argCount2 |
	methodHeader _ self headerOf: newMethod.
	newContext _ self allocateOrRecycleContext: (methodHeader 
bitAnd: LargeContextBit).

	initialIP _ ((LiteralStart + (self literalCountOfHeader: 
methodHeader)) * 4) + 1.
	tempCount _ (methodHeader >> 19) bitAnd: 16r3F.

	"Assume: newContext will be recorded as a root if necessary by the
	 call to newActiveContext: below, so we can use unchecked stores."

	where _  (self cCoerce: newContext to: 'char *') + BaseHeaderSize.
	self longAt: where + (SenderIndex << 2) put: activeContext.
	self longAt: where + (InstructionPointerIndex << 2) put: 
(self integerObjectOf: initialIP).
	self longAt: where + (StackPointerIndex << 2) put: (self 
integerObjectOf: tempCount).
	self longAt: where + (MethodIndex << 2) put: newMethod.

	"Copy the reciever and arguments..."
	argCount2 _ argumentCount.
	0 to: argCount2 do:
		[:i | self longAt: where + ((ReceiverIndex+i) << 2) 
put: (self stackValue: argCount2-i)].

	"clear remaining temps to nil in case it has been recycled"
	nilOop _ nilObj.
	argCount2+1+ReceiverIndex to: tempCount+ReceiverIndex do:
		[:i | self longAt: where + (i << 2) put: nilOop].

	self pop: argCount2 + 1.
	reclaimableContextCount _ reclaimableContextCount + 1.
	self newActiveContext: newContext.! !

!Interpreter methodsFor: 'message sending' stamp: 'JMM 12/29/2001 17:26'!
internalActivateNewMethod
	| methodHeader newContext tempCount argCount2 needsLarge where |
	self inline: true.

	methodHeader _ self headerOf: newMethod.
	needsLarge _ methodHeader bitAnd: LargeContextBit.
	(needsLarge = 0 and: [freeContexts ~= NilContext])
		ifTrue: [newContext _ freeContexts.
				freeContexts _ self fetchPointer: 0 
ofObject: newContext]
		ifFalse: ["Slower call for large contexts or empty free list"
				self externalizeIPandSP.
				newContext _ self 
allocateOrRecycleContext: needsLarge.
				self internalizeIPandSP].
	tempCount _ (methodHeader >> 19) bitAnd: 16r3F.

	"Assume: newContext will be recorded as a root if necessary by the
	 call to newActiveContext: below, so we can use unchecked stores."

	where _  (self cCoerce: newContext to: 'char *') + BaseHeaderSize.
	self longAt: where + (SenderIndex << 2) put: activeContext.
	self longAt: where + (InstructionPointerIndex << 2) put:
			(self integerObjectOf:
				(((LiteralStart + (self 
literalCountOfHeader: methodHeader)) * 4) + 1)).
	self longAt: where + (StackPointerIndex << 2) put: (self 
integerObjectOf: tempCount).
	self longAt: where + (MethodIndex << 2) put: newMethod.

	"Copy the reciever and arguments..."
	argCount2 _ argumentCount.
	0 to: argCount2 do:
		[:i | self longAt: where + ((ReceiverIndex+i) << 2)
				put: (self internalStackValue: argCount2-i)].

	"clear remaining temps to nil in case it has been recycled"
	methodHeader _ nilObj.  "methodHeader here used just as 
faster (register?) temp"
	argCount2+1+ReceiverIndex to: tempCount+ReceiverIndex do:
		[:i | self longAt: where + (i << 2) put: methodHeader].

	self internalPop: argCount2 + 1.
	reclaimableContextCount _ reclaimableContextCount + 1.
	self internalNewActiveContext: newContext.
! !

!Interpreter methodsFor: 'control primitives' stamp: 'JMM 12/29/2001 16:22'!
primitiveBlockCopy

	| context methodContext contextSize newContext initialIP where |
	context _ self stackValue: 1.
	(self isIntegerObject: (self fetchPointer: MethodIndex 
ofObject: context))
		ifTrue: ["context is a block; get the context of its 
enclosing method"
				methodContext _ self fetchPointer: 
HomeIndex ofObject: context]
		ifFalse: [methodContext _ context].
	contextSize _ self sizeBitsOf: methodContext.  "in bytes, 
including header"
	context _ nil.  "context is no longer needed and is not 
preserved across allocation"

	"remap methodContext in case GC happens during allocation"
	self pushRemappableOop: methodContext.
	newContext _ self instantiateContext: (self splObj: ClassBlockContext)
 
sizeInBytes: contextSize.
	methodContext _ self popRemappableOop.

	initialIP _ self integerObjectOf: instructionPointer - method.
	"Was instructionPointer + 3, but now it's greater by
		methodOop + 4 (headerSize) and less by 1 due to preIncrement"

	"Assume: have just allocated a new context; it must be young.
	 Thus, can use uncheck stores. See the comment in 
fetchContextRegisters."

	where _  (self cCoerce: newContext to: 'char *') + BaseHeaderSize.
	self longAt: where + (InitialIPIndex << 2) put: initialIP.
	self longAt: where + (InstructionPointerIndex << 2) put: initialIP.
	self longAt: where + (StackPointerIndex << 2) put: (self 
integerObjectOf: 0).
	self longAt: where + (BlockArgumentCountIndex << 2) put: 
(self stackValue: 0).
	self longAt: where + (HomeIndex << 2) put: methodContext.
	self longAt: where + (SenderIndex << 2) put: nilObj.

	self pop: 2 thenPush: newContext.! !


-- 
--
===========================================================================
John M. McIntosh <johnmci at smalltalkconsulting.com> 1-800-477-2659
Corporate Smalltalk Consulting Ltd.  http://www.smalltalkconsulting.com
===========================================================================




More information about the Squeak-dev mailing list