forax's Blog
Talks and Conferences
Hi all,
I have three talks scheduled at the beginning of this year.
The first one at FOSDEM in the Free Java Devroom is titled Java Lambdas for Hackers. It will explore the details of what can be the implementation of the lambda in Java 8
The agenda of the Free Java sessions this year is great, a kind of perfect mix between old friends and newcomers.
http://fosdem.org/2012/schedule/track/free_java_devroom
The two other talks are in French.
The first one, Java.next() at the Paris Jug hosted by Julien Ponge and Alexis Moussine-Pouchkine, I will also talk about lambdas but more from the user point of view that from the VM side.
http://www.parisjug.org/xwiki/bin/view/Main/WebHome
And the last talk will be at Devoxx France
http://devoxx.fr/display/FR12/Speakers
where I will show how you can implement your dynamic language on top of the JVM.
C U soon,
Rémi
- Login or register to post comments
- Printer-friendly version
- forax's blog
- 1133 reads
JSR 292 Goodness: Almost static final field.
Sometimes I want a express that a static field is unlikely to change, so the VM should consider it has a constant, but it may changed. And because we all live in a threaded world, if the static field is changed I want that all threads that want to read the field value to notice that the value has changed like a volatile field. Take by example the default Locale that you can obtain using Locale.getDefault(), it's unlikely that this value will change, but it may change, there is a Locale.setDefault().
The idea is that default Locale is mostly a constant, so when a code that call getDefault is JITed, the VM should consider it as a constant i.e. the JIT should replace the call to getDefault() by a direct reference to default Locale object. And if a code calls setDefault(), the VM should drop the JITed code and re-optimize it later.
Suppose that you have a magic class named AlmostFinalValue, a value wrapped in this class acts as a constant for the VM, so a code that call getDefault will be replaced by a constant by the VM. And if setValue() is called, the VM will deoptimize the code that use getDefault if the code was JITed and change the value of the default locale. If the code still use getDefault, it will be re-optimized by with the new constant.
Note that this deopt/reopt takes time so it's not a good idea to use an AlmostFinalValue for storing a value which changes frequently.
public class Locale {
...
private static final AlmostFinalValue<Locale> DEFAULT_LOCALE =
new AlmostFinalValue<Locale>() {
@Override
protected Locale initialValue() {
return new Locale("default");
}
};
private static final MethodHandle DEFAULT_LOCALE_GETTER =
DEFAULT_LOCALE.createGetter();
public static Locale getDefault() {
try {
return (Locale)(Object)DEFAULT_LOCALE_GETTER.invokeExact();
} catch (Throwable e) {
throw new AssertionError(e.getMessage(), e);
}
}
public static void setDefault(Locale locale) {
DEFAULT_LOCALE.setValue(locale);
}
}
How AlmostFinalValue works ?
All modern VMs, V8-cranksaft (Javascript/Google), IonMonkey (Javascript/Mozilla), Hostspot (Java/Oracle), J9 (Java/IBM) are able to de-optimize a JITed code if an optimistic assumption doesn't hold anymore.
In Java, we go a little bit further, JSR 292, foolishly you may say, gives access to VM optimization/deoptimization to the mass* by providing a silver bullet named the SwitchPoint.
A SwitchPoint can be seen as a volatile boolean which is true by default and that can be switched off once. On a SwitchPoint, you can create a guard, an 'if' statement seen as a method handle** that takes two method handles as parameter and execute the former if the boolean is true or the later otherwise. The JIT optimizes a SwitchPoint by considering that the boolean value is unlinkely to change so it will remove the guard to always call the first method handle and if the SwitchPoint is switch off, the VM will deoptimize the JITed code. In fact, in Hotspot, this is done lazily the code is just marked as must be dropped when it will be called. So later, when code will be called again, the VM will call the second method handle. The second method is the slow path, the path which is infrequenly called when there is an update, it get the new value, see it as a constant method handle and protect it with a new SwitchPoint. Then this new method handle is installed as the new target of a mutable method handle (a MutableCallSite + a dynamic invoker in fact). So the new constant is installed and will be called when the DEFAULT_LOCAL_GETTER will be called. The method hande DEFAULT_LOCAL_GETTER is called using invokeExact which calls the method handle with no runtime conversion. As a game, I will offer a free beer at FOSDEM or Devoxx France to the first that will explain why you need the two casts in front of invokeExact ***.
The full code is here, and works well with latest jdk8 and jdk7u2. With jdk7, the SwitchPoint was not fully optimized so the VM will fail to transform the default local value to a constant.
* In fact, JSR 292 was intended to be used by developers of dynamic language runtime (Jython, JRuby, Groovy, etc) but when you provide a golden hammer, everybody will want to use it.
** A MethodHandle is runtime typed function pointers.
*** People that have written the JSR 292 spec can't participate, sorry John, Dan and Fredrik :)
- Printer-friendly version
- forax's blog
- 7275 reads
AsyncSwing
At last JVM Summit, I've followed the presentation done by Mads Torgersen about Asynchronous Programming in .NET with the feeeling that while the idea is great,
avoid to explicitly write async callback like by example you do with node.js and let the compiler twist the code for you, but the implementation using two keywords async and await is not good.
The main issue is that the async keyword needs to be declared if a method containing an await thus needs to be propagated back to all methods that call this method recursively. It has exactly the same problem that the checked exceptions in Java, async is a part of the method signature so you can't refactor easily a method in order to add async because you have to back propagate it to all calling methods, if a method overrides a virtual method, you have to declare the virtual method async, you have also to create async delegate to use await in lambda, etc.
Java implementation of generics is done by the compiler, not by the VM, because of that the language is crippled of small stupid limitations that C# doesn't have because generics are reified by the VM in C# and not in Java. .Net guys are doing exactly same mistake here, thinking that the compiler translation is good enough even if it introduces some small glitches like the semantics of the async keyword.
This decision to only rely on the compiler to do the CPS transformation is beyond understanding because mechanism to implement this transformation in the VM (coroutine, continuation) are known since a long time.
As a proof, I propose to implement AsyncSwing a small framework that allows everyone to write Swing applications with tons of IO in the callback of the UI. This framework will trap IOs (the Java 7 File API is pluggable) and generates async IO instead, relying on coroutine (Lukas Stadler has implemented coroutine in the JVM, hoorah) to stop the current callback without stopping the UI event loop.
Here is an example of code that read a big file 'dict.txt' and append it to a text area.
UIApplication.start(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("test");
final JTextArea area = new JTextArea();
final JButton button = new JButton("load dictionnary");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent _) {
button.setEnabled(false);
Path path = Paths.get("dict.txt");
try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.ISO_8859_1)) {
String line;
while ((line=reader.readLine()) != null) {
area.append(line + '\n');
}
} catch (IOException e) {
e.printStackTrace();
}
button.setEnabled(true);
}
});
Container content = frame.getContentPane();
content.add(BorderLayout.NORTH, button);
content.add(BorderLayout.CENTER, new JScrollPane(area));
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(400, 300);
frame.setVisible(true);
}
});
If you want to test it by yourself, the VM+coroutine is avalaible on Lukas web page and the source code of the AsyncSwing is as attachment of this blog entry.
Known limitations:
The current implementation only trap newInputStream() on a Path and generates one UI event for each async call result instead of trying to reuse one event for several results like the SwingWorker does.
If you're at JavaOne, Lukas will give a talk about JVM coroutine internals, don't miss it !
cheers,
Rémi
| Attachment | Size |
|---|---|
| async-coro-io.zip | 707.53 KB |
- Login or register to post comments
- Printer-friendly version
- forax's blog
- 3365 reads
Comments
It appears you didn't understand (or Mads didn't explain ...
by gafter - 2011-10-01 16:33
It appears you didn't understand (or Mads didn't explain well) how the async feature works in C# (and VB). The async keyword isn't part of the method's signature at all. Any method that returns a Task or Task<T> (think of it as a "future") can be declared with the async keyword if you want to use the await keyword in its body. Or it can be declared without the async keyword if you want to manage the asynchrony yourself. The caller can't tell the difference. The "await" unary operator turns a Task<T> into a T by waiting, asynchronously, for the result to become available. But if you don't want that compiler magic, you can provide a lambda to be invoked when the result of a Task<T> is done.
isn't this can be achieved by using InvokeAndLater() an ...
by javinpaul - 2011-10-03 06:38
isn't this can be achieved by using InvokeAndLater() an asynchronous call, Can you explain what are the advantage it has over simple invokeAndWait Call ?
Hi Paul, invokeLater is not an asynchronous call because it ...
by forax - 2011-10-03 07:38
Hi Paul,
invokeLater is not an asynchronous call because it run in the EDT (Event Dispatch Thread, the thread used to repaint the screen),
it's more a kind of delayed call. Also you can't use invokeAndWait because you can't block the EDT to wait until the EDT run the code.
To take a simpler example, let's say we want to read something on the disk in an UI callback, if you write:
InputStream input = Files.newInputStream(path); input.read(buffer); buton.setEnable(true);
You block the EDT until the read finish, so the application is freezed until the read finish.
If you want to do the same asynchronously by hand, you have to write something like:
EventQueue.invokeLater(new Runnable() {
public void run() {
AsyncronousFileChannel channel = AsyncronousFileChannel.open(path); // let say this can not block
channel.read(buffer, 0, new CompletionHandler<Integer, Object>() {
public void completed(Integer result, Object attachment) {
EventQueue.invokeLater(new Runnable() { // read completed
public void run() { // go into the EDT to update the UI
buton.setEnable(true);
...
}
});
}
... // manage exceptions here
});
}
});
The advantage of using some coroutines is that you can keep the stack states when the async operation is under its way
and jump back when the async call finished.
Rémi
Hi Neal, I'm sure now that Mads said that T was transformed ...
by forax - 2011-10-02 18:22
Hi Neal,
I'm sure now that Mads said that T was transformed to a Task<T>, I've forgotten that point, async is part of the implementation, not a part of the signature.
Anyway, because you have transformed the callee to return a Task you have to transform all the callers to use await (or as you said write the boilerplate required to create a Task by yourself),
then you have to tag all caller implementations with async and so on along all possible stack traces.
So if by example if you want to use a high order methods like map or reduce (or LinQ expressions) in the middle, you have to provide two versions, the one that use a function that return a value and the one that use a function that returns a Task.
There is no reason to create an async keyword if await is handled by the VM, async+Task are artefact only required because the .Net team choose to use a compiler translation,
This decision has two unfortunate consequences, it burden developers because async functions can't be used everywhere in the language without doing some code duplications and it increases the technical debt of C# exactly like non-reified types increase the technical debt of Java.
cheers,
Rémi
Hotspot loves PHP.reboot
I've just compiled the hotspot (server 64bits) using the hotspot-comp workspace of hotspot express
http://hg.openjdk.java.net/hsx/hotspot-comp/hotspot/
Here are the result (average of 8 best run on 10) when running PHP.reboot (my own toy language) on fibonacci function,
(-server is the server VM of jdk7, -hsx is server VM of upcoming jdk7 update)
Java: java -server bigfibo 4.45 s java -hsx bigfibo 4.44 s PHP.reboot (no type annotation) phpr.sh -server bigfibo 22.72 s phpr.sh -hsx bigfibo 13.61 s PHP.reboot (type specialization) phpr.sh -server bigfibo 11.09 s phpr.sh -hsx bigfibo 8.06 s PHP.reboot (user defined type annotation) phpr.sh -server bigfibo2 6.96 s phpr.sh -hsx bigfibo2 4.21 s
PHP.reboot is an hybrid runtime, it starts with an interpreter that walks the AST (really slow) and then compile to bytecode.
The first test is with no type information provided by the user, so all variables are objects and invokedynamic is used for the operations, the comparison and for function calls. As you see, there is a huge speedup.
The second test enables a flag that ask the runtime to try to specialize the function at runtime. Because the algorithm used is a fast-forward typechecker, the parameter of fibo is san pecialized as int but the return type is still an object (because fibo is recursive). So basically here, invokedynamic is used for the function calls and the '+' between the results of the function calls. This '+' is a nasty one because the two parameters are objects, so it requires a double guards. You can see the speedup is nice too.
The third test uses a function fibo that declares the parameter type and return type of fibo as int, so only the function calls are done using invokedynamic. You can also see the speedup and weirdly it's now faster than Java (not a lot if you compare the value but don't forget that PHP.reboot starts in interpreter mode) so it's clearly faster. I will take a look to the inlining tree to try to understand why, it's maybe because fibo is a recursive call or because using an invokedynamic which is resolved as an invokestatic enables more inlining than just an invokestatic.
John, Christian, Tom and all the others of the hotspot-comp team,
you make my day
cheers,
Rémi
- Login or register to post comments
- Printer-friendly version
- forax's blog
- 2522 reads
ASM 4 RC1 released
I'm proud with Eric and Eugene to announce the release of ASM4 RC1.
This release is available to download here:
http://forge.ow2.org/projects/asm/
So what's new ?
- Full support of Java 7.
Because Java 7 introduces several changes in the bytecode format,
all other versions of ASM aren't able to deal with this new format.
That why I strongly recommend you to use ASM4.
- Overhaul of the support of invokedynamic to support the new encoding
and bootstrap method arguments.
Practically, this means that invokedynamic doesn't trigger a call to
MethodVisitor.visitMethodInsn but use a specific new method:
visitInvokeDynamicInsn(String name, String desc, MethodHandle bsm, Object... args)
- Two new constants org.objectweb.asm.MethodHandle and org.objectweb.asm.MethodType
can be used either in visitLdcInsn() or as last arguments of visitInvokeDynamicInsn.
org.objectweb.asm.MethodHandle encodes a constant method handle and
org.objectweb.asm.MethodType encodes a constant method type.
Here is the snippet of how generate an instruction invokedynamic foo()V that will
call as bootstrap method bootstrapName with two bootstrap constants "bar" and 3.
MethodVisitor mv = ...MethodHandle bootstrap = newMethodHandle(Opcodes.MH_GETSTATIC, "BoostrapClass", "boostrapName","(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;");mv.visitInvokeDynamicInsn("foo", "()V", bootstrap, "bar", 3);
- ASM4 API has been generified to use generics and varargs
but almost all jars are still small and 1.2 compatible.
In fact, there is a trick, the API is 1.5 compatible but the bytecode is downgraded
to be 1.2 compatible using a bytecode rewriter (our own dog food).
To use the 1.5 compatible API you have to compile using all/asm-debug-all-*.jar
which is 1.5 compatible and also contains the debug information.
When running you can use all/asm-all-*.jar or several asm-*jar depending on your usage.
Except all/asm-debug-all-*.jar all other jars are 1.2 compatible and optimized.
If you want debug information at runtime you have to use all/asm-debug-all-*.jar.
ASM4 API has been generified to be as compatible as possible with 3.3 API.
So some methods now return an array of parametrized type which are not type safe
(not less than before in fact) if you store something in it.
The other solution should have been to use a List instead of an array
but this introduces too many non-compatible changes.
The package org.asm.tree.analysis is now fully typesafe but during that process,
we have changed some signatures in Interpreter subtypes so if your code
uses an ad-hoc interpreter it has to be updated.
cheers,
Rémi
- Login or register to post comments
- Printer-friendly version
- forax's blog
- 1329 reads
Comments
<p>My 2 cents: dump backwards compatibility, especially ...
by cowwoc - 2011-04-20 17:36
My 2 cents: dump backwards compatibility, especially where Java 1.2 is concerned. Who the heck is using a version of Java that was released over 13 years ago?!
If you have to drop some backwards compatibility to add type-safety, do so.
Hi Gili, by 1.2 compatibility, I mean 1.2, 1.3 and ...
by forax - 2011-04-20 18:18
Hi Gili, by 1.2 compatibility, I mean 1.2, 1.3 and 1.4. Unfortunately, there is still to many WebSphere 5.1/6 used nowadays.Also, using List instead of array has some performance implications even if the jdk7 shows some incredible performance improvement. Rémi
Fixing The Inlining “Problem” - A prototype
Today, I've found the time to code a prototype of the solution proposed by Cliff Click for Fixing the inlining problem, i.e. to solve the problem of the inability of the Hotspot JITs to inline lambda calls.
In the example below, I want to filter a list depending on a predicate which is specified as a method handle (read a function pointer if you don't know the new java.lang.invoke API that will be introduced in Java 7).
private static List<String> filter(List<String> list, MethodHandle predicate) throws Throwable {
ArrayList<String> filteredList = new ArrayList<String>();
for(String element: list) {
if ((boolean)predicate.invokeExact(element)) {
filteredList.add(element);
}
}
return filteredList;
}
The problem is that apart if the method filter is always used with the same lambda,
the call predicate.invokeExact() will never be inlined because Hotspot doesn't trace calls
between several methods.
Cliff proposal is that the JIT should emit several specialized versions of filter; one by lambda;
with a dispatch table at the top of the method that will select the specialized code
depending on the lambda taken as parameter.
So I've created a Java Agent that detects this kind of method, installs a ConcurentHashMap as dispatch table on top of the method and specializes the code if a new lambda is found. The method handle invocations in the specialized code are transformed to plain old calls that are later inlined by the JIT.
Detection: the agent looks for methods that use method handles as parameter, after it run a simple
control-flow analysis (thanks to ASM analysis framework) to crawle among the variable aliases
to find if the method handles are invoked.
Specialization: when a new lambda is discovered, the agent duplicate the bytecode of the function
and replace each method handle calls find by the control flow analysis run previously
to the corresponding direct/virtual call.
Now a small benchmark, with a list of 1 000 000 strings, with a stock 1.7 VM (b136) and with the same VM plus the Java agent. The full source are available at the bottom of this entry.
java -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic Speed elapsed 2260394574 ns
java -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic -javaagent:lib/jsr335-lambda-optimizer.jar Speed elapsed 1244597693 ns
Ok, it's a micro benchmark but I think it clearly indicates that can be a really useful optimization.
cheers,
Rémi
* No, I have not modified the JIT itself, call me lazy if you want but I tend to avoid to touch a line of C++ if possible :)
| Attachment | Size |
|---|---|
| lambda-optimizer.zip | 694.93 KB |
- Login or register to post comments
- Printer-friendly version
- forax's blog
- 1032 reads
Comments
<p>The result is somehow expected, isn't it? ;)</p> <p>Your ...
by jodeleit - 2011-04-14 09:04
The result is somehow expected, isn't it? ;)
Your solution is a bit like "C++ templates on a usage base". Didn't read your source yet, but i suppose you've implemented a memory leak (imagine many, ok, really many, different function pointers getting in).
But a nice finger exercise.
<p>> The result is somehow expected, isn't it? ...
by forax - 2011-04-14 10:13
> The result is somehow expected, isn't it? ;)
Until VMs implements something like that, yes.
> Your solution is a bit like "C++ templates on a usage base". Didn't read your source yet, but i suppose you've implemented a memory leak (imagine many, ok, really many, different function pointers getting in).
Yes, there is a big leak, the dispatch table is a concurrent hashmap with no eviction policy at all.
Let say this is left as an exercise for the reader :)
> But a nice finger exercise.
Thank you !
Rémi
Sweet Home Alabama
As part of my sabbatical, in April, I will be in Alabama at UAB as invited professor.
A breaking change for me. No kid, no wife for a whole month.
In one way, I will have time to do some research I had to delay due to lack of time. But in another way,
I worry to have too much free time :)
So where should I go, what should I see, what should I do during this month in Alabama ?
Rémi
- Login or register to post comments
- Printer-friendly version
- forax's blog
- 466 reads
Comments
Man, never worry to have too
by fabriziogiudici - 2011-03-04 04:50
Man, never worry to have too much free time
(ok, it's actually a useless comment, just to test commenting on the new java.net).
JSR 292 Goodness: Fast code coverage tool in less than 10k
JSR 292 introduces a new bytecode instruction invokedynamic but also several new kind of constant pool constants. Which means that most of the tools that parse bytecodes like ASM, BCEL, findbugs or EMMA will need to be updated to be java 7 compatible.
EMMA is a code coverage tool, a tool that helps developers to know if their tests cover all the code of the application. While it's not the only code coverage tool available in Java, it's the most popular from my personal experience.
In this blog entry, I would like to show how to write a simple code coverage tool indycov that use JSR 292 API to have a runtime overhead close to zero.
How a code coverage tool works ?
A code coverage tool records all paths taken when running the application and checks at the end if all lines of codes was recorded.
By example, if I run the code below with no argument, it will print "foo" and "bar" and the code coverage tool will say that the else branch that prints "baz" will be not covered.
public static void main(String[] args) {
System.out.println("foo");
if (args.length == 0) {
System.out.println("bar");
} else {
System.out.println("baz");
}
}
To record if an instruction was executed or not, code coverage tools add a probe which is a small amount of bytecodes that will call the runtime of the tool to say: "I have visited this instruction".
In fact, tools, only add probes where necessary, at the begining of each basic block of the control flow. A basic block is a collection of instructions without any jump (return, thow, if, break etc).
By example, the code above has 4 basic blocks: the once printing "foo", the one printing "bar", the one printing "baz" and the one containing the return at the end of the method.
So a code coverge tool is a tool that find basic blocks and add probes at the begining of each one.
Using JSR 292 API to implement a code coverage tool.
Finding basic block is easy with bytecode that come from 1.6 or 1.7 compiler because the compiler is required to add stack maps
in the bytecode flow. Stack maps are used to verify the bytecode in linear time and are inserted at the join points of the control flow.
So finding basic block in a 1.7 compatible bytecode can be done in one pass thanks to the stack maps info inserted by the compiler.
All existing code coverage tools have an impact on the performance of the application because the code of the probe is executed each time you call a basic block even if it should be executed once.
If you are a regular reader of this blog, you already know how to create a probe that will be executed once. The trick is use use invokedynamic, to record the visit in the bootstrap method and
to use a target method handle that is equivalent to no-op. So subsequent call will not execute any code.
main([Ljava/lang/String;)V
INVOKEDYNAMIC probe ()V [fr/umlv/indycov/RT#bsm, 1]
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "foo"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
ALOAD 0
ARRAYLENGTH
IFNE L0
INVOKEDYNAMIC probe ()V [fr/umlv/indycov/RT#bsm, 2]
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "bar"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
GOTO L1
L0
FRAME SAME
INVOKEDYNAMIC probe ()V [fr/umlv/indycov/RT#bsm, 3]
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "baz"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L1
FRAME SAME
INVOKEDYNAMIC probe ()V [fr/umlv/indycov/RT#bsm, 4]
RETURN
A no-op, is a method handle that takes nothing and return void. This method handle can be retrieved with Methodhandles.identity(void.class).
So the bootstrap method is the following. The first line records that the basic block with number 'index' is visited.
public static CallSite bsm(Lookup lookup, String name, MethodType type, Object index) {
classValue.get(lookup.lookupClass()).cover((Integer)index);
return new ConstantCallSite(MethodHandles.identity(void.class));
}
The code of the prototype is freely available (as attachment of this blog) and works like an agent.
It relies on ASM 4 (still in beta) to do the bytecode transformation.
Side note: This prototype doesn't handle runtime exception correctly. By example, if a NPE is thrown, it will escape from the basic block without ending it.
How to modify the prototype to take care of exception is let to interrested readers.
Running the code with one argument "foo"
java -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic -javaagent:lib/indycov.jar -cp test-classes/ TestCoverage foo
will print
foo baz TestCoverage: no coverage for line(s) 2 to 2 TestCoverage: no coverage for line(s) 5 to 6
line 2 is the declaration of the class, it's because javac adds a default constructor which is not used.
lines 5 to 6 are the ones that print "bar".
If you want to play with it don't forget to compile your sources with the debug flag on. Otherwise, the generated bytecodes will not contain mapping information between opcodes and line numbers.
Cheers,
Rémi
- Login or register to post comments
- Printer-friendly version
- forax's blog
- 2483 reads
Comments
Hello Rémi, This is some really interesting stuff. ...
by hexer - 2012-02-09 01:29
Hello Rémi,
This is some really interesting stuff. Do you know about the Jacoco project. It is the successor of Emma and is currently under development. It is already very stable and we use it here for our code coverage statistics. I do not know how Jacoco works internally but they might be interested in your ideas to optimize it for Java 7.
Thanks for your great blog posts,
- Bernd Rosstauscher
JSR 292 Goodness: Fast code
by geovizer - 2011-02-16 11:55
Rémi,
This is great stuff, thank you. I like this post for a few reasons:
1. I've been a happy Emma user for years, but it seemed a bit crufty as a project and I worried that it might crash on the rocks of JDK 7. No more worries!
2. This is a great example of what is possible with INVOKEDYNAMIC, complete with source code.
One request. There doesn't seem to be any licensing statement in the source code. Can you give permission for others to copy and use your code? It's implicit in what you write, and I would have no problem using it now on a personal project. However, if someone wanted to use this code to start a project, or to include as part of an existing project, they would probably appriciate a statement of what, if any, conditions there are on using your code.
Thanks again!
regards,
-Frank Hardisty
http://www.personal.psu.edu/fah109/
FOSDEM 2011
Free Java at FOSDEM 2011 is over. This year was a 'grand cru',
mixing old friends and new faces, too bad that Ian and Dalibor was not there, we miss you.
This year, I was presenting two talks, IndyDroid and PHP.reboot.
IndyDroid aims to implements JSR 292 on Android in order to improve performance of dynamic language runtime on Android,
the project is still in infancy mode but we should have a working implementation in the next two months.
PHP.reboot is a Proof of Concept that any dynamic languages that run of JVM should be as fast as Java (almost !).
Enjoy the slides.
Rémi
- Login or register to post comments
- Printer-friendly version
- forax's blog
- 484 reads
JSR 292 Goodness: named parameters
Today, I want to show you a way to implement method invocation with named parameters using JSR 292.
But before using JSR 292 API, we need a way to reflect the parameter names of any existing methods.
The problem is that java.lang.reflect doesn't provide any way to get those parameter names,
so I had to first write a small class that does reflection of parameter names using my second favorite API, ASM.
public static List<MethodInfo> getMethods(final Class<?> clazz) throws IOException {
String className = clazz.getName().replace('.', '/') + ".class";
final ClassLoader classLoader = clazz.getClassLoader();
final ArrayListmethods = new ArrayList<>(); // thanks Joe
try(InputStream input = (classLoader != null)? // thanks Joe
classLoader.getResourceAsStream(className):
ClassLoader.getSystemResourceAsStream(className)) {
ClassReader reader = new ClassReader(input);
reader.accept(new EmptyVisitor(){
@Override
public MethodVisitor visitMethod(final int access, final String name, String desc, String signature, String[] exceptions) {
final MethodType methodType = MethodType.fromMethodDescriptorString(desc, classLoader);
int parameterCount = methodType.parameterCount();
final String[] parameterNames = new String[parameterCount];
if (parameterCount == 0) { // shortcut for method with no parameter
methods.add(new MethodInfo(access, clazz, name, methodType, parameterNames));
return null;
}
return new EmptyVisitor() {
@Override
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
if (index>= parameterNames.length || parameterNames[index] != null) {
return;
}
parameterNames[index] = name;
}
@Override
public void visitEnd() {
for(int i=0; i<parameterNames.length; i++) {
if (parameterNames[i] == null) {
parameterNames[i] = "args" + i;
}
}
methods.add(new MethodInfo(access, clazz, name, methodType, parameterNames));
}
};
}
}, ClassReader.SKIP_FRAMES);
return methods;
}
This code is not very efficient, it will be more efficient to lookup and decode the code attribute LocalVariableTable, anyway it does the job.
Otherwise, I think my co-worker had serious doubt about my mental sanity because I thank Joe loudly (the sound has to cross the Atlantic ocean) every 20 lines of codes,
his project Coin really simplifies my day to day job. We should start a Joe Thanker Club !
Named parameters
Now, let suppose I want to resolve MyClass.foo(s: "eleven", i:7) knowing that foo is declared like that:
public static String foo(int i, String s) {
return i + s;
}
One can notice that the names of the parameters are constant. The permutation can't be processed at compile time because we want that the declaration can be modified and recompiled independently of its usage.
Also instead of doing the permutation at each call, we can process the corresponding permutation of parameters once at linked time. Let's implement that with invokedynamic.
Calling foo is equivalent to this invokedynamic call
invokedynamic [#bsm, MyClass.class, "s", "i"] foo("eleven", 7)
but because Java the language has no syntax for invokedynamic, I will use the class DynamicIndy that I had previously introduced. I've just updated it to use a newer beta of ASM4.
MethodHandle mh = new DynamicIndy().invokeDynamic("foo",
MethodType.methodType(String.class, String.class, int.class),
Main.class,
"bsm",
MethodType.methodType(CallSite.class, Lookup.class, String.class, MethodType.class, Object[].class),
Main.class, "s", "i");
System.out.println((String)mh.invokeExact("eleven", 7));
Writting the boostrap method is straightforward knowing that Methodhandles has a method permuteArguments. To avoid to do the reflection more than once by class. The result of a reflection is cached.
Instead of using a homemade concurrent WeakHashMap and to try to workaround its defects (4429536, 6389107),
I use a new class introduced by JSR 292, <tt>ClassValue</tt> that safetly associates to a class a value computed on demand and cached.
private final static ClassValue<HashMap<String, MethodInfo>> methodsClassValue =
new ClassValue<>() { // thanks Joe
@Override
protected HashMap<String, MethodInfo> computeValue(Class<?> type) {
List methods;
try {
methods = MethodInfo.getMethods(type);
} catch(IOException e) {
throw (LinkageError)new LinkageError().initCause(e);
}
HashMap<String, MethodInfo> methodMap = new HashMap<>(); // thanks Joe
for(MethodInfo method: methods) {
methodMap.put(method.getName(), method);
}
return methodMap;
}
};
public static CallSite bsm(Lookup lookup, String name, MethodType methodType, Object... bsmArgs) throws Throwable {
HashMap<String, MethodInfo> methodMap = methodsClassValue.get((Class<?>)bsmArgs[0]);
MethodInfo method = methodMap.get(name);
if (method == null) {
throw new InvokeDynamicBootstrapError("no method "+name+" found");
}
int parameterCount = method.getParameterCount();
if (parameterCount != methodType.parameterCount()) {
throw new InvokeDynamicBootstrapError("wrong number of parameters "+methodType+ " for method "+method);
}
HashMap<String,Integer> parametersMap = new HashMap<>(); // thanks Joe
for(int i=0; i < parameterCount; i++) {
parametersMap.put(method.getParameterName(i), i);
}
int[] permutation = new int[parameterCount];
for(int i=0; i < parameterCount; i++) {
Object parameterName = bsmArgs[i + 1];
Integer slot = parametersMap.get(parameterName);
if (slot == null) {
throw new InvokeDynamicBootstrapError("unknown parameter name "+parameterName);
}
permutation[i] = slot;
}
MethodHandle mh = MethodHandles.permuteArguments(
method.asMethodHandle(lookup), methodType, permutation);
return new ConstantCallSite(mh);
}
The code is freely available, see the attachment.
That's all folks, at least for today,
Rémi
- Login or register to post comments
- Printer-friendly version
- forax's blog
- 946 reads
Comments
JSR 292 Goodness: named
by olefevre - 2011-01-23 10:31
Are you aware that both bugs are now marked as "not available"? Are they security-related?
JSR 292 Goodness: named
by forax - 2011-01-24 08:37
Weird, these bugs aren't, as far as I know, related to some security stuff.
There is a ghost in the shell or a bug in the bug database.
Rémi
JSR 292 Goodness: named
by olefevre - 2011-01-24 20:56
Or maybe oracle really is evil? Unlike many people I gave it the benefit of doubt so far but if it starts futzing with the immensely useful bugs database, esp. in such a sneaky way, I'm going to have to reconsider. You are thick with the core Java team at Oracle; maybe you can ask them what's going on?
JSR 292 Goodness: named
by forax - 2011-01-25 09:17
I can access to bug 4429536 now.
It's more a db failure than a new policy in my opinion but I will ask.
Rémi
JSR 292 Goodness: named
by olefevre - 2011-01-29 13:14
Indeed that works once again. False alarm (this time, at any rate).




Comments
Wouldn't something like this also work? (sorry for the ...
by juancn - 2011-12-23 11:44
Wouldn't something like this also work? (sorry for the pastebin link, but the comment area doesn't like generics).
Or there is some hidden overhead that I'm not seeing?
No, even if the field named 'getter' is declared final, you ...
by forax - 2011-12-25 17:31
No, even if the field named 'getter' is declared final, you can change it by reflection so the VM will not replace it by a constant.
cheers,
Rémi
Without the double cast, invokeExact would fail ...
by hchar - 2011-12-19 14:22
Without the double cast, invokeExact would fail with:
Caused by: java.lang.invoke.WrongMethodTypeException: ()Ljava/lang/Object; cannot be called as ()Ljava/util/Locale;
The explanation reasoned/found from the javadoc of MethodHandle#invokeExact(Object[]):
"The symbolic type descriptor at the call site of {@code invokeExact} must exactly match this method handle's {@link #type type}. No conversions are allowed on arguments or return values."
"@throws WrongMethodTypeException if the target's type is not identical with the caller's symbolic type descriptor"
So the first cast is to satisfy the invokeExact API to avoid the WrongMethodTypeExcepiton, whereas the second cast is to subsequently narrow the Object to the known returned object type which is Locale in this case.
Seems strange to me, however, why the underlying invokeExact API has such odd requirement in the first place.
Regards,
Hanson
Me trying to make a long story short: We want function ...
by forax - 2011-12-19 16:12
Me trying to make a long story short:
We want function pointesr but because we are in Java, we want safe function pointer.
There are two ways to implement that, the first is to have a special kind of object with a function signature, this is what the delegate are in C#, in the VM recognize these objects you can allow contravariance of the parameters and covariance of the return type i.e the classical relation between the caller signature et compile time and the callee signature. But it's hard to write a simple vtable with such beasts.
Or you can have one signature (like void* in C) that represent a kind of generic function pointer, a MethodHandle in Java parlance, but because you do want type safety at runtime, you have to do a signature check at runtime. Testing covariant signatures at runtime is hard to do fast, so the idea is that if you require to have exactly the same signature, you can implement that check as a pointer check. So invokeExact() is a fast safe call.
But the API also provide another way to call a method handle, nvoke() which let you call a method handle and will allow more conversions (even boxing and varargs) but with no performance guarantee. Note that here, because the method handle is constant, the conversions implied by invoke() is constant too, so an invoke() should be as fast as an invokeExact() if the VM is in good mood :)
Rémi
The double cast is because incokeExact returns Object (the ...
by tkowalcz - 2011-12-19 03:13
The double cast is because incokeExact returns Object (the call site was initialzied with MethodType.methodType( Object.class )) and method handle's type must match the descriptor at the call site - so caller must declare that he expects the Object and cannot cast to Locale directly. Call to incoke() will perform conversion on return value so only one cast would be needed in that case.
In case I am right & first, can you ship the beer to Poland? :)
Thanks, Tomek
Tomek, bravo! your right! and yes, invoke will work here, ...
by forax - 2011-12-19 15:53
Tomek, bravo! your right!
and yes, invoke will work here, another idea is to provide the class (Locale.class) to the constructor of the CallSite to initialize it with the correct class.
About the beer, let me know if one day you want to visit Paris, I will be there with a beer :)
Rémi
Your link just links me back to this blog article. Guessing ...
by edsrzf - 2011-12-18 22:37
Your link just links me back to this blog article. Guessing you meant to link here?
Your link just links me back to this blog article. Guessing ...
by forax - 2011-12-19 15:45
thanks, fixed.
Rémi
Hi Remi, What is the purpose of creating a local ...
by hchar - 2011-12-18 13:24
Hi Remi,
Yes, it's for avoiding an unsafe publication. I beleive ...
by forax - 2011-12-18 14:03
Yes, it's for avoiding an unsafe publication.
I beleive than I don't need this here because I only use this constructor in the boostrap method and I think that the VM guarantee that a callsite is fully initialized before being used.
The JSR 292 spec says "If several threads simultaneously execute a bootstrap method for a single dynamic call site, the JVM must choose one
CallSiteobject and install it visibly to all threads."But it was not clear enough for me. I wanted to ask John Rose before publishing that code and forget to do it.
Rémi
So I'm allowed to submit an answer? :)
by headius - 2011-12-17 12:51
So I'm allowed to submit an answer? :)
Too easy for you :) but I will be happy to offer you a beer ...
by forax - 2011-12-17 13:36
Too easy for you :)
but I will be happy to offer you a beer during the next JVM Summit.
Rémi