Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 2538b8ffbfe2^
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: fc4bcb53150d
Choose a head ref
  • 8 commits
  • 17 files changed
  • 1 contributor

Commits on Jan 11, 2016

  1. Copy the full SHA
    2538b8f View commit details
  2. Add a separate type of JavaMethodDescriptor for compile time.

    MethodDescriptor is the common base class and calculates most
    fields based on overridden functions in the specialized types.
    
    ExecutableElementDescriptor is the version used at compile time
    against the javac model.
    
    JavaMethodDescriptor is the version used at runtime against
    reflected method objects.
    headius committed Jan 11, 2016
    Copy the full SHA
    a0dd89c View commit details

Commits on Jan 12, 2016

  1. Copy the full SHA
    3ffd526 View commit details
  2. Copy the full SHA
    8b16a72 View commit details
  3. Copy the full SHA
    e4ff00b View commit details
  4. Copy the full SHA
    00fe248 View commit details
  5. Add an offline indy-based invoker generator.

    HandleMethod has existed for a while, allowing us to have Ruby
    core methods implemented in Java bound without using bytecode-
    generated invoker classes. However the cost of building all the
    method handles necessary makes the indy approach slower than the
    generated code.
    
    Part of this is due to the reflective walking of all classes done
    at every boot to bind their methods. This commit fixes that by
    adding a new offline (annotation-processing) code generator that
    spins a new version of our "populator" classes for indy binding.
    These classes just contain method construction and binding calls,
    avoiding reflective class and method walking. They also contain
    all direct method handles for the core methods as constant pool
    entries, avoiding the checks required to acquire those at runtime.
    
    This was not enough to beat the generated invokers for boot time,
    because the direct handles still needed to be adapted for
    different arities, heap framing, and argument casting and
    reordering. In order to reduce those costs, HandleMethod will now
    defer that adaptation on a per-arity basis until the adapted
    handle is actually needed. This reduces boot costs of the handle-
    based methods to equivalent or less than the generated invokers.
    
    Caveats:
    
    * This is not yet passing all tests. In particular there's some
      bit-rotted indy binding code for HandleMethod that appears to
      need some work.
    * Boot time was tested with "-e 1" (about the same speed) and
    "gem list" (a bit faster with handles). Other cases may vary.
    * For an application that forces many handles to get adapted,
      this could lead to slower boot time. However, most applications
      only ever call a fraction of the methods defined in the system.
    * There appear to be significant memory improvements when running
      in this mode. Base memory for a "-e sleep" process dropped from
      125MB to 95MB and the number of loaded classes dropped from
      6600 to around 4400. Heap utilization was slightly lower at 11MB
      versus 12-15MB.
    
    I will push a separate PR that enables fully handle-based mode on
    a separate branch for experimentation, discussion, and fixes.
    headius committed Jan 12, 2016
    Copy the full SHA
    318136e View commit details
  6. Copy the full SHA
    fc4bcb5 View commit details
34 changes: 7 additions & 27 deletions core/pom.rb
Original file line number Diff line number Diff line change
@@ -56,7 +56,7 @@
jar 'org.jruby.jcodings:jcodings:1.0.16-SNAPSHOT'
jar 'org.jruby:dirgra:0.3'

jar 'com.headius:invokebinder:1.5'
jar 'com.headius:invokebinder:1.7-SNAPSHOT'
jar 'com.headius:options:1.4-SNAPSHOT'
jar 'com.headius:coro-mock:1.0', :scope => 'provided'
jar 'com.headius:unsafe-mock', '${unsafe.version}', :scope => 'provided'
@@ -138,19 +138,6 @@
:id => 'add-populators',
'sources' => [ '${anno.sources}' ] )
end

plugin 'org.codehaus.mojo:exec-maven-plugin' do
execute_goals( 'exec',
:id => 'invoker-generator',
'arguments' => [ '-Djruby.bytecode.version=${base.java.version}',
'-classpath',
xml( '<classpath/>' ),
'org.jruby.anno.InvokerGenerator',
'${anno.sources}/annotated_classes.txt',
'${project.build.outputDirectory}' ],
'executable' => 'java',
'classpathScope' => 'compile' )
end
end

plugin( :compiler,
@@ -168,32 +155,25 @@
:id => 'anno',
:phase => 'process-resources',
'includes' => [ 'org/jruby/anno/FrameField.java',
'org/jruby/anno/AnnotationBinder.java',
'org/jruby/anno/IndyBinder.java',
'org/jruby/anno/JRubyMethod.java',
'org/jruby/anno/FrameField.java',
'org/jruby/CompatVersion.java',
'org/jruby/runtime/Visibility.java',
'org/jruby/util/CodegenUtils.java',
'org/jruby/util/SafePropertyAccessor.java' ] )
'org/jruby/util/SafePropertyAccessor.java',
'org/jruby/anno/JavaMethodDescriptorLight.java',
'org/jruby/internal/runtime/methods/DescriptorInfoLight.java'] )
execute_goals( 'compile',
:id => 'default-compile',
:phase => 'compile',
'annotationProcessors' => [ 'org.jruby.anno.AnnotationBinder' ],
'generatedSourcesDirectory' => 'target/generated-sources',
'annotationProcessors' => [ 'org.jruby.anno.IndyBinder' ],
'generatedSourcesDirectory' => 'target/classes',
'compilerArgs' => [ '-XDignore.symbol.file=true',
'-J-Duser.language=en',
'-J-Dfile.encoding=UTF-8',
'-J-Xbootclasspath/p:${unsafe.jar}',
'-J-Xmx${jruby.compile.memory}' ] )
execute_goals( 'compile',
:id => 'populators',
:phase => 'process-classes',
'compilerArgs' => [ '-XDignore.symbol.file=true',
'-J-Duser.language=en',
'-J-Dfile.encoding=UTF-8',
'-J-Xbootclasspath/p:${unsafe.jar}',
'-J-Xmx${jruby.compile.memory}' ],
'includes' => [ 'org/jruby/gen/**/*.java' ] )
execute_goals( 'compile',
:id => 'eclipse-hack',
:phase => 'process-classes',
54 changes: 6 additions & 48 deletions core/pom.xml
Original file line number Diff line number Diff line change
@@ -194,7 +194,7 @@ DO NOT MODIFIY - GENERATED CODE
<dependency>
<groupId>com.headius</groupId>
<artifactId>invokebinder</artifactId>
<version>1.5</version>
<version>1.7-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.headius</groupId>
@@ -405,31 +405,6 @@ DO NOT MODIFIY - GENERATED CODE
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>invoker-generator</id>
<phase>process-classes</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<arguments>
<argument>-Djruby.bytecode.version=${base.java.version}</argument>
<argument>-classpath</argument>
<classpath />
<argument>org.jruby.anno.InvokerGenerator</argument>
<argument>${anno.sources}/annotated_classes.txt</argument>
<argument>${project.build.outputDirectory}</argument>
</arguments>
<executable>java</executable>
<classpathScope>compile</classpathScope>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
@@ -442,13 +417,15 @@ DO NOT MODIFIY - GENERATED CODE
<configuration>
<includes>
<include>org/jruby/anno/FrameField.java</include>
<include>org/jruby/anno/AnnotationBinder.java</include>
<include>org/jruby/anno/IndyBinder.java</include>
<include>org/jruby/anno/JRubyMethod.java</include>
<include>org/jruby/anno/FrameField.java</include>
<include>org/jruby/CompatVersion.java</include>
<include>org/jruby/runtime/Visibility.java</include>
<include>org/jruby/util/CodegenUtils.java</include>
<include>org/jruby/util/SafePropertyAccessor.java</include>
<include>org/jruby/anno/JavaMethodDescriptorLight.java</include>
<include>org/jruby/internal/runtime/methods/DescriptorInfoLight.java</include>
</includes>
</configuration>
</execution>
@@ -460,9 +437,9 @@ DO NOT MODIFIY - GENERATED CODE
</goals>
<configuration>
<annotationProcessors>
<annotationProcessor>org.jruby.anno.AnnotationBinder</annotationProcessor>
<annotationProcessor>org.jruby.anno.IndyBinder</annotationProcessor>
</annotationProcessors>
<generatedSourcesDirectory>target/generated-sources</generatedSourcesDirectory>
<generatedSourcesDirectory>target/classes</generatedSourcesDirectory>
<compilerArgs>
<compilerArg>-XDignore.symbol.file=true</compilerArg>
<compilerArg>-J-Duser.language=en</compilerArg>
@@ -472,25 +449,6 @@ DO NOT MODIFIY - GENERATED CODE
</compilerArgs>
</configuration>
</execution>
<execution>
<id>populators</id>
<phase>process-classes</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<compilerArgs>
<compilerArg>-XDignore.symbol.file=true</compilerArg>
<compilerArg>-J-Duser.language=en</compilerArg>
<compilerArg>-J-Dfile.encoding=UTF-8</compilerArg>
<compilerArg>-J-Xbootclasspath/p:${unsafe.jar}</compilerArg>
<compilerArg>-J-Xmx${jruby.compile.memory}</compilerArg>
</compilerArgs>
<includes>
<include>org/jruby/gen/**/*.java</include>
</includes>
</configuration>
</execution>
<execution>
<id>eclipse-hack</id>
<phase>process-classes</phase>
110 changes: 110 additions & 0 deletions core/src/main/java/org/jruby/anno/ExecutableElementDescriptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2008-2013 Charles Oliver Nutter <headius@headius.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.anno;

import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.NestingKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Set;

/**
* A version of MethodDescriptor that works against ExecutableElement during compile time annotation processing.
*/
public class ExecutableElementDescriptor extends MethodDescriptor<ExecutableElement> {
public final ExecutableElement method;

public ExecutableElementDescriptor(ExecutableElement method) {
super(method);
this.method = method;
}

protected <A extends Annotation> A getAnnotation(ExecutableElement methodObject, Class<A> annotationType) {
return methodObject.getAnnotation(annotationType);
}

protected int getModifiers(ExecutableElement methodObject) {
Set<javax.lang.model.element.Modifier> mods = methodObject.getModifiers();
int modifierTmp = 0;
try {
// TODO: gross
for (javax.lang.model.element.Modifier mod : mods) {
modifierTmp |= (Integer) Modifier.class.getField(mod.name()).get(null);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return modifierTmp;
}

protected String getDeclaringClassName(ExecutableElement methodObject) {
return getActualQualifiedName((TypeElement) methodObject.getEnclosingElement()).toString();
}

protected String getSimpleName(ExecutableElement methodObject) {
return methodObject.getSimpleName().toString();
}

protected boolean hasContext(ExecutableElement methodObject) {
List<? extends VariableElement> symbolicParameters = methodObject.getParameters();
if (symbolicParameters.size() > 0) {
return symbolicParameters.get(0).asType().toString().equals("org.jruby.runtime.ThreadContext");
}

return false;
}

protected boolean hasBlock(ExecutableElement methodObject) {
List<? extends VariableElement> symbolicParameters = methodObject.getParameters();

if (symbolicParameters.size() > 0) {
return symbolicParameters.get(symbolicParameters.size() - 1).asType().toString().equals("org.jruby.runtime.Block");
}

return false;
}

protected int parameterCount(ExecutableElement methodObject) {
return methodObject.getParameters().size();
}

protected String parameterAsString(ExecutableElement methodObject, int index) {
return methodObject.getParameters().get(index).asType().toString();
}

public static CharSequence getActualQualifiedName(TypeElement td) {
if (td.getNestingKind() == NestingKind.MEMBER) {
return getActualQualifiedName((TypeElement)td.getEnclosingElement()) + "$" + td.getSimpleName();
}
return td.getQualifiedName().toString();
}
}
Loading