[Vm-dev] VMBIGENDIAN question (was: A proposal to split VMMaker into subpackages)

Nicolas Cellier nicolas.cellier.aka.nice at gmail.com
Fri Mar 22 21:25:14 UTC 2013


2013/3/22 Bert Freudenberg <bert at freudenbergs.de>:
>
> On 2013-03-22, at 05:43, David T. Lewis <lewis at mail.msen.com> wrote:
>
>> "It ain't what you don't know that gets you into trouble. It's what you know for sure that just ain't so."
>> -- Mark Twain
>>
>> An interpreter VM compiled with the normal C macros in sqMemoryAccess.h (for "performance"):
>>
>> 0 tinyBenchmarks. '417277913 bytecodes/sec; 14395420 sends/sec'
>> 0 tinyBenchmarks. '414239482 bytecodes/sec; 14646769 sends/sec'
>> 0 tinyBenchmarks. '417277913 bytecodes/sec; 14406658 sends/sec'
>>
>> The same interpreter VM with C macros replaced by Smalltalk slang (class MemoryAccess):
>>
>> 0 tinyBenchmarks. '455111111 bytecodes/sec; 14217973 sends/sec'
>> 0 tinyBenchmarks. '451897616 bytecodes/sec; 14485815 sends/sec'
>> 0 tinyBenchmarks. '453900709 bytecodes/sec; 14497194 sends/sec'
>>
>> Dave
>
> That is ... unexpected :)
>

Well, that's almost the same code both in MemoryAcess and
sqMemoryAccess.h right?
Maybe a bit different if you define USE_INLINE_MEMORY_ACCESSORS:

  static inline sqInt byteAt(sqInt oop)				{ return
byteAtPointer(pointerForOop(oop)); }
  static inline sqInt byteAtPointer(char *ptr)			{ return
(sqInt)(*((unsigned char *)ptr)); }
  static inline char *pointerForOop(usqInt oop)			{ return sqMemoryBase + oop; }

Though I do not well see why it would not inline such simple piece,
gcc has a license to not honour the inline request.
On the other side MemoryAccess will always inline as we asked the code
generator to (self inline: true)
It would be worth verifying if one of the static function is generated
in the executable (with nm -a or something).

But I also see other subtle differences like this:

intAtPointer: ptr put: val
	self inline: true.
	self var: #ptr type: 'char *'.
	self var: #val type: 'unsigned int'.
	^ self cCoerce:
			((self cCoerce: ptr to: 'unsigned int *')
				at: 0
				put: val)
		to: 'sqInt'

while the header tells
  static inline sqInt intAtPointerput(char *ptr, int val)	{ return
(sqInt)(*((unsigned int *)ptr)= (int)val); }

OK, you might think that casting int->unsigned int is no-op on
2-complement machines.
But it's a distraction, we must omit the intermediate (*(unsigned int
*)) and just consider that the return value is assigned with the
parameter val.
So the header just copy an int->int, but MemoryAccess uses the
opposite cast unsigned int->int
It's also a no-op except that:
- the cast can overflow, which would be UB.
- gcc has a licence to presume you don't rely on UB and thus can
further consider the returned int is always >= 0
That assertion cannot be done in the case of sqMemoryAccess.h

So all I see here has nothing to do with premature optimization.
It has to do with lack of understanding of the modern C standards, and
the absolute casualness attitude we take with signed and unsigned
types.

Nicolas

> But it again shows the importance of the 3rd rule of Optimization.
> http://c2.com/cgi/wiki?RulesOfOptimization
>
> - Bert -
>


More information about the Vm-dev mailing list