Hi everybody,
JNIPort for Pharo 3.0 alpha is now available at SmalltalkHub. JNIPort is a Smalltalk library which allows Java code to be invoked from Smalltalk. It acts as a bridge between the world of Smalltalk objects and a Java Virtual Machine (JVM) where Java code is executing. When I first ported JNIPort to Squeak and Pharo 1.x, I used Alien as the FFI library. As NativeBoost has superseded Alien, the interface to the Java Native Interface library had to be rewritten. There are still some undocumented and obscure parts in NativeBoost, so this has taken some time. If you want to try it, load it via the ConfigurationOfJNIPort. I hope this works. If it does not, load the packages individually in the order in which they appear in the ConfigurationOfJNIPort. http://www.smalltalkhub.com/#!/~JNIPort/JNIPort The next step is to read the documentation at http://jniport.wikispaces.com/ Otherwise, you won't know what to do next. ;-) Download the extra files from the download page at Wikispaces, you will need two jar files from there. Start a Java VM attached to Pharo by adapting and executing the following code snippet: ---- | jvmSettings | "You can set the path to the Java VM library by editing the path as needed." JNIPortJNIInterface libraryFile: '/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Libraries/libclient.dylib' . "JNIPortJNIInterface libraryFile: 'C:\Programme\Java\jre7\bin\client\jvm.dll' ." jvmSettings := JVMSettings new. jvmSettings usesGhosts: true. jvmSettings supportsCallbacks: true. jvmSettings jniPortSettings useJNIHelperLibrary: false. jvmSettings ghostClassSettings retainMethodSource: true. "On Mac OS X and Linux, class path entries are separated by colons." "On Windows, you have to use semicolons instead. Edit the following line as needed." jvmSettings runtimeSettings classpath: '.:/Users/myname/JNIPort.jar:/Users/myname/JNIPort-Tests.jar'. " jvmSettings runtimeSettings classpath: '.;.\JNIPort.jar;.\JNIPort-Tests.jar'." "Uncomment the following if you want the Java VM to emit verbose messages. How to access these messages depends on your platform. On Mac OS X, they are visible in the Console application." "jvmSettings runtimeSettings addOption: '-verbose:jni'; addOption: '-verbose:gc'; addOption: '-verbose:class'." Cursor execute showWhile: [JVM newWithSettings: jvmSettings]. ---- Then the fun starts: ---- | zfClass zipfile entries | zfClass := JVM current findClass: #'java.util.zip.ZipFile'. zipfile := zfClass new_String: 'JNIPort.jar'. zipfile size. "--> answers an Integer" entries := zipfile entries. entries asAnEnumeration do: [:each | Transcript cr; print: each]. ---- And now to the ugly little secret reason why I call it an alpha version: While everything seems to work, it is much slower than it used to be in Pharo 1.2 with Alien. And when I compare it with VisualWorks, I wonder if the Pharo version secretly does some additional work for SETI: - Starting the JVM: VisualWorks 0.7 seconds Pharo 7 to 8 seconds - A benchmark using some methods of java.io.BufferedReader and java.util.StringTokenizer: VisualWorks 114 milliseconds Pharo 12 to 13 seconds I have not yet found the reason. The TimeProfiler is not very helpful, as its 1 millisecond resolution is too coarse. It would be nice if it had microsecond resolution. From the TimeProfiler results, it seems that calling a JNI method with NativeBoost is extremely slow, but I don't trust those profiles too much. So, give it a try, and if you find something useful concerning the bad performance, please send me a message. Best regards, Joachim Geidel |
Thanks a lot for this contribution! I will give it a try soon. Doru On Tue, Mar 25, 2014 at 7:59 PM, Joachim Geidel <[hidden email]> wrote: Hi everybody, "Every thing has its own flow"
|
Works on Windows 8.1 x64 with a 32-bit JRE 1.7 on a freshly fetched Pharo30 There is a Transcript flush that would help seeing the output to the Transcript… Snippet: | jvmSettings | "You can set the path to the Java VM library by editing the path as needed." "JNIPortJNIInterface libraryFile: '/System/Library/Java/JavaVirtualMachines/1..6.0.jdk/Contents/Libraries/libclient.dylib'" . JNIPortJNIInterface libraryFile: 'C:\Program Files (x86)\Java\jre7\bin\client\jvm.dll' .. jvmSettings := JVMSettings new. jvmSettings usesGhosts: true. jvmSettings supportsCallbacks: true. jvmSettings jniPortSettings useJNIHelperLibrary: false. jvmSettings ghostClassSettings retainMethodSource: true. "On Mac OS X and Linux, class path entries are separated by colons." "On Windows, you have to use semicolons instead. Edit the following line as needed." "jvmSettings runtimeSettings classpath: '.:/Users/myname/JNIPort.jar:/Users/myname/JNIPort-Tests.jar'." jvmSettings runtimeSettings classpath: '.;.\JNIPort.jar;.\JNIPort-Tests.jar'. "Uncomment the following if you want the Java VM to emit verbose messages. How to access these messages depends on your platform. On Mac OS X, they are visible in the Console application." "jvmSettings runtimeSettings addOption: '-verbose:jni'; addOption: '-verbose:gc'; addOption: '-verbose:class'." Cursor execute showWhile: [JVM newWithSettings: jvmSettings]. "---- Then the fun starts: ----" zfClass := JVM current findClass: #'java.util.zip.ZipFile'. zipfile := zfClass new_String: 'JNIPort.jar'. zipfile size. "--> answers an Integer" entries := zipfile entries. entries asAnEnumeration do: [:each | Transcript cr; print: each]. Transcript flush. "----" Cool to have a bridge. Phil From: Pharo-users [mailto:[hidden email]] On Behalf Of Tudor Girba Thanks a lot for this contribution! I will give it a try soon. Doru On Tue, Mar 25, 2014 at 7:59 PM, Joachim Geidel <[hidden email]> wrote:
-- "Every thing has its own flow"
JNIPort25-03-14 23-52-52.png (243K) Download Attachment |
I also tested it on Windows 7 and it works just great. Just a question: is there a way to communicate with an already running VM that was started independently from Pharo? Doru On Tue, Mar 25, 2014 at 11:55 PM, Philippe Back <[hidden email]> wrote:
"Every thing has its own flow"
|
In reply to this post by Joachim Geidel
EXCELLENT!!!!
Thanks for this important contribution. Stef On 25 Mar 2014, at 19:59, Joachim Geidel <[hidden email]> wrote: > Hi everybody, > > JNIPort for Pharo 3.0 alpha is now available at SmalltalkHub. > > JNIPort is a Smalltalk library which allows Java code to be invoked from > Smalltalk. It acts as a bridge between the world of Smalltalk objects and a > Java Virtual Machine (JVM) where Java code is executing. > > When I first ported JNIPort to Squeak and Pharo 1.x, I used Alien as the FFI > library. As NativeBoost has superseded Alien, the interface to the Java > Native Interface library had to be rewritten. There are still some > undocumented and obscure parts in NativeBoost, so this has taken some time. > > If you want to try it, load it via the ConfigurationOfJNIPort. I hope this > works. If it does not, load the packages individually in the order in which > they appear in the ConfigurationOfJNIPort. > http://www.smalltalkhub.com/#!/~JNIPort/JNIPort > The next step is to read the documentation at > http://jniport.wikispaces.com/ > Otherwise, you won't know what to do next. ;-) > Download the extra files from the download page at Wikispaces, you will need > two jar files from there. > > Start a Java VM attached to Pharo by adapting and executing the following > code snippet: > > ---- > | jvmSettings | > "You can set the path to the Java VM library by editing the path as > needed." > JNIPortJNIInterface libraryFile: > '/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Libraries/libclient.dylib' > . > "JNIPortJNIInterface libraryFile: > 'C:\Programme\Java\jre7\bin\client\jvm.dll' ." > > jvmSettings := JVMSettings new. > jvmSettings usesGhosts: true. > jvmSettings supportsCallbacks: true. > jvmSettings jniPortSettings useJNIHelperLibrary: false. > jvmSettings ghostClassSettings retainMethodSource: true. > > "On Mac OS X and Linux, class path entries are separated by colons." > "On Windows, you have to use semicolons instead. Edit the following line as > needed." > jvmSettings runtimeSettings classpath: > '.:/Users/myname/JNIPort.jar:/Users/myname/JNIPort-Tests.jar'. > " jvmSettings runtimeSettings classpath: > '.;.\JNIPort.jar;.\JNIPort-Tests.jar'." > > "Uncomment the following if you want the Java VM to emit verbose messages. > How to access these messages depends on your platform. > On Mac OS X, they are visible in the Console application." > "jvmSettings runtimeSettings addOption: '-verbose:jni'; addOption: > '-verbose:gc'; addOption: '-verbose:class'." > Cursor execute showWhile: [JVM newWithSettings: jvmSettings]. > ---- > > Then the fun starts: > > ---- > | zfClass zipfile entries | > zfClass := JVM current findClass: #'java.util.zip.ZipFile'. > zipfile := zfClass new_String: 'JNIPort.jar'. > zipfile size. "--> answers an Integer" > entries := zipfile entries. > entries asAnEnumeration do: [:each | Transcript cr; print: each]. > ---- > > And now to the ugly little secret reason why I call it an alpha version: > > While everything seems to work, it is *much* slower than it used to be in > Pharo 1.2 with Alien. And when I compare it with VisualWorks, I wonder if > the Pharo version secretly does some additional work for SETI: > - Starting the JVM: > VisualWorks 0.7 seconds > Pharo 7 to 8 seconds > - A benchmark using some methods of java.io.BufferedReader and > java.util.StringTokenizer: > VisualWorks 114 milliseconds > Pharo 12 to 13 seconds > > I have not yet found the reason. The TimeProfiler is not very helpful, as > its 1 millisecond resolution is too coarse. It would be nice if it had > microsecond resolution. From the TimeProfiler results, it seems that calling > a JNI method with NativeBoost is extremely slow, but I don't trust those > profiles too much. > > So, give it a try, and if you find something useful concerning the bad > performance, please send me a message. > > Best regards, > Joachim Geidel > > > > > -- > View this message in context: http://forum.world.st/ANN-JNIPort-for-Pharo-3-0-alpha-tp4750750.html > Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com. > |
In reply to this post by Tudor Girba-2
Hi Doru,
Am 27.03.2014 um 13:07 schrieb Tudor Girba: > Just a question: is there a way to communicate with an already running VM that was started independently from Pharo? JNIPort uses the Java VM as a DLL / shared library, using the Java Invocation Interface which is part of the Java Native Interface. Actually, the Java VM *is* a shared library, and the java.exe is just a wrapper for starting it. When using JNIPort, the Pharo VM and the Java VM run in the same operating system process, and communicating with the Java VM just means calling functions from a library. Communicating with an already running Java VM is a totally different use case, as the Pharo VM and the Java VM run in two different operating system processes. To establish a communication between them, you would have to use an inter-process communication mechanism available in both of them. Pipes, TCP sockets, a messaging system, or running one of them as a server and the other as a client using a common protocol. That’s not what JNIPort is made for, and what you need for implementing it depends on the communication mechanism. HTH, Joachim |
Hi Joachim, Thanks for the answer. I suspected that it goes in this direction :). Now, the next question. I saw that it is possible to enable callbacks, but I could not find an example of how it works.
For example, could we model this: - in a JavaClassOriginator class we have a method like execute(JavaClassTargetInterface target) { target.m() } - In Pharo we create PharoClassTargetImplementation as an implementation of JavaClassTargetInterface - passing an instance of PharoClassTargetImplementation to JavaClassOriginator results in calling PharoClassTargetImplementation>>m ? Cheers,
Doru On Sat, Mar 29, 2014 at 11:32 AM, Joachim Geidel <[hidden email]> wrote: Hi Doru, "Every thing has its own flow"
|
Am 31.03.2014 um 21:25 schrieb Tudor Girba <[hidden email]>:
> Now, the next question. I saw that it is possible to enable callbacks, but I could not find an example of how it works. > > For example, could we model this: > > - in a JavaClassOriginator class we have a method like > execute(JavaClassTargetInterface target) { target.m() } > - In Pharo we create PharoClassTargetImplementation as an implementation of JavaClassTargetInterface > - passing an instance of PharoClassTargetImplementation to JavaClassOriginator results in calling PharoClassTargetImplementation>>m > > ? > > > Cheers, > Doru Hi Doru, it’s not completely straightforward, but also not really complicated. Chris Uppal’s JNIPort documentation contains examples, and you can also get some information from the test cases for callbacks. You will need the jniport.jar file which can be downloaded from http://jniport.wikispaces.com/Downloads (it is contained in JNIPort_Extras.zip). The Java classes needed for callback support have names starting with „Dolphin“ - they come directly from Chris’ implementation for Dolphin Smalltalk. A simple example: To set up sending a simple notification without parameters to a Smalltalk object, you need a Java class similar to the following: ----------------- package my.package; import org.metagnostic.jniport.*; public class MyClass { private static final Object notifyTag = new String("MyClass.sendNotification()“); public static Object notifyTag () { return notifyTag; } public void sendNotification() throws RequestNotHandedException { new DolphinNotification(notifyTag, this).send(); } } ----------------- You have to install the callback in Smalltalk: ----------------- NotificationHandler := … "the object which receives the notifications" classStatic := jvm findClass: 'my.package.MyClass'. jvm callbackRegistry setCallback: classStatic notifyTag "the tag can be an arbitrary String" handler: [:this :param | NotificationHandler handleNotificationFor: this]. ----------------- When you send sendNotification() to an instance of MyClass, NotificationHandler should receive the message #handleNotificationFor:. In the example code, the tag is a static variable in MyClass. Of course, you can also give every instance of MyClass its own tag. In this case, you have to set up a callback in Smalltalk for every tag which is used. The example is a short version of the test code for handling notifications in JNIPortCallbackNotificationsTest and the corresponding Java class org.metagnostic.jniport.test.regression.Notifications. The source code of this Java class is in the archive JNIPort-Tests.zip which is contained in JNIPort_Extras.zip. If you need an example for a notification which has parameters and where the Smalltalk code returns a result, have a look at JNIPortCallbackRequestsTest>>testCallbackWithOneParameterAndReturn and the corresponding Java class org.metagnostic.jniport.test.regression.Callbacks. In the Java class, you need something like this (copied from Callbacks and simplified) which enables a callback taking an int parameter and returning an instance of the Java class Integer: private static final Object callbackTag = new String(„callback with parameters"), // callback with return value public int callbackAndReturn(int i) throws Throwable { Object returnValue = new DolphinRequest(callbackTag, this, i).value(); return ((Integer) returnValue).intValue(); } A DolphinRequest is created with the tag which identifies the callback in Smalltalk, the object sending the callback (this), and an arbitrary number of parameters. For example, the following creates a DolphinRequest with two parameters: DolphinRequest dr = new DolphinRequest( s_callbackTag, this, new Integer(i), new Double(d)); Calling the value() method of the DolphinRequest sends the request to VisualWorks and returns the result of the Smalltalk callback block. In Smalltalk, you can create callbacks accepting parameters like this (see JNIPortCallbackRequestsTest): "No parameters" jvm callbackRegistry setCallback: classStatic callbackTag handler: [:this :param | self handleCallbackFor: this]. "One parameter" jvm callbackRegistry setCallback: classStatic callbackTag handler: [:this :param | self handleCallbackFor: this with: param]. "Arbitrary number of parameters" jvm callbackRegistry setCallback: classStatic callbackTag handler: [:this :params | self handleCallbackFor: this withArguments: params asArray]. If the callback has a result which should be passed from Smalltalk to Java, it must be a Java object. Here is an example from the test class (callbacksStatus is just an instance variable of JNIPortCallbackRequestsTest): handleCallbackFor: aJavaObject with: anotherJavaObject | jliClass | jliClass := jvm findClass: #'java.lang.Integer'. callbacksStatus addLast: (aJavaObject = instance). "identity test on the Java object" callbacksStatus addLast: ((anotherJavaObject static = jliClass) and: [anotherJavaObject intValue = 1]). ^ jliClass new_int: 15. "Do not return a Smalltalk Integer. It must be a Java Integer“ Johan Brichau’s JavaConnect had a more direct way of creating callbacks, using Java’s dynamic proxies. However, it had an open issue, as it relied on different Java objects having different identity hashes, which is not true in all cases. JNIPort’s implementation is relatively complicated because it had to take into account that the callbacks can be triggered from a different thread than the Smalltalk thread. In VisualWorks, that’s not a problem, as the VM handles this, and Pharo simply does not support callbacks from foreign threads. So, it could be simplified in JNIPort as well. Does this help? Joachim |
In reply to this post by Joachim Geidel
On 25 March 2014 19:59, Joachim Geidel <[hidden email]> wrote: Hi everybody, profiled a bit.. this: =================== | jvmSettings | "You can set the path to the Java VM library by editing the path as needed." JNIPortJNIInterface libraryFile: '/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Libraries/libclient.dylib' . "JNIPortJNIInterface libraryFile: 'C:\Programme\Java\jre7\bin\client\jvm.dll' ." jvmSettings := JVMSettings new. jvmSettings usesGhosts: true. jvmSettings supportsCallbacks: true. jvmSettings jniPortSettings useJNIHelperLibrary: false. jvmSettings ghostClassSettings retainMethodSource: true. JVM newWithSettings: jvmSettings ============== and got this: **Leaves** 22.8% {925ms} StaticJavaLangClassLoader(JavaStatic)>>knownJavaSubclasses 19.9% {804ms} JVMWithCallbacks(JVM)>>findClassFor: 5.9% {240ms} OrderedCollection>>do: 5.1% {206ms} Metaclass(Behavior)>>registerLocalSelector: 3.0% {120ms} JNIPortJNIEnv>>return:onException: 2.2% {91ms} CompiledMethod>>methodClass: 2.2% {88ms} OrderedCollection>>add: 2.1% {84ms} Array(SequenceableCollection)>>do: 2.0% {81ms} WordArray class(Behavior)>>new: 2.0% {79ms} WeakArray class>>finalizationProcess 1.3% {52ms} JNIPortJNIInterfaceForMacOSX(JNIPortJNIInterface)>>checkReturnCode: 1.1% {46ms} Heap>>updateObjectIndex: ------------- findClassFor: aJNIObject "private -- find/make the JavaStatic that will wrap aJNIObject" "we have to ensure that the ref is converted to global if necessary *before* the class lookup, since that might take considerable time during which callbacks might happen (especially if new ghost classes have to be constructed). Hence we convert it now, even though we will 'convert' again later (in #own:)" "NB: this will #become: the JNIObject into a global ref if necessary" self convertToGlobal: aJNIObject. ^ super findClassFor: aJNIObject. ---------- so, without going deep into implementation details, just by reading comment i can already see potential bottleneck - using #become: operation which is veery slow. also, by tracking senders of #knownJavaSubclasses , i see there's also uses of #become operation.. So, my first verdict is: - excessive use of #become ==> slow performance :) (sure, i have no idea if you can avoid using become or not, but my bet this is main reason of low performance) -- Best regards, Igor Stasenko. |
In reply to this post by Joachim Geidel
Joachim
would you be interested to write a little Chpater for the Pharo for the entreprise book? I can help. Ste On 05 Apr 2014, at 14:32, Joachim Geidel <[hidden email]> wrote: > Am 31.03.2014 um 21:25 schrieb Tudor Girba <[hidden email]>: >> Now, the next question. I saw that it is possible to enable callbacks, but I could not find an example of how it works. >> >> For example, could we model this: >> >> - in a JavaClassOriginator class we have a method like >> execute(JavaClassTargetInterface target) { target.m() } >> - In Pharo we create PharoClassTargetImplementation as an implementation of JavaClassTargetInterface >> - passing an instance of PharoClassTargetImplementation to JavaClassOriginator results in calling PharoClassTargetImplementation>>m >> >> ? >> >> >> Cheers, >> Doru > > Hi Doru, > > it’s not completely straightforward, but also not really complicated. Chris Uppal’s JNIPort documentation contains examples, and you can also get some information from the test cases for callbacks. You will need the jniport.jar file which can be downloaded from http://jniport.wikispaces.com/Downloads (it is contained in JNIPort_Extras.zip). The Java classes needed for callback support have names starting with „Dolphin“ - they come directly from Chris’ implementation for Dolphin Smalltalk. > > A simple example: To set up sending a simple notification without parameters to a Smalltalk object, you need a Java class similar to the following: > > ----------------- > package my.package; > import org.metagnostic.jniport.*; > public class MyClass > { > private static final Object notifyTag = new String("MyClass.sendNotification()“); > public static Object notifyTag () { return notifyTag; } > public void sendNotification() > throws RequestNotHandedException > { > new DolphinNotification(notifyTag, this).send(); > } > } > ----------------- > > You have to install the callback in Smalltalk: > > ----------------- > NotificationHandler := … "the object which receives the notifications" > classStatic := jvm findClass: 'my.package.MyClass'. > jvm callbackRegistry > setCallback: classStatic notifyTag "the tag can be an arbitrary String" > handler: [:this :param | NotificationHandler handleNotificationFor: this]. > ----------------- > > When you send sendNotification() to an instance of MyClass, NotificationHandler should receive the message #handleNotificationFor:. > > In the example code, the tag is a static variable in MyClass. Of course, you can also give every instance of MyClass its own tag. In this case, you have to set up a callback in Smalltalk for every tag which is used. > > The example is a short version of the test code for handling notifications in JNIPortCallbackNotificationsTest and the corresponding Java class org.metagnostic.jniport.test.regression.Notifications. The source code of this Java class is in the archive JNIPort-Tests.zip which is contained in JNIPort_Extras.zip. > > If you need an example for a notification which has parameters and where the Smalltalk code returns a result, have a look at JNIPortCallbackRequestsTest>>testCallbackWithOneParameterAndReturn and the corresponding Java class org.metagnostic.jniport.test.regression.Callbacks. > > In the Java class, you need something like this (copied from Callbacks and simplified) which enables a callback taking an int parameter and returning an instance of the Java class Integer: > > private static final Object callbackTag = new String(„callback with parameters"), > > // callback with return value > public int callbackAndReturn(int i) > throws Throwable > { > Object returnValue = new DolphinRequest(callbackTag, this, i).value(); > return ((Integer) returnValue).intValue(); > } > > A DolphinRequest is created with the tag which identifies the callback in Smalltalk, the object sending the callback (this), and an arbitrary number of parameters. For example, the following creates a DolphinRequest with two parameters: > > DolphinRequest dr = new DolphinRequest( > s_callbackTag, > this, > new Integer(i), > new Double(d)); > > Calling the value() method of the DolphinRequest sends the request to VisualWorks and returns the result of the Smalltalk callback block. > > In Smalltalk, you can create callbacks accepting parameters like this (see JNIPortCallbackRequestsTest): > > "No parameters" > jvm callbackRegistry setCallback: classStatic callbackTag > handler: [:this :param | self handleCallbackFor: this]. > > "One parameter" > jvm callbackRegistry setCallback: classStatic callbackTag > handler: [:this :param | self handleCallbackFor: this with: param]. > > "Arbitrary number of parameters" > jvm callbackRegistry setCallback: classStatic callbackTag > handler: [:this :params | self handleCallbackFor: this withArguments: params asArray]. > > If the callback has a result which should be passed from Smalltalk to Java, it must be a Java object. Here is an example from the test class (callbacksStatus is just an instance variable of JNIPortCallbackRequestsTest): > > handleCallbackFor: aJavaObject with: anotherJavaObject > > | jliClass | > > jliClass := jvm findClass: #'java.lang.Integer'. > > callbacksStatus addLast: (aJavaObject = instance). "identity test on the Java object" > callbacksStatus addLast: ((anotherJavaObject static = jliClass) and: [anotherJavaObject intValue = 1]). > > ^ jliClass new_int: 15. "Do not return a Smalltalk Integer. It must be a Java Integer“ > > Johan Brichau’s JavaConnect had a more direct way of creating callbacks, using Java’s dynamic proxies. However, it had an open issue, as it relied on different Java objects having different identity hashes, which is not true in all cases. JNIPort’s implementation is relatively complicated because it had to take into account that the callbacks can be triggered from a different thread than the Smalltalk thread. In VisualWorks, that’s not a problem, as the VM handles this, and Pharo simply does not support callbacks from foreign threads. So, it could be simplified in JNIPort as well. > > Does this help? > Joachim > > |
In reply to this post by Igor Stasenko
Hi Igor,
thanks a lot, your analysis is correct, and it helped. Am 05.04.2014 um 17:05 schrieb Igor Stasenko: > so, without going deep into implementation details, just by reading comment > i can already see potential bottleneck - using #become: operation which is veery slow. Yes, #become: is *very* slow in Pharo. No problem at all in VisualWorks or Dolphin Smalltalk or Pharo 1.2, though! Time millisecondsToRun: [1000 timesRepeat: [String new become: String new ]] Pharo 3.0 (and also in 2.0 and 1.4): 3948 3495 2891 2532 3162 Pharo 1.2 with the Squeak 4.2.5beta1U VM: 90 89 82 90 72 VisualWorks 7.9.1: note that this is measured in microseconds: Time microsecondsToRun: [1000 timesRepeat: [String new become: String new ]] 211 208 222 212 208 That’s a factor of more than 10.000! IMHO, someone should have a look at the primitives involved. > also, by tracking senders of #knownJavaSubclasses , i see there's also uses of #become operation.. > > So, my first verdict is: > - excessive use of #become ==> slow performance > :) > > (sure, i have no idea if you can avoid using become or not, but my bet this is main reason of low performance) Well, given the measurements above, I wouldn’t say that the reason is excessive use of #become:, but the extremely slow implementation of #become: in Pharo since 1.4. However, I managed to remove the need for sending #become: in #findClassFor:, but not in the second place. This reduces the time to start a Java VM to 5-6 seconds on my aged machine (2.4 GHz Intel Core 2 Duo from early 2008). The benchmark which I mentioned in my announcement and which took 12 seconds came down to less than 0.2 seconds. That's a little more than the 0.11 seconds in VisualWorks, but at least in the same order of magnitude. Thanks again! Joachim |
On 05 Apr 2014, at 23:35, Joachim Geidel <[hidden email]> wrote: > Hi Igor, > > thanks a lot, your analysis is correct, and it helped. > > Am 05.04.2014 um 17:05 schrieb Igor Stasenko: >> so, without going deep into implementation details, just by reading comment >> i can already see potential bottleneck - using #become: operation which is veery slow. > > Yes, #become: is *very* slow in Pharo. No problem at all in VisualWorks or Dolphin Smalltalk or Pharo 1.2, though! > > Time millisecondsToRun: [1000 timesRepeat: [String new become: String new ]] > > Pharo 3.0 (and also in 2.0 and 1.4): > 3948 3495 2891 2532 3162 > > Pharo 1.2 with the Squeak 4.2.5beta1U VM: > 90 89 82 90 72 these numbers are strange. Because mariano used a lot become: and he did not report a factor like that. Thanks for reporting them. > > VisualWorks 7.9.1: note that this is measured in microseconds: > Time microsecondsToRun: [1000 timesRepeat: [String new become: String new ]] > 211 208 222 212 208 this will be fixed with spur and body. > > That’s a factor of more than 10.000! IMHO, someone should have a look at the primitives involved. > >> also, by tracking senders of #knownJavaSubclasses , i see there's also uses of #become operation.. >> >> So, my first verdict is: >> - excessive use of #become ==> slow performance >> :) >> >> (sure, i have no idea if you can avoid using become or not, but my bet this is main reason of low performance) > > Well, given the measurements above, I wouldn’t say that the reason is excessive use of #become:, but the extremely slow implementation of #become: in Pharo since 1.4. However, I managed to remove the need for sending #become: in #findClassFor:, but not in the second place. This reduces the time to start a Java VM to 5-6 seconds on my aged machine (2.4 GHz Intel Core 2 Duo from early 2008). The benchmark which I mentioned in my announcement and which took 12 seconds came down to less than 0.2 seconds. That's a little more than the 0.11 seconds in VisualWorks, but at least in the same order of magnitude. > > Thanks again! > Joachim > > |
In reply to this post by Joachim Geidel
Hi Joachim, Thanks for the explanations. It looks complicated but I am very happy it is possible! I will give a try next week and get back to you. Cheers, Doru On Sat, Apr 5, 2014 at 2:32 PM, Joachim Geidel <[hidden email]> wrote: Am 31.03.2014 um 21:25 schrieb Tudor Girba <[hidden email]>: "Every thing has its own flow"
|
BTW, I have published an updated version of JNIPort which partly resolves the performance issue caused by using #become:.
Joachim -- My OpenPGP Key: 29F88108 available from hkp://wwwkeys.de.pgp.net |
In reply to this post by Joachim Geidel
On 05 Apr 2014, at 23:35, Joachim Geidel <[hidden email]> wrote: > Yes, #become: is *very* slow in Pharo. No problem at all in VisualWorks or Dolphin Smalltalk or Pharo 1.2, though! > > Time millisecondsToRun: [1000 timesRepeat: [String new become: String new ]] > > Pharo 3.0 (and also in 2.0 and 1.4): > 3948 3495 2891 2532 3162 > > Pharo 1.2 with the Squeak 4.2.5beta1U VM: > 90 89 82 90 72 > > VisualWorks 7.9.1: note that this is measured in microseconds: > Time microsecondsToRun: [1000 timesRepeat: [String new become: String new ]] > 211 208 222 212 208 Yes, this is an extreme difference. On my machine, the above takes between 1 and 2 seconds in Pharo 3 (Pharo VM) and Squeak 4.5 (Cog VM), while VW 7.1 takes just 100 to 200 microseconds. Normally, #become: has to iterate over all memory, but this benchmark is a bit special, since it involves two new objects, maybe VW's memory manager somehow optimises this away. Sven |
In VW there is the concept of object bodies and head now we will eventually have that with Spur. Maybe clement explained it somewhere in his blog (I understood the point once and of course I forgot).
Now I wonder what changed so much between 1.2 and 3.0 > >> Yes, #become: is *very* slow in Pharo. No problem at all in VisualWorks or Dolphin Smalltalk or Pharo 1.2, though! >> >> Time millisecondsToRun: [1000 timesRepeat: [String new become: String new ]] >> >> Pharo 3.0 (and also in 2.0 and 1.4): >> 3948 3495 2891 2532 3162 >> >> Pharo 1.2 with the Squeak 4.2.5beta1U VM: >> 90 89 82 90 72 >> >> VisualWorks 7.9.1: note that this is measured in microseconds: >> Time microsecondsToRun: [1000 timesRepeat: [String new become: String new ]] >> 211 208 222 212 208 > > Yes, this is an extreme difference. > > On my machine, the above takes between 1 and 2 seconds in Pharo 3 (Pharo VM) and Squeak 4.5 (Cog VM), while VW 7.1 takes just 100 to 200 microseconds. > > Normally, #become: has to iterate over all memory, but this benchmark is a bit special, since it involves two new objects, maybe VW's memory manager somehow optimises this away. > > Sven |
In reply to this post by Joachim Geidel
Hi everbody,
I have published an update of JNIPort 3.0 alpha at SmalltalkHub. Changes from the previous version: - The Java classes needed for calling back from Java into Smalltalk and for executing the regression tests have been updated. I have shortened the Java package name from org.metagnostic.jniport to just jniport, and renamed those classes which had „Dolphin“ in their class name. JNIPort has been developed for Dolphin Smalltalk by Chris Uppal, but it is available for VisualWorks and Pharo, too. Therefore I decided that the references to Dolphin are obsolete. Some methods have been renamed for the same reason. - The class files in the jars have been compiled with Java 1.6. I don’t think that the old class files compiled with Java 1.4 (or earlier?) would be a problem, but still... (and I don’t have anything earlier than Java 1.6 anyway). - Several Smalltalk wrapper classes and methods for the renamed Java classes and methods have been renamed as well. The new names should be more obvious than the old ones. - The performance problem from the first alpha version has been partly solved. The rest is caused by longer execution times for #become: in Pharo - I can’t do anything about it for the moment. If you want to use the new version, download it from SmalltalkHub, and get the archive JNIPort30-Extras.zip containing the updated jar files from the JNIPort wiki (see below). The new jar files are called JNIPort30.jar and JNIPort30-Tests.jar instead of JNIPort.jar and JNIPort-Tests.jar, such that you can use the old and the new version in parallel, if there is any need for it. For those who don’t know what JNIPort is: > JNIPort is a Smalltalk library which allows Java code to be invoked from > Smalltalk. It acts as a bridge between the world of Smalltalk objects and a > Java Virtual Machine (JVM) where Java code is executing. > > If you want to try it, load it via the ConfigurationOfJNIPort. I hope this > works. If it does not, load the packages individually in the order in which > they appear in the ConfigurationOfJNIPort. > http://www.smalltalkhub.com/#!/~JNIPort/JNIPort > The next step is to read the documentation at > http://jniport.wikispaces.com/ > Otherwise, you won't know what to do next. ;-) > Download the extra files from the download page at Wikispaces, you will need > two jar files from there. Issues: - Callbacks from „foreign“ threads, i.e. threads in the Java VM other than the one in which the Smalltalk VM runs, should be handled by the callback queueing mechanism implemented by the classes in JNIPort30.jar. This mechanism ensures that the callback into Smalltalk is executed in the Smalltalk thread. This works in VisualWorks and Dolphin, but in Pharo it leads to a crash of the VM when executing the Smalltalk callback block which is installed as a native method in Java space. I have as yet no idea why this does not work. - The JNIPort wiki lacks documentation about the Pharo version and a recipe for using callbacks. The latter is hidden in the original documentation by Chris Uppal, which can be downloaded from the wiki. - No tool support in Pharo yet. In VisualWorks, there is a settings editor, storage of settings in a „registry“ (on Windows, this is the „real“ registry), and there are menu items in the system browser for generating Smalltalk wrapper classes for Java classes. None of this exists for Pharo yet. Best regards, Joachim Geidel |
In reply to this post by pharo4Stef@free.fr
2014-04-08 16:04 GMT-03:00 Pharo4Stef <[hidden email]>:
> In VW there is the concept of object bodies and head now we will eventually have that with Spur. Maybe clement explained it somewhere in his blog (I understood the point once and of course I forgot). > > Now I wonder what changed so much between 1.2 and 3.0 Dolphin's #become: uses the same concept. E.g. a become: b Doesn't switch references, but instead "body" pointers in a and b. It is, a becomes b, and b becomes a. i Time microsecondsToRun: [1000 timesRepeat: [String new become: String new ]]. 684 898 663 812 665 Also, there is a #oneWayBecome: which DOES an object table scan, and makes all references to the receiver point to the argument object (it is, a becomes b, and not the other way around). This gets slow directly proportional to the size of the OT. Time microsecondsToRun: [1000 timesRepeat: [String new become: String new ]]. 10936532 Most proxies and stubs implementations in Dolphin uses #become: because it is instantaneous, and the "old" object (b in this) eventually gets garbage collected. Regards. Esteban A. Maringolo |
Free forum by Nabble | Edit this page |