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.
Hi Clement,
2017-12-22 11:28 GMT+01:00 Clément Bera bera.clement@gmail.com:
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.
Also let the primitive return standard -1,0,1 code as the rest of the
world instead of Squeak-only 1,2,3
*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 https://clementbera.wordpress.com/ Bâtiment B 40, avenue Halley 59650 *Villeneuve d'Ascq*
Hi Nicolas, Hi Clément,
On Dec 22, 2017, at 6:30 AM, Nicolas Cellier nicolas.cellier.aka.nice@gmail.com wrote:
Hi Clement,
2017-12-22 11:28 GMT+01:00 Clément Bera bera.clement@gmail.com:
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
- 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.
- 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.
- findFirstInString, indexOfAscii, findSubString
For these 3 primitives we can either add a ByteStringPlugin or add them as numbered primitive.
- 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.
Also let the primitive return standard -1,0,1 code as the rest of the world instead of Squeak-only 1,2,3
Agreed!
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.
- 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 https://clementbera.wordpress.com/ Bâtiment B 40, avenue Halley 59650 Villeneuve d'Ascq
Hi Clément, Hi All,
On Dec 22, 2017, at 2:28 AM, Clément Bera bera.clement@gmail.com wrote:
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.
IMO you should definitely go ahead. There is s backwards compatibility issue so it would be good to coordinate with a major release and maybe we can still support older images with some name hack in the vm which continues to recognize the 'MiscPrimitivePlugin' name in primitive pragmas, mapping to the correct name.
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
IIRC, Tim Rowledge wrote a primitive that uses smart syntax in the BitBlt plugin (BirBltSimulation) and so you don't have to use pure slang. You can at least use the smart syntax signature mechanism. I think this means that it is wrong to say that the translated primitives are written in smart syntax. They are written in a pure Smalltalk syntax that is different again from the syntax use to write primitives under SmartSyntaxPlugin (how confusing!! :-) )
1 Sound primitive
This primitive can simply be moved to the SoundGenerator plugin. SoundGenerator is also built using SmartSyntax. Agreed ?
+1
6 ByteObject primitives
- 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.
+1
- 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.
+1. And I've already suggested we sample a subset of elements for large strings, so we could beat the primitive if, say, we sampled no more than some large number of characters for very large strings.
- findFirstInString, indexOfAscii, findSubString
For these 3 primitives we can either add a ByteStringPlugin or add them as numbered primitive.
Whatever you do it would be really nice not to break vm backward compatibility/image forward compatibility. Otherwise this should be coordinated with a major release and we should provide a file in for older images.
- 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).
+1. Do you imagine the primitive being polymorphic in element widths (being able to compare an n byte string with an m byte string)? There are only 8 combinations.
Alternatively we can add a numbered String>>#= numbered primitive and put the compare primitive in a ByteStringPrimitive.
- 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.
Sure, but it might be better to investigate tree strings with O(1) concatenation. Mapping tree strings to flat strings would then occur in primitive failure code of primitives expecting flat strings. IIUC JavaScript VMs use tree strings right?
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.
The main question here is what the backward compatibility story is. What you're proposing will break backwards compatibility meaning older Spur images will not be able to run on new VMs. That's ok only if we coordinate these changes with a major release.
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 :-)
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.
+1
Clément Béra https://clementbera.wordpress.com/ Bâtiment B 40, avenue Halley 59650 Villeneuve d'Ascq
2017-12-22 17:49 GMT+01:00 Eliot Miranda eliot.miranda@gmail.com:
Hi Clément, Hi All,
On Dec 22, 2017, at 2:28 AM, Clément Bera bera.clement@gmail.com wrote:
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.
IMO you should definitely go ahead. There is s backwards compatibility issue so it would be good to coordinate with a major release and maybe we can still support older images with some name hack in the vm which continues to recognize the 'MiscPrimitivePlugin' name in primitive pragmas, mapping to the correct name.
*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
IIRC, Tim Rowledge wrote a primitive that uses smart syntax in the BitBlt plugin (BirBltSimulation) and so you don't have to use pure slang. You can at least use the smart syntax signature mechanism. I think this means that it is wrong to say that the translated primitives are written in smart syntax. They are written in a pure Smalltalk syntax that is different again from the syntax use to write primitives under SmartSyntaxPlugin (how confusing!! :-) )
*1 Sound primitive*
This primitive can simply be moved to the SoundGenerator plugin. SoundGenerator is also built using SmartSyntax. Agreed ?
+1
*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.
+1
*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.
+1. And I've already suggested we sample a subset of elements for large strings, so we could beat the primitive if, say, we sampled no more than some large number of characters for very large strings.
*3. findFirstInString, indexOfAscii, findSubString*
For these 3 primitives we can either add a ByteStringPlugin or add them as numbered primitive.
Whatever you do it would be really nice not to break vm backward compatibility/image forward compatibility. Otherwise this should be coordinated with a major release and we should provide a file in for older images.
*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).
+1. Do you imagine the primitive being polymorphic in element widths (being able to compare an n byte string with an m byte string)? There are only 8 combinations.
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.
Sure, but it might be better to investigate tree strings with O(1) concatenation. Mapping tree strings to flat strings would then occur in primitive failure code of primitives expecting flat strings. IIUC JavaScript VMs use tree strings right?
*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.
The main question here is what the backward compatibility story is. What you're proposing will break backwards compatibility meaning older Spur images will not be able to run on new VMs. That's ok only if we coordinate these changes with a major release.
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 :-)
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.
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.
+1
Clément Béra https://clementbera.wordpress.com/ Bâtiment B 40, avenue Halley 59650 *Villeneuve d'Ascq*
Hi,
Backward compatibility is no problem Eliot.
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 < nicolas.cellier.aka.nice@gmail.com> wrote:
2017-12-22 17:49 GMT+01:00 Eliot Miranda eliot.miranda@gmail.com:
Hi Clément, Hi All,
On Dec 22, 2017, at 2:28 AM, Clément Bera bera.clement@gmail.com wrote:
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.
IMO you should definitely go ahead. There is s backwards compatibility issue so it would be good to coordinate with a major release and maybe we can still support older images with some name hack in the vm which continues to recognize the 'MiscPrimitivePlugin' name in primitive pragmas, mapping to the correct name.
*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
IIRC, Tim Rowledge wrote a primitive that uses smart syntax in the BitBlt plugin (BirBltSimulation) and so you don't have to use pure slang. You can at least use the smart syntax signature mechanism. I think this means that it is wrong to say that the translated primitives are written in smart syntax. They are written in a pure Smalltalk syntax that is different again from the syntax use to write primitives under SmartSyntaxPlugin (how confusing!! :-) )
*1 Sound primitive*
This primitive can simply be moved to the SoundGenerator plugin. SoundGenerator is also built using SmartSyntax. Agreed ?
+1
*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.
+1
*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.
+1. And I've already suggested we sample a subset of elements for large strings, so we could beat the primitive if, say, we sampled no more than some large number of characters for very large strings.
*3. findFirstInString, indexOfAscii, findSubString*
For these 3 primitives we can either add a ByteStringPlugin or add them as numbered primitive.
Whatever you do it would be really nice not to break vm backward compatibility/image forward compatibility. Otherwise this should be coordinated with a major release and we should provide a file in for older images.
*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).
+1. Do you imagine the primitive being polymorphic in element widths (being able to compare an n byte string with an m byte string)? There are only 8 combinations.
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.
Sure, but it might be better to investigate tree strings with O(1) concatenation. Mapping tree strings to flat strings would then occur in primitive failure code of primitives expecting flat strings. IIUC JavaScript VMs use tree strings right?
*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.
The main question here is what the backward compatibility story is. What you're proposing will break backwards compatibility meaning older Spur images will not be able to run on new VMs. That's ok only if we coordinate these changes with a major release.
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 :-)
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.
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.
+1
Clément Béra https://clementbera.wordpress.com/ 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
[1] https://github.com/OpenSmalltalk/opensmalltalk-vm/issues/177
On Sat, 23 Dec 2017 at 9:57 am, Clément Bera bera.clement@gmail.com wrote:
Hi,
Backward compatibility is no problem Eliot.
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 < nicolas.cellier.aka.nice@gmail.com> wrote:
2017-12-22 17:49 GMT+01:00 Eliot Miranda eliot.miranda@gmail.com:
Hi Clément, Hi All,
On Dec 22, 2017, at 2:28 AM, Clément Bera bera.clement@gmail.com wrote:
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.
IMO you should definitely go ahead. There is s backwards compatibility issue so it would be good to coordinate with a major release and maybe we can still support older images with some name hack in the vm which continues to recognize the 'MiscPrimitivePlugin' name in primitive pragmas, mapping to the correct name.
*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
IIRC, Tim Rowledge wrote a primitive that uses smart syntax in the BitBlt plugin (BirBltSimulation) and so you don't have to use pure slang. You can at least use the smart syntax signature mechanism. I think this means that it is wrong to say that the translated primitives are written in smart syntax. They are written in a pure Smalltalk syntax that is different again from the syntax use to write primitives under SmartSyntaxPlugin (how confusing!! :-) )
*1 Sound primitive*
This primitive can simply be moved to the SoundGenerator plugin. SoundGenerator is also built using SmartSyntax. Agreed ?
+1
*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.
+1
*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.
+1. And I've already suggested we sample a subset of elements for large strings, so we could beat the primitive if, say, we sampled no more than some large number of characters for very large strings.
*3. findFirstInString, indexOfAscii, findSubString*
For these 3 primitives we can either add a ByteStringPlugin or add them as numbered primitive.
Whatever you do it would be really nice not to break vm backward compatibility/image forward compatibility. Otherwise this should be coordinated with a major release and we should provide a file in for older images.
*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).
+1. Do you imagine the primitive being polymorphic in element widths (being able to compare an n byte string with an m byte string)? There are only 8 combinations.
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.
Sure, but it might be better to investigate tree strings with O(1) concatenation. Mapping tree strings to flat strings would then occur in primitive failure code of primitives expecting flat strings. IIUC JavaScript VMs use tree strings right?
*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.
The main question here is what the backward compatibility story is. What you're proposing will break backwards compatibility meaning older Spur images will not be able to run on new VMs. That's ok only if we coordinate these changes with a major release.
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 :-)
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.
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.
+1
Clément Béra https://clementbera.wordpress.com/ Bâtiment B 40, avenue Halley 59650 *Villeneuve d'Ascq*
-- Clément Béra Pharo consortium engineer https://clementbera.wordpress.com/ Bâtiment B 40, avenue Halley 59650 Villeneuve d'Ascq
On Fri, 22 Dec 2017, Clément Bera wrote:
Hi all,
snip
- 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.
- 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
On Sat, Dec 23, 2017 at 10:49 PM, Levente Uzonyi leves@caesar.elte.hu wrote:
On Fri, 22 Dec 2017, Clément Bera wrote:
Hi all,
snip
- 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.
- 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.
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
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
On Sat, Dec 23, 2017 at 10:49 PM, Levente Uzonyi leves@caesar.elte.hu wrote:
On Fri, 22 Dec 2017, Clément Bera wrote:
Hi all,
snip
- 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.
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 bert@freudenbergs.de wrote:
On Sat, Dec 23, 2017 at 10:49 PM, Levente Uzonyi leves@caesar.elte.hu wrote:
On Fri, 22 Dec 2017, Clément Bera wrote:
Hi all,
snip
- 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.
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.
I agree.
One idea would be to rewrite MiscPrimitivePlugin as a plain plugin, and only keep the few primitives that are not moved elsewhere.
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.
- Bert -
_,,,^..^,,,_ best, Eliot
vm-dev@lists.squeakfoundation.org