Hi all, As I said earlier I am on my way through a student project (cc'ed) to re-write/split/kill MiscPrimitive plugin. The plugin is composed of: - 2 Bitmap primitives - 1 Sound primitive. - 6 ByteObject primitives I will discuss here what is plan for each category of primitives. I would like comments and Eliot's approval before moving forward with the student project. 2 Bitmap primitives Those are the compress and uncompress Bitmap primitives. Tim suggested that we move the primitive to the BitBlt plugin (hence converting from SmartSyntax to Slang). I think this is the right thing to do. Agreed ? 1 Sound primitive This primitive can simply be moved to the SoundGenerator plugin. SoundGenerator is also built using SmartSyntax. Agreed ? 6 ByteObject primitives 1. translate: aString from: start to: stop table: table This primitive seems to be unused. I suggest we move it from a primitive to plain Smalltalk code. 2. stringHash: aString initialHash: speciesHash Since we now have hashMultiply as a primitive, the stringHash primitive is now faster for very small strings with a Smalltalk version using hashMultiply, and slower (2x) on medium to large strings. I suggest we move it to plain Smalltalk code. 3. findFirstInString, indexOfAscii, findSubString For these 3 primitives we can either add a ByteStringPlugin or add them as numbered primitive. 4. compare: string1 with: string2 collated: order This primitive is really important for performance, there are production application spending a huge amount of times for string equality. I suggest that we move this one to a numbered primitive that takes 2 or 3 parameters, the order being optional, and to rewrite the version without order (i.e. the version used by String>>#=, String>>#>, String>>#>=, etc.) in the JIT (numbered primitives are required for that). Alternatively we can add a numbered String>>#= numbered primitive and put the compare primitive in a ByteStringPrimitive. 5. String concatenation I would suggest to add in addition ByteString>>#, as a primitive. String concatenation is really important performance wise and that will ease deforestation (i.e. removing the allocation of temporary byte strings) in the Sista JIT which is quite difficult right now. Conclusion The main question is do we want 1 numbered primitive and 3-5 primitives in a small ByteStringPlugin, or do we want 3-5 numbered primitive. I don't mind either. I looked at the performance on the Sista VM, but there is still quite some work to make all of those efficient and in production. Recently other VMs (especially V8) decided to loose some peak performance in exchange for better baseline performance since many applications they were running were spending only 15-20% of the time in optimised code and 80-85% in baseline code, so primitive performance is a big deal no matter what we have in the future. Clément Béra Bâtiment B 40, avenue Halley 59650 Villeneuve d'Ascq |
Hi Clement,
2017-12-22 11:28 GMT+01:00 Clément Bera <[hidden email]>:
Also let the primitive return standard -1,0,1 code as the rest of the world instead of Squeak-only 1,2,3
|
In reply to this post by Clément Béra
Hi Clément, Hi All,
+1
There's a smaller intermediate step which is to eliminate the Smalltalk syntax for MiscPrimitivePlugin and simply rewrite it using traditional smart syntax slang as methods on MiscPrimitivePlugin. Perhaps we could add some mechanism to invoke jitted versions of these primitives for the performance critical ones, but it sounds like a lot of work. To be clear, Clément, I like the conceptual clarity of your proposal, and would be fully behind if we're if not for backward compatibility. It would be preferable to me if we can add some hack to use your new scheme but still run older images unchanged. This hack can then be discarded some time in the future. I don't want to have to maintain legacy VMs :-)
|
In reply to this post by Nicolas Cellier
Hi Nicolas, Hi Clément,
So what this suggests to me is that the right way to go about this is to - implement the primitives as Clément proposes, using numbered primitives for the performance critical ones, and implementing them in the JIT. - rewrite all the MiscPrimitivePlugin primitives as methods on MiscPrimitivePlugin using smart syntax slang, dropping the translatedPrimitives hack. Compile MiscPrimitivePlugin as an external plugin, included for backwards compatibility.
|
In reply to this post by Eliot Miranda-2
2017-12-22 17:49 GMT+01:00 Eliot Miranda <[hidden email]>:
Hi Eliot, Aren't all these primitives optional? Running an old image on a new VM woud fail to find primitives and fallback to Smalltalk (slang) code. That would mean that we don't have to maintain any compatibility at all (old image + new VM would just be slower). New image would require new VM, but we could also add fallback code to make the primitives optional.
|
Hi, First you can compile a VM with both the existing MiscPrimitivePlugin and the new plugins/numbered primitives so all images work fine. Second all MiscPrimitives are optional, so once MiscPrimitivePlugin support is dropped, everything is backward/forward compatible. Ok so it's decided. On Fri, Dec 22, 2017 at 9:17 PM, Nicolas Cellier <[hidden email]> wrote:
Clément Béra Pharo consortium engineer Bâtiment B 40, avenue Halley 59650 Villeneuve d'Ascq |
Hi Clément, Thanks for improving the VM with cleanups and refactorings like this! I've opened issue #117 [1] to track the progress of this effort if you don't mind :) Best, Fabio On Sat, 23 Dec 2017 at 9:57 am, Clément Bera <[hidden email]> wrote:
|
In reply to this post by Clément Béra
On Fri, 22 Dec 2017, Clément Bera wrote: > Hi all, snip > 1. translate: aString from: start to: stop table: table > > This primitive seems to be unused. I suggest we move it from a primitive to plain Smalltalk code. That primitive is currently used to convert ByteStrings to lower and upper case, and it's also used to convert between cr and lf line endings. The primitive fails in Pharo for some reason, but it's still used in the 6.0 image I have. It works properly in Squeak and Cuis. > > 2. stringHash: aString initialHash: speciesHash > > Since we now have hashMultiply as a primitive, the stringHash primitive is now faster for very small strings with a Smalltalk version using hashMultiply, and slower (2x) on medium to large strings. I suggest we move it to plain Smalltalk code. In my 64-bit Squeak Spur image, the primitive is faster than the pure Smalltalk code (which uses the new hashMultiply primitive) for strings of length 4 and longer. The longer the string, the more significant the different becomes, probably due to the more frequent use of the hashMultiply primitive (for a string of length 1000, it's used 1000 times). Here's the benchmark I wrote: data := #(0 1 2 3 4 5 10 20 50 100 200 500 1000) collect: [ :size | | s primitive smalltalk overhead iterations | Smalltalk garbageCollect. s := String new: size withAll: $a. iterations := 100000000 // (size max: 1). overhead := [ 1 to: iterations do: [ :i | ] ] timeToRun. primitive := [ 1 to: iterations do: [ :i | ByteString stringHash: s initialHash: 1 ] ] timeToRun. smalltalk := [ 1 to: iterations do: [ :i | ByteString stringHash2: s initialHash: 1 ] ] timeToRun. { size. primitive - overhead. smalltalk - overhead } ]. So, I suggest the primitive be kept in some form. What I always wanted to see is a linear search primitive. Something like primitiveIndexOfAsciiInString, but more general: - it should use #== for comparison - it should return a number according to the following rules - return the (one-based) index of the first indexable field containing the value if such exists - return the (one-based) index of the first pointer field containing the value times -1 if such exists - return 0 otherwise We actually have a primitive for this, primitive 132, but it returns a boolean value instead of the actual index, which makes it far less useful than what it could be. Levente |
On Sat, 23 Dec 2017, Levente Uzonyi wrote: > Here's the benchmark I wrote: > > data := #(0 1 2 3 4 5 10 20 50 100 200 500 1000) collect: [ :size | > | s primitive smalltalk overhead iterations | > Smalltalk garbageCollect. > s := String new: size withAll: $a. > iterations := 100000000 // (size max: 1). > overhead := [ 1 to: iterations do: [ :i | ] ] timeToRun. > primitive := [ 1 to: iterations do: [ :i | ByteString stringHash: s initialHash: 1 ] ] timeToRun. > smalltalk := [ 1 to: iterations do: [ :i | ByteString stringHash2: s initialHash: 1 ] ] timeToRun. > { size. primitive - overhead. smalltalk - overhead } ]. Forgot to mention that #stringHash2:initialHash: is the same method as #stringHash:initialHash: but without the primitive. Levente |
In reply to this post by Levente Uzonyi
On Sat, Dec 23, 2017 at 10:49 PM, Levente Uzonyi <[hidden email]> wrote:
Thing is instVarAt: works for everything #[10] instVarAt: 1 => 10 #(10) instVarAt: 1 => 10 So why having negative values ? Could it only answer a positive index or 0 and never a negative index ? Especially I think this makes most sense for variable objects, data or pointers, and I don't think negative values there makes sense. I don't really mind having a more generic numbered primitiveIndexOfAsciiInString instead of a string specific one. Maybe #indexOf: value startingAt: startInteger to: lastInteger So it can be used easily for OrderedCollection too: OrderedCollection>>#indexOf: anElement startingAt: start ifAbsent: exceptionBlock ^(index := array indexOf: anElement startingAt: firstIndex + start - 1 to: lastIndex) = 0 ifTrue: [exceptionBlock value] ifFalse: [index] instead of: OrderedCollection>>#indexOf: anElement startingAt: start ifAbsent: exceptionBlock firstIndex + start - 1 to: lastIndex do: [ :index | (array at: index) = anElement ifTrue: [ ^index - firstIndex + 1 ] ]. ^exceptionBlock value Levente Clément Béra Pharo consortium engineer Bâtiment B 40, avenue Halley 59650 Villeneuve d'Ascq |
On Thu, 28 Dec 2017, Clément Bera wrote: > Thing is instVarAt: works for everything > #[10] instVarAt: 1 => 10 > #(10) instVarAt: 1 => 10 > So why having negative values ? > Could it only answer a positive index or 0 and never a negative index ? > Especially I think this makes most sense for variable objects, data or pointers, and I don't think negative values there makes sense. I see three potential use cases (listed by decreasing genericity): (A) to ask the index of the indexable field or the receiver pointing to the argument (similar to primitiveIndexOfAsciiInString) (B) to ask whether or not the receiver points to argument (same as primitive 132) (C) to ask which field (let it either be indexable or a variable) of the receiver points to the argument (handy in pointer explorers) If the new primitive returned the absolute index (the one which can be used with #instVarAt:) as you proposed, then (A) would require subtracting the receiver's instance variable count. (B) would not be affected, (C) would require a comparison with the number of instance variables of the receiver. With the sign approach, (A) and (B) would just work, and (C) would only require a sign check. > > I don't really mind having a more generic numbered primitiveIndexOfAsciiInString instead of a string specific one. > > Maybe #indexOf: value startingAt: startInteger to: lastInteger I'd call it #identityIndexOf:..., since that's what a primitive can do. The start and end indices would be useful too, but I'd prefer them be optional with the default values 1 and nil. Levente > > So it can be used easily for OrderedCollection too: > OrderedCollection>>#indexOf: anElement startingAt: start ifAbsent: exceptionBlock > ^(index := array > indexOf: anElement > startingAt: firstIndex + start - 1 > to: lastIndex) = 0 > ifTrue: [exceptionBlock value] > ifFalse: [index] > instead of: > OrderedCollection>>#indexOf: anElement startingAt: start ifAbsent: exceptionBlock > firstIndex + start - 1 to: lastIndex do: [ :index | > (array at: index) = anElement ifTrue: [ ^index - firstIndex + 1 ] ]. > ^exceptionBlock value > |
In reply to this post by Levente Uzonyi
Indeed. Also, the primitive is a lot faster for non-jit VMs. E.g. the Stack interpreter for iOS, where no JIT compiler is allowed. We should keep those primitives, in some form. One idea would be to rewrite MiscPrimitivePlugin as a plain plugin, and only keep the few primitives that are not moved elsewhere. - Bert - |
On Sat, Dec 30, 2017 at 5:35 AM, Bert Freudenberg <[hidden email]> wrote:
I agree.
I like this. It shouldn't be hard. The parse trees from the translated primitives are good starting points. I take Clément's point that these are optional primitives but don't like the idea of a significant performance regression. And if MiscPrimitivePlugin is external then there's no overhead for the VM itself.
_,,,^..^,,,_ best, Eliot |
Free forum by Nabble | Edit this page |