From 6652d6ac332f34a2a222639b6438f64a9138075e Mon Sep 17 00:00:00 2001 From: Vladimir Ivanov Date: Fri, 15 May 2015 19:23:27 +0300 Subject: [PATCH 1/8] 8079205: CallSite dependency tracking is broken after sun.misc.Cleaner became automatically cleared Reviewed-by: roland, psandoz, plevart, kbarrett, jrose --- .../classes/java/lang/invoke/CallSite.java | 44 +------------------ .../java/lang/invoke/MethodHandleNatives.java | 25 +++++++++-- 2 files changed, 24 insertions(+), 45 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java b/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java index 11e452b96c9..13cf24ca0f2 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java @@ -27,8 +27,6 @@ package java.lang.invoke; import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; -import java.lang.reflect.Field; -import sun.misc.Cleaner; /** * A {@code CallSite} is a holder for a variable {@link MethodHandle}, @@ -138,47 +136,9 @@ public class CallSite { /** * {@code CallSite} dependency context. - * VM uses context class to store nmethod dependencies on the call site target. - * Can be in 2 states: (a) null; or (b) {@code Cleaner} instance pointing to some Class instance. - * Lazily initialized when CallSite instance is linked to some indy call site or VM needs - * it to store dependencies. As a corollary, "null" context means there are no dependencies - * registered yet. {@code Cleaner} is used in 2 roles: - * (a) context class access for VM; - * (b) stale context class cleanup. - * {@code Cleaner} holds the context class until cleanup action is finished (see {@code PhantomReference}). - * Though it's impossible to get the context class using {@code Reference.get()}, VM extracts it directly - * from {@code Reference.referent} field. + * JVM uses CallSite.context to store nmethod dependencies on the call site target. */ - private volatile Cleaner context = null; - - /** - * Default context. - * VM uses it to initialize non-linked CallSite context. - */ - private static class DefaultContext {} - private static final Cleaner DEFAULT_CONTEXT = makeContext(DefaultContext.class, null); - - private static Cleaner makeContext(Class referent, final CallSite holder) { - return Cleaner.create(referent, - new Runnable() { - @Override public void run() { - MethodHandleNatives.invalidateDependentNMethods(holder); - } - }); - } - - /** Initialize context class used for nmethod dependency tracking */ - /*package-private*/ - void initContext(Class newContext) { - // If there are concurrent actions, exactly one succeeds. - if (context == null) { - UNSAFE.compareAndSwapObject(this, CONTEXT_OFFSET, /*expected=*/null, makeContext(newContext, this)); - // No need to care about failed CAS attempt. - // Since initContext is called from indy call site linkage in newContext class, there's no risk - // that the context class becomes dead while corresponding context cleaner is alive (causing cleanup - // action in the wrong context). - } - } + private final MethodHandleNatives.CallSiteContext context = MethodHandleNatives.CallSiteContext.make(this); /** * Returns the type of this call site's target. diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java index 3d3c47f689c..b53b44b99b1 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java @@ -30,6 +30,7 @@ import java.lang.reflect.Field; import static java.lang.invoke.MethodHandleNatives.Constants.*; import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; +import sun.misc.Cleaner; /** * The JVM interface for the method handles package is all here. @@ -61,8 +62,27 @@ class MethodHandleNatives { static native void setCallSiteTargetNormal(CallSite site, MethodHandle target); static native void setCallSiteTargetVolatile(CallSite site, MethodHandle target); - /** Invalidate CallSite context: clean up dependent nmethods and reset call site context to initial state (null). */ - static native void invalidateDependentNMethods(CallSite site); + /** Represents a context to track nmethod dependencies on CallSite instance target. */ + static class CallSiteContext implements Runnable { + //@Injected JVM_nmethodBucket* vmdependencies; + + static CallSiteContext make(CallSite cs) { + final CallSiteContext newContext = new CallSiteContext(); + // Cleaner is attached to CallSite instance and it clears native structures allocated for CallSite context. + // Though the CallSite can become unreachable, its Context is retained by the Cleaner instance (which is + // referenced from Cleaner class) until cleanup is performed. + Cleaner.create(cs, newContext); + return newContext; + } + + @Override + public void run() { + MethodHandleNatives.clearCallSiteContext(this); + } + } + + /** Invalidate all recorded nmethods. */ + private static native void clearCallSiteContext(CallSiteContext context); private static native void registerNatives(); static { @@ -235,7 +255,6 @@ class MethodHandleNatives { return Invokers.linkToTargetMethod(type); } else { appendixResult[0] = callSite; - callSite.initContext(caller); return Invokers.linkToCallSiteMethod(type); } } From 47a5823eae3cb8956956f7c954e8e6e4382bf30a Mon Sep 17 00:00:00 2001 From: Vladimir Ivanov Date: Thu, 21 May 2015 18:23:01 +0300 Subject: [PATCH 2/8] 8059340: ConstantPool::_resolved_references is missing in heap dump Reviewed-by: sspitsyn, stefank, twisti --- jdk/src/java.base/share/classes/java/lang/Class.java | 3 +++ jdk/src/java.base/share/classes/sun/reflect/Reflection.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/jdk/src/java.base/share/classes/java/lang/Class.java b/jdk/src/java.base/share/classes/java/lang/Class.java index 3aefe6ee2fb..514c6d188d7 100644 --- a/jdk/src/java.base/share/classes/java/lang/Class.java +++ b/jdk/src/java.base/share/classes/java/lang/Class.java @@ -3653,4 +3653,7 @@ public final class Class implements java.io.Serializable, public AnnotatedType[] getAnnotatedInterfaces() { return TypeAnnotationParser.buildAnnotatedInterfaces(getRawTypeAnnotations(), getConstantPool(), this); } + + /** An array of resolved objects from constant pool. Used by JVM. */ + private transient Object[] resolvedReferences; } diff --git a/jdk/src/java.base/share/classes/sun/reflect/Reflection.java b/jdk/src/java.base/share/classes/sun/reflect/Reflection.java index bc3e13e23cb..34160be305c 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/Reflection.java +++ b/jdk/src/java.base/share/classes/sun/reflect/Reflection.java @@ -46,7 +46,7 @@ public class Reflection { map.put(Reflection.class, new String[] {"fieldFilterMap", "methodFilterMap"}); map.put(System.class, new String[] {"security"}); - map.put(Class.class, new String[] {"classLoader"}); + map.put(Class.class, new String[] {"classLoader", "resolvedReferences"}); fieldFilterMap = map; methodFilterMap = new HashMap<>(); From dc63fa46894d28d27209f4d7f31a41b28b399543 Mon Sep 17 00:00:00 2001 From: Katja Kantserova Date: Wed, 27 May 2015 14:35:55 +0200 Subject: [PATCH 3/8] 8081037: serviceability/sa/ tests time out on Windows Reviewed-by: jbachorik, sla, dsamersoff --- .../jmap/heapconfig/JMapHeapConfigTest.java | 24 +- .../tools/jmap/heapconfig/LingeredApp.java | 431 ------------------ .../jmap/heapconfig/LingeredAppTest.java | 70 --- .../jmap/heapconfig/TmtoolTestScenario.java | 2 + 4 files changed, 16 insertions(+), 511 deletions(-) delete mode 100644 jdk/test/sun/tools/jmap/heapconfig/LingeredApp.java delete mode 100644 jdk/test/sun/tools/jmap/heapconfig/LingeredAppTest.java diff --git a/jdk/test/sun/tools/jmap/heapconfig/JMapHeapConfigTest.java b/jdk/test/sun/tools/jmap/heapconfig/JMapHeapConfigTest.java index 768e6504c69..69fbf206f62 100644 --- a/jdk/test/sun/tools/jmap/heapconfig/JMapHeapConfigTest.java +++ b/jdk/test/sun/tools/jmap/heapconfig/JMapHeapConfigTest.java @@ -21,25 +21,29 @@ * questions. */ -/* - * @test - * @bug 8042397 - * @summary Unit test for jmap utility test heap configuration reader - * @library /lib/testlibrary - * @modules java.management - * @build jdk.testlibrary.* - * @build JMapHeapConfigTest LingeredApp TmtoolTestScenario - * @run main JMapHeapConfigTest - */ import java.io.IOException; import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; + +import jdk.test.lib.apps.LingeredApp; import jdk.testlibrary.Utils; import jdk.testlibrary.Platform; +/* + * @test + * @bug 8042397 + * @summary Unit test for jmap utility test heap configuration reader + * @library /../../test/lib/share/classes + * @library /lib/testlibrary + * @modules java.management + * @build jdk.testlibrary.* + * @build jdk.test.lib.apps.* + * @build JMapHeapConfigTest TmtoolTestScenario + * @run main JMapHeapConfigTest + */ public class JMapHeapConfigTest { static final String expectedJMapValues[] = { diff --git a/jdk/test/sun/tools/jmap/heapconfig/LingeredApp.java b/jdk/test/sun/tools/jmap/heapconfig/LingeredApp.java deleted file mode 100644 index a6f1a64ee88..00000000000 --- a/jdk/test/sun/tools/jmap/heapconfig/LingeredApp.java +++ /dev/null @@ -1,431 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.attribute.BasicFileAttributes; -import java.nio.file.attribute.FileTime; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -/** - * This is a framework to launch an app that could be synchronized with caller - * to make further attach actions reliable across supported platforms - - * Caller example: - * SmartTestApp a = SmartTestApp.startApp(cmd); - * // do something - * a.stopApp(); - * - * or fine grained control - * - * a = new SmartTestApp("MyLock.lck"); - * a.createLock(); - * a.runApp(); - * a.waitAppReady(); - * // do something - * a.deleteLock(); - * a.waitAppTerminate(); - * - * Then you can work with app output and process object - * - * output = a.getAppOutput(); - * process = a.getProcess(); - * - */ -public class LingeredApp { - - private static final long spinDelay = 1000; - - private final String lockFileName; - private long lockCreationTime; - private Process appProcess; - private final ArrayList storedAppOutput; - - /* - * Drain child process output, store it into string array - */ - class InputGobbler extends Thread { - - InputStream is; - List astr; - - InputGobbler(InputStream is, List astr) { - this.is = is; - this.astr = astr; - } - - public void run() { - try { - InputStreamReader isr = new InputStreamReader(is); - BufferedReader br = new BufferedReader(isr); - String line = null; - while ((line = br.readLine()) != null) { - astr.add(line); - } - } catch (IOException ex) { - // pass - } - } - } - - /** - * Create LingeredApp object on caller side. Lock file have be a valid filename - * at writable location - * - * @param lockFileName - the name of lock file - */ - public LingeredApp(String lockFileName) { - this.lockFileName = lockFileName; - this.storedAppOutput = new ArrayList(); - } - - /** - * - * @return name of lock file - */ - public String getLockFileName() { - return this.lockFileName; - } - - /** - * - * @return name of testapp - */ - public String getAppName() { - return this.getClass().getName(); - } - - /** - * - * @return pid of java process running testapp - */ - public long getPid() { - if (appProcess == null) { - throw new RuntimeException("Process is not alive"); - } - return appProcess.getPid(); - } - - /** - * - * @return process object - */ - public Process getProcess() { - return appProcess; - } - - /** - * - * @return application output as string array. Empty array if application produced no output - */ - List getAppOutput() { - if (appProcess.isAlive()) { - throw new RuntimeException("Process is still alive. Can't get its output."); - } - return storedAppOutput; - } - - /* Make sure all part of the app use the same method to get dates, - as different methods could produce different results - */ - private static long epoch() { - return new Date().getTime(); - } - - private static long lastModified(String fileName) throws IOException { - Path path = Paths.get(fileName); - BasicFileAttributes attr = Files.readAttributes(path, BasicFileAttributes.class); - return attr.lastModifiedTime().toMillis(); - } - - private static void setLastModified(String fileName, long newTime) throws IOException { - Path path = Paths.get(fileName); - FileTime fileTime = FileTime.fromMillis(newTime); - Files.setLastModifiedTime(path, fileTime); - } - - /** - * create lock - * - * @throws IOException - */ - public void createLock() throws IOException { - Path path = Paths.get(lockFileName); - // Files.deleteIfExists(path); - Files.createFile(path); - lockCreationTime = lastModified(lockFileName); - } - - /** - * Delete lock - * - * @throws IOException - */ - public void deleteLock() throws IOException { - try { - Path path = Paths.get(lockFileName); - Files.delete(path); - } catch (NoSuchFileException ex) { - // Lock already deleted. Ignore error - } - } - - public void waitAppTerminate() { - while (true) { - try { - appProcess.waitFor(); - break; - } catch (InterruptedException ex) { - // pass - } - } - } - - /** - * The app touches the lock file when it's started - * wait while it happens. Caller have to delete lock on wait error. - * - * @param timeout - * @throws java.io.IOException - */ - public void waitAppReady(long timeout) throws IOException { - long here = epoch(); - while (true) { - long epoch = epoch(); - if (epoch - here > (timeout * 1000)) { - throw new IOException("App waiting timeout"); - } - - // Live process should touch lock file every second - long lm = lastModified(lockFileName); - if (lm > lockCreationTime) { - break; - } - - // Make sure process didn't already exit - if (!appProcess.isAlive()) { - throw new IOException("App exited unexpectedly with " + appProcess.exitValue()); - } - - try { - Thread.sleep(spinDelay); - } catch (InterruptedException ex) { - // pass - } - } - } - - /** - * Run the app - * - * @param vmArguments - * @throws IOException - */ - public void runApp(List vmArguments) - throws IOException { - - // We should always use testjava or throw an exception, - // so we can't use JDKToolFinder.getJDKTool("java"); - // that falls back to compile java on error - String jdkPath = System.getProperty("test.jdk"); - if (jdkPath == null) { - // we are not under jtreg, try env - Map env = System.getenv(); - jdkPath = env.get("TESTJAVA"); - } - - if (jdkPath == null) { - throw new RuntimeException("Can't determine jdk path neither test.jdk property no TESTJAVA env are set"); - } - - String osname = System.getProperty("os.name"); - String javapath = jdkPath + ((osname.startsWith("window")) ? "/bin/java.exe" : "/bin/java"); - - List cmd = new ArrayList(); - cmd.add(javapath); - - - if (vmArguments == null) { - // Propagate test.vm.options to LingeredApp, filter out possible empty options - String testVmOpts[] = System.getProperty("test.vm.opts","").split("\\s+"); - for (String s : testVmOpts) { - if (!s.equals("")) { - cmd.add(s); - } - } - } - else{ - // Lets user manage LingerApp options - cmd.addAll(vmArguments); - } - - // Make sure we set correct classpath to run the app - cmd.add("-cp"); - String classpath = System.getProperty("test.class.path"); - cmd.add((classpath == null) ? "." : classpath); - - cmd.add(this.getAppName()); - cmd.add(lockFileName); - - // Reporting - StringBuilder cmdLine = new StringBuilder(); - for (String strCmd : cmd) { - cmdLine.append("'").append(strCmd).append("' "); - } - - // A bit of verbosity - System.out.println("Command line: [" + cmdLine.toString() + "]"); - - ProcessBuilder pb = new ProcessBuilder(cmd); - // we don't expect any error output but make sure we are not stuck on pipe - // pb.redirectErrorStream(false); - pb.redirectError(ProcessBuilder.Redirect.INHERIT); - - appProcess = pb.start(); - - // Create pipe reader for process, and read stdin and stderr to array of strings - InputGobbler gb = new InputGobbler(appProcess.getInputStream(), storedAppOutput); - gb.start(); - } - - /** - * High level interface for test writers - */ - /** - * Factory method that creates SmartAppTest object with ready to use application - * lock name is autogenerated, wait timeout is hardcoded - * @param cmd - vm options, could be null to auto add testvm.options - * @return LingeredApp object - * @throws IOException - */ - public static LingeredApp startApp(List cmd) throws IOException { - final String lockName = UUID.randomUUID().toString() + ".lck"; - final int waitTime = 10; - - LingeredApp a = new LingeredApp(lockName); - a.createLock(); - try { - a.runApp(cmd); - a.waitAppReady(waitTime); - } catch (Exception ex) { - a.deleteLock(); - throw ex; - } - - return a; - } - - public static LingeredApp startApp() throws IOException { - return startApp(null); - } - - /** - * Delete lock file that signal app to terminate, then - * waits until app is actually terminated. - * @throws IOException - */ - public void stopApp() throws IOException { - deleteLock(); - waitAppTerminate(); - int exitcode = appProcess.exitValue(); - if (exitcode != 0) { - throw new IOException("LingeredApp terminated with non-zero exit code " + exitcode); - } - } - - /** - * LastModified time might not work correctly in some cases it might - * cause later failures - */ - - public static boolean isLastModifiedWorking() { - boolean sane = true; - try { - long lm = lastModified("."); - if (lm == 0) { - System.err.println("SANITY Warning! The lastModifiedTime() doesn't work on this system, it returns 0"); - sane = false; - } - - long now = epoch(); - if (lm > now) { - System.err.println("SANITY Warning! The Clock is wrong on this system lastModifiedTime() > getTime()"); - sane = false; - } - - setLastModified(".", epoch()); - long lm1 = lastModified("."); - if (lm1 <= lm) { - System.err.println("SANITY Warning! The setLastModified doesn't work on this system"); - sane = false; - } - } - catch(IOException e) { - System.err.println("SANITY Warning! IOException during sanity check " + e); - sane = false; - } - - return sane; - } - - /** - * This part is the application it self - */ - public static void main(String args[]) { - - if (args.length != 1) { - System.err.println("Lock file name is not specified"); - System.exit(7); - } - - String theLockFileName = args[0]; - - try { - Path path = Paths.get(theLockFileName); - - while (Files.exists(path)) { - // Touch the lock to indicate our readiness - setLastModified(theLockFileName, epoch()); - Thread.sleep(spinDelay); - } - } catch (NoSuchFileException ex) { - // Lock deleted while we are setting last modified time. - // Ignore error and lets the app exits - } catch (Exception ex) { - System.err.println("LingeredApp ERROR: " + ex); - // Leave exit_code = 1 to Java launcher - System.exit(3); - } - - System.exit(0); - } -} diff --git a/jdk/test/sun/tools/jmap/heapconfig/LingeredAppTest.java b/jdk/test/sun/tools/jmap/heapconfig/LingeredAppTest.java deleted file mode 100644 index f29708db4b5..00000000000 --- a/jdk/test/sun/tools/jmap/heapconfig/LingeredAppTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @summary Unit test for LingeredApp - * @compile LingeredAppTest.java - * @compile LingeredApp.java - * @run main LingeredAppTest - */ -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; - -public class LingeredAppTest { - - public static void main(String[] args) { - try { - System.out.println("Starting LingeredApp with default parameters"); - - ArrayList cmd = new ArrayList(); - - // Propagate test.vm.options to LingeredApp, filter out possible empty options - String testVmOpts[] = System.getProperty("test.vm.opts","").split("\\s+"); - for (String s : testVmOpts) { - if (!s.equals("")) { - cmd.add(s); - } - } - - cmd.add("-XX:+PrintFlagsFinal"); - - LingeredApp a = LingeredApp.startApp(cmd); - System.out.printf("App pid: %d\n", a.getPid()); - a.stopApp(); - - System.out.println("App output:"); - int count = 0; - for (String line : a.getAppOutput()) { - count += 1; - } - System.out.println("Found " + count + " lines in VM output"); - System.out.println("Test PASSED"); - } catch (IOException ex) { - ex.printStackTrace(); - System.out.println("Test ERROR"); - System.exit(3); - } - } -} diff --git a/jdk/test/sun/tools/jmap/heapconfig/TmtoolTestScenario.java b/jdk/test/sun/tools/jmap/heapconfig/TmtoolTestScenario.java index 667085f4fe8..049663c86ac 100644 --- a/jdk/test/sun/tools/jmap/heapconfig/TmtoolTestScenario.java +++ b/jdk/test/sun/tools/jmap/heapconfig/TmtoolTestScenario.java @@ -31,6 +31,8 @@ import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; + +import jdk.test.lib.apps.LingeredApp; import jdk.testlibrary.JDKToolLauncher; import jdk.testlibrary.Utils; From e6e9202e9364aefec0e97d5ec37efe4e7b719f8b Mon Sep 17 00:00:00 2001 From: Andreas Eriksson Date: Thu, 28 May 2015 12:11:33 +0200 Subject: [PATCH 4/8] 8080428: [TESTBUG] java/lang/invoke/8022701/MHIllegalAccess.java - FAIL: Unexpected wrapped exception java.lang.BootstrapMethodError Reviewed-by: vlivanov --- .../invoke/8022701/InvokeSeveralWays.java | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/jdk/test/java/lang/invoke/8022701/InvokeSeveralWays.java b/jdk/test/java/lang/invoke/8022701/InvokeSeveralWays.java index a5099dd60c5..5c1f64bf9b0 100644 --- a/jdk/test/java/lang/invoke/8022701/InvokeSeveralWays.java +++ b/jdk/test/java/lang/invoke/8022701/InvokeSeveralWays.java @@ -38,12 +38,19 @@ public class InvokeSeveralWays { failures++; } catch (InvocationTargetException e) { Throwable c = e.getCause(); - if (expected.isInstance(c)) - System.out.println("EXPECTED: " + expected.getName() + ", "+ c); - else { - failures++; - System.out.println("FAIL: Unexpected wrapped exception " + c); - e.printStackTrace(System.out); + if (BootstrapMethodError.class.isInstance(c)) { + c = c.getCause(); + if (expected.isInstance(c)) + System.out.println("EXPECTED: " + expected.getName() + ", "+ c); + else { + failures++; + System.out.println("FAIL: Unexpected wrapped exception " + c); + e.printStackTrace(System.out); + } + } else { + failures++; + System.out.println("FAIL: Exception from MethodHandle invocation not wrapped in BootstrapMethodError " + c); + e.printStackTrace(System.out); } } catch (Throwable e) { failures++; @@ -74,14 +81,19 @@ public class InvokeSeveralWays { Invoker.invoke(); System.out.println("FAIL: No exception throw, probably failed to load modified bytecodes for MethodSupplier"); failures++; - } catch (Throwable e) { - if (expected.isInstance(e)) - System.out.println("EXPECTED: " + expected.getName() + ", "+ e); + } catch (BootstrapMethodError e) { + Throwable c = e.getCause(); + if (expected.isInstance(c)) + System.out.println("EXPECTED: " + expected.getName() + ", "+ c); else { - failures++; - System.out.println("FAIL: Unexpected exception has been caught " + e); - e.printStackTrace(System.out); + failures++; + System.out.println("FAIL: Unexpected exception has been caught " + c); + e.printStackTrace(System.out); } + } catch (Throwable e) { + failures++; + System.out.println("FAIL: Exception from MethodHandle invocation not wrapped in BootstrapMethodError " + e); + e.printStackTrace(System.out); } System.out.println(); try { From eaf1406cf40215f6fca1511a0da5719df3123a3a Mon Sep 17 00:00:00 2001 From: Vladimir Ivanov Date: Fri, 29 May 2015 17:05:33 +0300 Subject: [PATCH 5/8] 8081320: Backout JDK-8059340: ConstantPool::_resolved_references is missing in heap dump Reviewed-by: sspitsyn, coleenp --- jdk/src/java.base/share/classes/java/lang/Class.java | 3 --- jdk/src/java.base/share/classes/sun/reflect/Reflection.java | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/lang/Class.java b/jdk/src/java.base/share/classes/java/lang/Class.java index 514c6d188d7..3aefe6ee2fb 100644 --- a/jdk/src/java.base/share/classes/java/lang/Class.java +++ b/jdk/src/java.base/share/classes/java/lang/Class.java @@ -3653,7 +3653,4 @@ public final class Class implements java.io.Serializable, public AnnotatedType[] getAnnotatedInterfaces() { return TypeAnnotationParser.buildAnnotatedInterfaces(getRawTypeAnnotations(), getConstantPool(), this); } - - /** An array of resolved objects from constant pool. Used by JVM. */ - private transient Object[] resolvedReferences; } diff --git a/jdk/src/java.base/share/classes/sun/reflect/Reflection.java b/jdk/src/java.base/share/classes/sun/reflect/Reflection.java index 34160be305c..bc3e13e23cb 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/Reflection.java +++ b/jdk/src/java.base/share/classes/sun/reflect/Reflection.java @@ -46,7 +46,7 @@ public class Reflection { map.put(Reflection.class, new String[] {"fieldFilterMap", "methodFilterMap"}); map.put(System.class, new String[] {"security"}); - map.put(Class.class, new String[] {"classLoader", "resolvedReferences"}); + map.put(Class.class, new String[] {"classLoader"}); fieldFilterMap = map; methodFilterMap = new HashMap<>(); From 820f4d308916599d6933a690aeba7e08def1af9c Mon Sep 17 00:00:00 2001 From: Magnus Ihse Bursie Date: Tue, 2 Jun 2015 15:09:49 +0200 Subject: [PATCH 6/8] 8081616: Remove hard-coded CFLAGS_WARNINGS_ARE_ERRORS to fully respect --disable-warnings-as-errors Reviewed-by: erikj, dholmes --- jdk/make/lib/Lib-java.instrument.gmk | 2 +- jdk/make/lib/Lib-java.management.gmk | 2 +- jdk/make/lib/Lib-jdk.attach.gmk | 4 ++-- jdk/make/lib/Lib-jdk.hprof.agent.gmk | 8 ++++---- jdk/make/lib/Lib-jdk.jdi.gmk | 4 ++-- jdk/make/lib/Lib-jdk.jdwp.agent.gmk | 6 +++--- jdk/make/lib/Lib-jdk.management.gmk | 2 +- jdk/make/lib/Lib-jdk.sctp.gmk | 12 ++++-------- 8 files changed, 18 insertions(+), 22 deletions(-) diff --git a/jdk/make/lib/Lib-java.instrument.gmk b/jdk/make/lib/Lib-java.instrument.gmk index b0eb7e858db..ed40a831896 100644 --- a/jdk/make/lib/Lib-java.instrument.gmk +++ b/jdk/make/lib/Lib-java.instrument.gmk @@ -61,7 +61,7 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBINSTRUMENT, \ OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ SRC := $(LIBINSTRUMENT_SRC), \ OPTIMIZATION := LOW, \ - CFLAGS := $(LIBINSTRUMENT_CFLAGS) $(CFLAGS_WARNINGS_ARE_ERRORS), \ + CFLAGS := $(LIBINSTRUMENT_CFLAGS), \ CFLAGS_debug := -DJPLIS_LOGGING, \ CFLAGS_release := -DNO_JPLIS_LOGGING, \ MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libinstrument/mapfile-vers, \ diff --git a/jdk/make/lib/Lib-java.management.gmk b/jdk/make/lib/Lib-java.management.gmk index b0062445341..9f5aaaed3a9 100644 --- a/jdk/make/lib/Lib-java.management.gmk +++ b/jdk/make/lib/Lib-java.management.gmk @@ -50,7 +50,7 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBMANAGEMENT, \ OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ SRC := $(LIBMANAGEMENT_SRC), \ OPTIMIZATION := $(LIBMANAGEMENT_OPTIMIZATION), \ - CFLAGS := $(CFLAGS_JDKLIB) $(CFLAGS_WARNINGS_ARE_ERRORS) $(LIBMANAGEMENT_CFLAGS), \ + CFLAGS := $(CFLAGS_JDKLIB) $(LIBMANAGEMENT_CFLAGS), \ MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libmanagement/mapfile-vers, \ LDFLAGS := $(LDFLAGS_JDKLIB) \ $(call SET_SHARED_LIBRARY_ORIGIN), \ diff --git a/jdk/make/lib/Lib-jdk.attach.gmk b/jdk/make/lib/Lib-jdk.attach.gmk index dbabea5b733..d1bdd5e1bc0 100644 --- a/jdk/make/lib/Lib-jdk.attach.gmk +++ b/jdk/make/lib/Lib-jdk.attach.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -39,7 +39,7 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBATTACH, \ OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ SRC := $(call FindSrcDirsForLib, jdk.attach, attach), \ OPTIMIZATION := LOW, \ - CFLAGS := $(CFLAGS_JDKLIB) $(CFLAGS_WARNINGS_ARE_ERRORS) \ + CFLAGS := $(CFLAGS_JDKLIB) \ -I$(SUPPORT_OUTPUTDIR)/headers/jdk.attach \ $(LIBJAVA_HEADER_FLAGS) $(LIBATTACH_CFLAGS), \ CFLAGS_windows := /Gy, \ diff --git a/jdk/make/lib/Lib-jdk.hprof.agent.gmk b/jdk/make/lib/Lib-jdk.hprof.agent.gmk index 7c43331b33c..c6d1bbd1d04 100644 --- a/jdk/make/lib/Lib-jdk.hprof.agent.gmk +++ b/jdk/make/lib/Lib-jdk.hprof.agent.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,7 @@ BUILD_LIBHPROF_SRC := $(call FindSrcDirsForLib, jdk.hprof.agent, hprof) BUILD_LIBHPROF_CFLAGS := $(addprefix -I, $(BUILD_LIBHPROF_SRC)) \ -I$(JDK_TOPDIR)/src/demo/share/jvmti/java_crw_demo - + BUILD_LIBHPROF_LDFLAGS := LIBHPROF_OPTIMIZATION := HIGHEST @@ -46,7 +46,7 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBHPROF, \ OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ SRC := $(BUILD_LIBHPROF_SRC), \ OPTIMIZATION := $(LIBHPROF_OPTIMIZATION), \ - CFLAGS := $(CFLAGS_JDKLIB) $(CFLAGS_WARNINGS_ARE_ERRORS) \ + CFLAGS := $(CFLAGS_JDKLIB) \ $(BUILD_LIBHPROF_CFLAGS), \ CFLAGS_debug := -DHPROF_LOGGING, \ MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libhprof/mapfile-vers, \ @@ -75,7 +75,7 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBJAVA_CRW_DEMO, \ OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ SRC := $(LIBJAVA_CRW_DEMO_SRC), \ OPTIMIZATION := LOW, \ - CFLAGS := $(CFLAGS_JDKLIB) $(CFLAGS_WARNINGS_ARE_ERRORS) \ + CFLAGS := $(CFLAGS_JDKLIB) \ $(addprefix -I, $(LIBJAVA_CRW_DEMO_SRC)), \ MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libjava_crw_demo/mapfile-vers, \ LDFLAGS := $(LDFLAGS_JDKLIB) \ diff --git a/jdk/make/lib/Lib-jdk.jdi.gmk b/jdk/make/lib/Lib-jdk.jdi.gmk index eb1de3b43c1..74d246f50d4 100644 --- a/jdk/make/lib/Lib-jdk.jdi.gmk +++ b/jdk/make/lib/Lib-jdk.jdi.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -44,7 +44,7 @@ ifeq ($(OPENJDK_TARGET_OS), windows) OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ SRC := $(LIBDT_SHMEM_SRC), \ OPTIMIZATION := LOW, \ - CFLAGS := $(CFLAGS_JDKLIB) $(CFLAGS_WARNINGS_ARE_ERRORS) -DUSE_MMAP \ + CFLAGS := $(CFLAGS_JDKLIB) -DUSE_MMAP \ $(LIBDT_SHMEM_CPPFLAGS), \ LDFLAGS := $(LDFLAGS_JDKLIB), \ LDFLAGS_windows := -export:jdwpTransport_OnLoad, \ diff --git a/jdk/make/lib/Lib-jdk.jdwp.agent.gmk b/jdk/make/lib/Lib-jdk.jdwp.agent.gmk index 0743ac85a97..03a1d3e3965 100644 --- a/jdk/make/lib/Lib-jdk.jdwp.agent.gmk +++ b/jdk/make/lib/Lib-jdk.jdwp.agent.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -41,7 +41,7 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBDT_SOCKET, \ OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ SRC := $(LIBDT_SOCKET_SRC), \ OPTIMIZATION := LOW, \ - CFLAGS := $(CFLAGS_JDKLIB) $(CFLAGS_CFLAGS_WARNINGS_ARE_ERRORS) -DUSE_MMAP \ + CFLAGS := $(CFLAGS_JDKLIB) -DUSE_MMAP \ $(LIBDT_SOCKET_CPPFLAGS), \ MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libdt_socket/mapfile-vers, \ LDFLAGS := $(LDFLAGS_JDKLIB) \ @@ -77,7 +77,7 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBJDWP, \ OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ SRC := $(LIBJDWP_SRC), \ OPTIMIZATION := LOW, \ - CFLAGS := $(CFLAGS_JDKLIB) $(CFLAGS_WARNINGS_ARE_ERRORS) -DJDWP_LOGGING \ + CFLAGS := $(CFLAGS_JDKLIB) -DJDWP_LOGGING \ $(LIBJDWP_CPPFLAGS) \ -I$(SUPPORT_OUTPUTDIR)/headers/jdk.jdwp.agent, \ MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libjdwp/mapfile-vers, \ diff --git a/jdk/make/lib/Lib-jdk.management.gmk b/jdk/make/lib/Lib-jdk.management.gmk index 84e71f7060c..8950738b453 100644 --- a/jdk/make/lib/Lib-jdk.management.gmk +++ b/jdk/make/lib/Lib-jdk.management.gmk @@ -59,7 +59,7 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBMANAGEMENT_EXT, \ SRC := $(LIBMANAGEMENT_EXT_SRC), \ LANG := C, \ OPTIMIZATION := $(LIBMANAGEMENT_EXT_OPTIMIZATION), \ - CFLAGS := $(CFLAGS_JDKLIB) $(CFLAGS_WARNINGS_ARE_ERRORS) $(LIBMANAGEMENT_EXT_CFLAGS), \ + CFLAGS := $(CFLAGS_JDKLIB) $(LIBMANAGEMENT_EXT_CFLAGS), \ MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libmanagement_ext/mapfile-vers, \ LDFLAGS := $(LDFLAGS_JDKLIB) \ $(call SET_SHARED_LIBRARY_ORIGIN), \ diff --git a/jdk/make/lib/Lib-jdk.sctp.gmk b/jdk/make/lib/Lib-jdk.sctp.gmk index 15db8b3b380..dff84900595 100644 --- a/jdk/make/lib/Lib-jdk.sctp.gmk +++ b/jdk/make/lib/Lib-jdk.sctp.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -30,12 +30,8 @@ include LibCommon.gmk ifeq ($(OPENJDK_TARGET_OS_TYPE), unix) ifeq (, $(filter $(OPENJDK_TARGET_OS), macosx aix)) - - # Suppress unused parameters required by exported JNI functions. - SCTP_WERROR := -Werror -Wno-error=unused-parameter - ifeq ($(OPENJDK_TARGET_CPU_ARCH), ppc) - SCTP_WERROR := - endif + # DISABLED_WARNINGS_gcc := unused-parameter needed to + # suppress unused parameters required by exported JNI functions. $(eval $(call SetupNativeCompilation,BUILD_LIBSCTP, \ LIBRARY := sctp, \ @@ -49,7 +45,7 @@ ifeq ($(OPENJDK_TARGET_OS_TYPE), unix) $(LIBJAVA_HEADER_FLAGS) \ -I$(SUPPORT_OUTPUTDIR)/headers/jdk.sctp \ -I$(SUPPORT_OUTPUTDIR)/headers/java.base, \ - CFLAGS_linux := $(SCTP_WERROR), \ + DISABLED_WARNINGS_gcc := unused-parameter, \ MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libsctp/mapfile-vers, \ LDFLAGS := $(LDFLAGS_JDKLIB) \ $(call SET_SHARED_LIBRARY_ORIGIN), \ From ee5103f696b2c1c670106f68179a13dcfc8f0edd Mon Sep 17 00:00:00 2001 From: Rob McKenna Date: Mon, 8 Jun 2015 21:35:36 +0100 Subject: [PATCH 7/8] 7130985: Four helper classes missing in Sun JDK Reviewed-by: coffeys, msheppar --- .../ssl/SignatureAndHashAlgorithm.java | 3 + .../7130985/CorbaExceptionsCompileTest.java | 119 ++++++++++++++++++ .../corba/7130985/CorbaExceptionsTest.java | 35 ++++++ 3 files changed, 157 insertions(+) create mode 100644 jdk/test/com/sun/corba/7130985/CorbaExceptionsCompileTest.java create mode 100644 jdk/test/com/sun/corba/7130985/CorbaExceptionsTest.java diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/SignatureAndHashAlgorithm.java b/jdk/src/java.base/share/classes/sun/security/ssl/SignatureAndHashAlgorithm.java index 9640ffb8589..453f6752ed0 100644 --- a/jdk/src/java.base/share/classes/sun/security/ssl/SignatureAndHashAlgorithm.java +++ b/jdk/src/java.base/share/classes/sun/security/ssl/SignatureAndHashAlgorithm.java @@ -28,6 +28,7 @@ package sun.security.ssl; import java.security.AlgorithmConstraints; import java.security.CryptoPrimitive; import java.security.PrivateKey; +import java.security.Security; import java.util.Set; import java.util.HashSet; @@ -415,10 +416,12 @@ final class SignatureAndHashAlgorithm { "SHA1withRSA", --p); supports(HashAlgorithm.SHA1, SignatureAlgorithm.ECDSA, "SHA1withECDSA", --p); + if (Security.getProvider("SunMSCAPI") == null) { supports(HashAlgorithm.SHA224, SignatureAlgorithm.RSA, "SHA224withRSA", --p); supports(HashAlgorithm.SHA224, SignatureAlgorithm.ECDSA, "SHA224withECDSA", --p); + } supports(HashAlgorithm.SHA256, SignatureAlgorithm.RSA, "SHA256withRSA", --p); supports(HashAlgorithm.SHA256, SignatureAlgorithm.ECDSA, diff --git a/jdk/test/com/sun/corba/7130985/CorbaExceptionsCompileTest.java b/jdk/test/com/sun/corba/7130985/CorbaExceptionsCompileTest.java new file mode 100644 index 00000000000..6130a83420d --- /dev/null +++ b/jdk/test/com/sun/corba/7130985/CorbaExceptionsCompileTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 7130985 + * @summary Four helper classes missing in Sun JDK + * @library /lib/testlibrary + * @build jdk.testlibrary.* + * @run main CorbaExceptionsCompileTest + */ + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.rmi.RemoteException; +import org.omg.CORBA.ORBPackage.InvalidName; +import org.omg.CORBA.TypeCodePackage.BadKind; +import org.omg.CORBA.TypeCodePackage.Bounds; + +import jdk.testlibrary.FileUtils; +import jdk.testlibrary.JDKToolLauncher; + +public class CorbaExceptionsCompileTest implements CorbaExceptionsTest { + + public CorbaExceptionsCompileTest() { + super(); + } + + public void testExceptionInvalidName() + throws java.rmi.RemoteException, InvalidName {} + + public void testExceptionBounds() + throws java.rmi.RemoteException, Bounds {} + + public void testExceptionBadKind() + throws java.rmi.RemoteException, BadKind {} + + public void testExceptionCorba_Bounds() + throws java.rmi.RemoteException, org.omg.CORBA.Bounds {} + + public static void main(String[] args) throws Exception { + final File f = new File( + CorbaExceptionsCompileTest.class.getProtectionDomain() + .getCodeSource().getLocation().getPath()); + System.out.println(f.getCanonicalPath()); + ProcessBuilder pb = new ProcessBuilder("ls", "-l"); + pb.directory(f); + Process p = pb.start(); + p.waitFor(); + if (p.exitValue() == 0) { + try (BufferedReader br = new BufferedReader( + new InputStreamReader(p.getInputStream()))) { + StringBuilder builder = new StringBuilder(); + String line = null; + while ( (line = br.readLine()) != null) { + builder.append(line + "\n"); + } + String result = builder.toString(); + System.out.println(result); + } + } + + Path outDir = Paths.get("CorbaExceptionsCompileTest-compiled"); + outDir = Files.createDirectory(outDir).toAbsolutePath(); + JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("rmic"); + launcher.addToolArg("-classpath").addToolArg(f.getCanonicalPath()) + .addToolArg("-d").addToolArg(outDir.toString()) + .addToolArg("-iiop").addToolArg("CorbaExceptionsCompileTest"); + + pb = new ProcessBuilder(launcher.getCommand()); + pb.directory(f); + System.out.println("Working Directory: " + pb.directory()); + System.out.println("CorbaExceptionsCompileTest.class exists: " + + new File(f, "CorbaExceptionsCompileTest.class").exists()); + + p = pb.start(); + p.waitFor(); + if (p.exitValue() != 0) { + try (BufferedReader br = new BufferedReader( + new InputStreamReader(p.getInputStream()))) { + StringBuilder builder = new StringBuilder(); + String line = null; + while ( (line = br.readLine()) != null) { + builder.append(line + "\n"); + } + String result = builder.toString(); + System.out.println(result); + throw new RuntimeException(launcher.getCommand() + + " -iiop CorbaExceptionsCompileTest failed with status: " + + p.exitValue()); + } + } + + if (Files.exists(outDir)) + FileUtils.deleteFileTreeWithRetry(outDir); + } +} diff --git a/jdk/test/com/sun/corba/7130985/CorbaExceptionsTest.java b/jdk/test/com/sun/corba/7130985/CorbaExceptionsTest.java new file mode 100644 index 00000000000..da0a2587ff4 --- /dev/null +++ b/jdk/test/com/sun/corba/7130985/CorbaExceptionsTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.rmi.Remote; +import org.omg.CORBA.ORBPackage.InvalidName; +import org.omg.CORBA.TypeCodePackage.BadKind; +import org.omg.CORBA.TypeCodePackage.Bounds; + +public interface CorbaExceptionsTest extends Remote { + public void testExceptionInvalidName() throws java.rmi.RemoteException, InvalidName; + public void testExceptionBounds() throws java.rmi.RemoteException, Bounds; + public void testExceptionBadKind() throws java.rmi.RemoteException, BadKind; + public void testExceptionCorba_Bounds() throws java.rmi.RemoteException, org.omg.CORBA.Bounds; +} + From 36d62dcbb1e7eb2ede664d9fd271a3dd0f9c21ee Mon Sep 17 00:00:00 2001 From: Sunny Chan Date: Tue, 9 Jun 2015 07:05:48 +0100 Subject: [PATCH 8/8] 8080945: Improve the performance of primitive Arrays.sort for certain patterns of array elements Co-authored-by: Mohammad Rezaei Reviewed-by: psandoz --- .../classes/java/util/DualPivotQuicksort.java | 127 +-- .../Arrays/SortingIntBenchmarkTestJMH.java | 708 +++++++++++++++++ .../Arrays/SortingLongBenchmarkTestJMH.java | 725 ++++++++++++++++++ .../Arrays/SortingNearlySortedPrimitive.java | 274 +++++++ 4 files changed, 1780 insertions(+), 54 deletions(-) create mode 100644 jdk/test/java/util/Arrays/SortingIntBenchmarkTestJMH.java create mode 100644 jdk/test/java/util/Arrays/SortingLongBenchmarkTestJMH.java create mode 100644 jdk/test/java/util/Arrays/SortingNearlySortedPrimitive.java diff --git a/jdk/src/java.base/share/classes/java/util/DualPivotQuicksort.java b/jdk/src/java.base/share/classes/java/util/DualPivotQuicksort.java index 4682ddee5d8..fbc0359ed37 100644 --- a/jdk/src/java.base/share/classes/java/util/DualPivotQuicksort.java +++ b/jdk/src/java.base/share/classes/java/util/DualPivotQuicksort.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,11 +60,6 @@ final class DualPivotQuicksort { */ private static final int MAX_RUN_COUNT = 67; - /** - * The maximum length of run in merge sort. - */ - private static final int MAX_RUN_LENGTH = 33; - /** * If the length of an array to be sorted is less than this * constant, Quicksort is used in preference to merge sort. @@ -121,20 +116,24 @@ final class DualPivotQuicksort { // Check if the array is nearly sorted for (int k = left; k < right; run[count] = k) { + // Equal items in the beginning of the sequence + while (k < right && a[k] == a[k + 1]) + k++; + if (k == right) break; // Sequence finishes with equal items if (a[k] < a[k + 1]) { // ascending while (++k <= right && a[k - 1] <= a[k]); } else if (a[k] > a[k + 1]) { // descending while (++k <= right && a[k - 1] >= a[k]); + // Transform into an ascending sequence for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) { int t = a[lo]; a[lo] = a[hi]; a[hi] = t; } - } else { // equal - for (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k]; ) { - if (--m == 0) { - sort(a, left, right, true); - return; - } - } + } + + // Merge a transformed descending sequence followed by an + // ascending sequence + if (run[count] > left && a[run[count]] >= a[run[count] - 1]) { + count--; } /* @@ -151,7 +150,7 @@ final class DualPivotQuicksort { // Implementation note: variable "right" is increased by 1. if (run[count] == right++) { // The last run contains one element run[++count] = right; - } else if (count == 1) { // The array is already sorted + } else if (count <= 1) { // The array is already sorted return; } @@ -569,20 +568,24 @@ final class DualPivotQuicksort { // Check if the array is nearly sorted for (int k = left; k < right; run[count] = k) { + // Equal items in the beginning of the sequence + while (k < right && a[k] == a[k + 1]) + k++; + if (k == right) break; // Sequence finishes with equal items if (a[k] < a[k + 1]) { // ascending while (++k <= right && a[k - 1] <= a[k]); } else if (a[k] > a[k + 1]) { // descending while (++k <= right && a[k - 1] >= a[k]); + // Transform into an ascending sequence for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) { long t = a[lo]; a[lo] = a[hi]; a[hi] = t; } - } else { // equal - for (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k]; ) { - if (--m == 0) { - sort(a, left, right, true); - return; - } - } + } + + // Merge a transformed descending sequence followed by an + // ascending sequence + if (run[count] > left && a[run[count]] >= a[run[count] - 1]) { + count--; } /* @@ -599,7 +602,7 @@ final class DualPivotQuicksort { // Implementation note: variable "right" is increased by 1. if (run[count] == right++) { // The last run contains one element run[++count] = right; - } else if (count == 1) { // The array is already sorted + } else if (count <= 1) { // The array is already sorted return; } @@ -1053,20 +1056,24 @@ final class DualPivotQuicksort { // Check if the array is nearly sorted for (int k = left; k < right; run[count] = k) { + // Equal items in the beginning of the sequence + while (k < right && a[k] == a[k + 1]) + k++; + if (k == right) break; // Sequence finishes with equal items if (a[k] < a[k + 1]) { // ascending while (++k <= right && a[k - 1] <= a[k]); } else if (a[k] > a[k + 1]) { // descending while (++k <= right && a[k - 1] >= a[k]); + // Transform into an ascending sequence for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) { short t = a[lo]; a[lo] = a[hi]; a[hi] = t; } - } else { // equal - for (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k]; ) { - if (--m == 0) { - sort(a, left, right, true); - return; - } - } + } + + // Merge a transformed descending sequence followed by an + // ascending sequence + if (run[count] > left && a[run[count]] >= a[run[count] - 1]) { + count--; } /* @@ -1083,7 +1090,7 @@ final class DualPivotQuicksort { // Implementation note: variable "right" is increased by 1. if (run[count] == right++) { // The last run contains one element run[++count] = right; - } else if (count == 1) { // The array is already sorted + } else if (count <= 1) { // The array is already sorted return; } @@ -1537,20 +1544,24 @@ final class DualPivotQuicksort { // Check if the array is nearly sorted for (int k = left; k < right; run[count] = k) { + // Equal items in the beginning of the sequence + while (k < right && a[k] == a[k + 1]) + k++; + if (k == right) break; // Sequence finishes with equal items if (a[k] < a[k + 1]) { // ascending while (++k <= right && a[k - 1] <= a[k]); } else if (a[k] > a[k + 1]) { // descending while (++k <= right && a[k - 1] >= a[k]); + // Transform into an ascending sequence for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) { char t = a[lo]; a[lo] = a[hi]; a[hi] = t; } - } else { // equal - for (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k]; ) { - if (--m == 0) { - sort(a, left, right, true); - return; - } - } + } + + // Merge a transformed descending sequence followed by an + // ascending sequence + if (run[count] > left && a[run[count]] >= a[run[count] - 1]) { + count--; } /* @@ -1567,7 +1578,7 @@ final class DualPivotQuicksort { // Implementation note: variable "right" is increased by 1. if (run[count] == right++) { // The last run contains one element run[++count] = right; - } else if (count == 1) { // The array is already sorted + } else if (count <= 1) { // The array is already sorted return; } @@ -2117,20 +2128,24 @@ final class DualPivotQuicksort { // Check if the array is nearly sorted for (int k = left; k < right; run[count] = k) { + // Equal items in the beginning of the sequence + while (k < right && a[k] == a[k + 1]) + k++; + if (k == right) break; // Sequence finishes with equal items if (a[k] < a[k + 1]) { // ascending while (++k <= right && a[k - 1] <= a[k]); } else if (a[k] > a[k + 1]) { // descending while (++k <= right && a[k - 1] >= a[k]); + // Transform into an ascending sequence for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) { float t = a[lo]; a[lo] = a[hi]; a[hi] = t; } - } else { // equal - for (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k]; ) { - if (--m == 0) { - sort(a, left, right, true); - return; - } - } + } + + // Merge a transformed descending sequence followed by an + // ascending sequence + if (run[count] > left && a[run[count]] >= a[run[count] - 1]) { + count--; } /* @@ -2147,7 +2162,7 @@ final class DualPivotQuicksort { // Implementation note: variable "right" is increased by 1. if (run[count] == right++) { // The last run contains one element run[++count] = right; - } else if (count == 1) { // The array is already sorted + } else if (count <= 1) { // The array is already sorted return; } @@ -2656,20 +2671,24 @@ final class DualPivotQuicksort { // Check if the array is nearly sorted for (int k = left; k < right; run[count] = k) { + // Equal items in the beginning of the sequence + while (k < right && a[k] == a[k + 1]) + k++; + if (k == right) break; // Sequence finishes with equal items if (a[k] < a[k + 1]) { // ascending while (++k <= right && a[k - 1] <= a[k]); } else if (a[k] > a[k + 1]) { // descending while (++k <= right && a[k - 1] >= a[k]); + // Transform into an ascending sequence for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) { double t = a[lo]; a[lo] = a[hi]; a[hi] = t; } - } else { // equal - for (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k]; ) { - if (--m == 0) { - sort(a, left, right, true); - return; - } - } + } + + // Merge a transformed descending sequence followed by an + // ascending sequence + if (run[count] > left && a[run[count]] >= a[run[count] - 1]) { + count--; } /* @@ -2686,7 +2705,7 @@ final class DualPivotQuicksort { // Implementation note: variable "right" is increased by 1. if (run[count] == right++) { // The last run contains one element run[++count] = right; - } else if (count == 1) { // The array is already sorted + } else if (count <= 1) { // The array is already sorted return; } diff --git a/jdk/test/java/util/Arrays/SortingIntBenchmarkTestJMH.java b/jdk/test/java/util/Arrays/SortingIntBenchmarkTestJMH.java new file mode 100644 index 00000000000..bd0ab1366f1 --- /dev/null +++ b/jdk/test/java/util/Arrays/SortingIntBenchmarkTestJMH.java @@ -0,0 +1,708 @@ +/* + * Copyright 2015 Goldman Sachs. + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +@State(Scope.Thread) +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +public class SortingIntBenchmarkTestJMH { + private static final int QUICKSORT_THRESHOLD = 286; + private static final int MAX_RUN_COUNT = 67; + private static final int INSERTION_SORT_THRESHOLD = 47; + public static final int MAX_VALUE = 1_000_000; + + @Param({"pairFlipZeroPairFlip", "pairFlipOneHundredPairFlip" + , "zeroHi", "hiZeroLow", "hiFlatLow", "identical", + "randomDups", "randomNoDups", "sortedReversedSorted", "pairFlip", "endLessThan"}) + + public String listType; + + private int[] array; + private static final int LIST_SIZE = 10_000_000; + public static final int NUMBER_OF_ITERATIONS = 10; + + @Setup + public void setUp() { + Random random = new Random(123456789012345L); + this.array = new int[LIST_SIZE]; + int threeQuarters = (int) (LIST_SIZE * 0.75); + if ("zeroHi".equals(this.listType)) { + for (int i = 0; i < threeQuarters; i++) { + this.array[i] = 0; + } + int k = 1; + for (int i = threeQuarters; i < LIST_SIZE; i++) { + this.array[i] = k; + k++; + } + } + else if ("hiFlatLow".equals(this.listType)) { + int oneThird = LIST_SIZE / 3; + for (int i = 0; i < oneThird; i++) { + this.array[i] = i; + } + int twoThirds = oneThird * 2; + int constant = oneThird - 1; + for (int i = oneThird; i < twoThirds; i++) { + this.array[i] = constant; + } + for (int i = twoThirds; i < LIST_SIZE; i++) { + this.array[i] = constant - i + twoThirds; + } + } + else if ("hiZeroLow".equals(this.listType)) { + int oneThird = LIST_SIZE / 3; + for (int i = 0; i < oneThird; i++) { + this.array[i] = i; + } + int twoThirds = oneThird * 2; + for (int i = oneThird; i < twoThirds; i++) { + this.array[i] = 0; + } + for (int i = twoThirds; i < LIST_SIZE; i++) { + this.array[i] = oneThird - i + twoThirds; + } + } + else if ("identical".equals(this.listType)) { + for (int i = 0; i < LIST_SIZE; i++) { + this.array[i] = 0; + } + } + else if ("randomDups".equals(this.listType)) { + for (int i = 0; i < LIST_SIZE; i++) { + this.array[i] = random.nextInt(1000); + } + } + else if ("randomNoDups".equals(this.listType)) { + Set set = new HashSet(); + while (set.size() < LIST_SIZE + 1) { + set.add(random.nextInt()); + } + List list = new ArrayList<>(LIST_SIZE); + list.addAll(set); + for (int i = 0; i < LIST_SIZE; i++) { + this.array[i] = list.get(i); + } + } + else if ("sortedReversedSorted".equals(this.listType)) { + for (int i = 0; i < LIST_SIZE / 2; i++) { + this.array[i] = i; + } + int num = 0; + for (int i = LIST_SIZE / 2; i < LIST_SIZE; i++) { + this.array[i] = LIST_SIZE - num; + num++; + } + } + else if ("pairFlip".equals(this.listType)) { + for (int i = 0; i < LIST_SIZE; i++) { + this.array[i] = i; + } + for (int i = 0; i < LIST_SIZE; i += 2) { + int temp = this.array[i]; + this.array[i] = this.array[i + 1]; + this.array[i + 1] = temp; + } + } + else if ("endLessThan".equals(this.listType)) { + for (int i = 0; i < LIST_SIZE - 1; i++) { + this.array[i] = 3; + } + this.array[LIST_SIZE - 1] = 1; + } + else if ("pairFlipZeroPairFlip".equals(this.listType)) { + //pairflip + for (int i = 0; i < 64; i++) { + this.array[i] = i; + } + for (int i = 0; i < 64; i += 2) { + int temp = this.array[i]; + this.array[i] = this.array[i + 1]; + this.array[i + 1] = temp; + } + //zero + for (int i = 64; i < this.array.length - 64; i++) { + this.array[i] = 0; + } + //pairflip + for (int i = this.array.length - 64; i < this.array.length; i++) { + this.array[i] = i; + } + for (int i = this.array.length - 64; i < this.array.length; i += 2) { + int temp = this.array[i]; + this.array[i] = this.array[i + 1]; + this.array[i + 1] = temp; + } + } + else if ("pairFlipOneHundredPairFlip".equals(this.listType)) { + //10, 5 + for (int i = 0; i < 64; i++) { + if (i % 2 == 0) { + this.array[i] = 10; + } + else { + this.array[i] = 5; + } + } + + //100 + for (int i = 64; i < this.array.length - 64; i++) { + this.array[i] = 100; + } + + //10, 5 + for (int i = this.array.length - 64; i < this.array.length; i++) { + if (i % 2 == 0) { + this.array[i] = 10; + } + else { + this.array[i] = 5; + } + } + } + } + + @Warmup(iterations = 20) + @Measurement(iterations = 10) + @Benchmark + public void sortNewWay() { + for (int i = 0; i < NUMBER_OF_ITERATIONS; i++) { + SortingIntTestJMH.sort(this.array, 0, this.array.length - 1, null, 0, 0); + } + } + + @Warmup(iterations = 20) + @Measurement(iterations = 10) + @Benchmark + public void sortCurrentWay() { + for (int i = 0; i < NUMBER_OF_ITERATIONS; i++) { + Arrays.sort(this.array); + } + } + + static void sort(int[] a, int left, int right, + int[] work, int workBase, int workLen) { + // Use Quicksort on small arrays + if (right - left < QUICKSORT_THRESHOLD) { + SortingIntTestJMH.sort(a, left, right, true); + return; + } + + /* + * Index run[i] is the start of i-th run + * (ascending or descending sequence). + */ + int[] run = new int[MAX_RUN_COUNT + 1]; + int count = 0; + run[0] = left; + + // Check if the array is nearly sorted + for (int k = left; k < right; run[count] = k) { + while (k < right && a[k] == a[k + 1]) + k++; + if (k == right) break; + if (a[k] < a[k + 1]) { // ascending + while (++k <= right && a[k - 1] <= a[k]) ; + } + else if (a[k] > a[k + 1]) { // descending + while (++k <= right && a[k - 1] >= a[k]) ; + for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) { + int t = a[lo]; + a[lo] = a[hi]; + a[hi] = t; + } + } + if (run[count] > left && a[run[count]] >= a[run[count] - 1]) { + count--; + } + /* + * The array is not highly structured, + * use Quicksort instead of merge sort. + */ + if (++count == MAX_RUN_COUNT) { + sort(a, left, right, true); + return; + } + } + + // Check special cases + // Implementation note: variable "right" is increased by 1. + if (run[count] == right++) { + run[++count] = right; + } + if (count <= 1) { // The array is already sorted + return; + } + + // Determine alternation base for merge + byte odd = 0; + for (int n = 1; (n <<= 1) < count; odd ^= 1) { + } + + // Use or create temporary array b for merging + int[] b; // temp array; alternates with a + int ao, bo; // array offsets from 'left' + int blen = right - left; // space needed for b + if (work == null || workLen < blen || workBase + blen > work.length) { + work = new int[blen]; + workBase = 0; + } + if (odd == 0) { + System.arraycopy(a, left, work, workBase, blen); + b = a; + bo = 0; + a = work; + ao = workBase - left; + } + else { + b = work; + ao = 0; + bo = workBase - left; + } + + // Merging + for (int last; count > 1; count = last) { + for (int k = (last = 0) + 2; k <= count; k += 2) { + int hi = run[k], mi = run[k - 1]; + for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) { + if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) { + b[i + bo] = a[p++ + ao]; + } + else { + b[i + bo] = a[q++ + ao]; + } + } + run[++last] = hi; + } + if ((count & 1) != 0) { + for (int i = right, lo = run[count - 1]; --i >= lo; + b[i + bo] = a[i + ao] + ) { + } + run[++last] = right; + } + int[] t = a; + a = b; + b = t; + int o = ao; + ao = bo; + bo = o; + } + } + + private static void sort(int[] a, int left, int right, boolean leftmost) { + int length = right - left + 1; + + // Use insertion sort on tiny arrays + if (length < INSERTION_SORT_THRESHOLD) { + if (leftmost) { + /* + * Traditional (without sentinel) insertion sort, + * optimized for server VM, is used in case of + * the leftmost part. + */ + for (int i = left, j = i; i < right; j = ++i) { + int ai = a[i + 1]; + while (ai < a[j]) { + a[j + 1] = a[j]; + if (j-- == left) { + break; + } + } + a[j + 1] = ai; + } + } + else { + /* + * Skip the longest ascending sequence. + */ + do { + if (left >= right) { + return; + } + } + while (a[++left] >= a[left - 1]); + + /* + * Every element from adjoining part plays the role + * of sentinel, therefore this allows us to avoid the + * left range check on each iteration. Moreover, we use + * the more optimized algorithm, so called pair insertion + * sort, which is faster (in the context of Quicksort) + * than traditional implementation of insertion sort. + */ + for (int k = left; ++left <= right; k = ++left) { + int a1 = a[k], a2 = a[left]; + + if (a1 < a2) { + a2 = a1; + a1 = a[left]; + } + while (a1 < a[--k]) { + a[k + 2] = a[k]; + } + a[++k + 1] = a1; + + while (a2 < a[--k]) { + a[k + 1] = a[k]; + } + a[k + 1] = a2; + } + int last = a[right]; + + while (last < a[--right]) { + a[right + 1] = a[right]; + } + a[right + 1] = last; + } + return; + } + + // Inexpensive approximation of length / 7 + int seventh = (length >> 3) + (length >> 6) + 1; + + /* + * Sort five evenly spaced elements around (and including) the + * center element in the range. These elements will be used for + * pivot selection as described below. The choice for spacing + * these elements was empirically determined to work well on + * a wide variety of inputs. + */ + int e3 = (left + right) >>> 1; // The midpoint + int e2 = e3 - seventh; + int e1 = e2 - seventh; + int e4 = e3 + seventh; + int e5 = e4 + seventh; + + // Sort these elements using insertion sort + if (a[e2] < a[e1]) { + int t = a[e2]; + a[e2] = a[e1]; + a[e1] = t; + } + + if (a[e3] < a[e2]) { + int t = a[e3]; + a[e3] = a[e2]; + a[e2] = t; + if (t < a[e1]) { + a[e2] = a[e1]; + a[e1] = t; + } + } + if (a[e4] < a[e3]) { + int t = a[e4]; + a[e4] = a[e3]; + a[e3] = t; + if (t < a[e2]) { + a[e3] = a[e2]; + a[e2] = t; + if (t < a[e1]) { + a[e2] = a[e1]; + a[e1] = t; + } + } + } + if (a[e5] < a[e4]) { + int t = a[e5]; + a[e5] = a[e4]; + a[e4] = t; + if (t < a[e3]) { + a[e4] = a[e3]; + a[e3] = t; + if (t < a[e2]) { + a[e3] = a[e2]; + a[e2] = t; + if (t < a[e1]) { + a[e2] = a[e1]; + a[e1] = t; + } + } + } + } + + // Pointers + int less = left; // The index of the first element of center part + int great = right; // The index before the first element of right part + + if (a[e1] != a[e2] && a[e2] != a[e3] && a[e3] != a[e4] && a[e4] != a[e5]) { + /* + * Use the second and fourth of the five sorted elements as pivots. + * These values are inexpensive approximations of the first and + * second terciles of the array. Note that pivot1 <= pivot2. + */ + int pivot1 = a[e2]; + int pivot2 = a[e4]; + + /* + * The first and the last elements to be sorted are moved to the + * locations formerly occupied by the pivots. When partitioning + * is complete, the pivots are swapped back into their final + * positions, and excluded from subsequent sorting. + */ + a[e2] = a[left]; + a[e4] = a[right]; + + /* + * Skip elements, which are less or greater than pivot values. + */ + while (a[++less] < pivot1) { + } + while (a[--great] > pivot2) { + } + + /* + * Partitioning: + * + * left part center part right part + * +--------------------------------------------------------------+ + * | < pivot1 | pivot1 <= && <= pivot2 | ? | > pivot2 | + * +--------------------------------------------------------------+ + * ^ ^ ^ + * | | | + * less k great + * + * Invariants: + * + * all in (left, less) < pivot1 + * pivot1 <= all in [less, k) <= pivot2 + * all in (great, right) > pivot2 + * + * Pointer k is the first index of ?-part. + */ + outer: + for (int k = less - 1; ++k <= great; ) { + int ak = a[k]; + if (ak < pivot1) { // Move a[k] to left part + a[k] = a[less]; + /* + * Here and below we use "a[i] = b; i++;" instead + * of "a[i++] = b;" due to performance issue. + */ + a[less] = ak; + ++less; + } + else if (ak > pivot2) { // Move a[k] to right part + while (a[great] > pivot2) { + if (great-- == k) { + break outer; + } + } + if (a[great] < pivot1) { // a[great] <= pivot2 + a[k] = a[less]; + a[less] = a[great]; + ++less; + } + else { // pivot1 <= a[great] <= pivot2 + a[k] = a[great]; + } + /* + * Here and below we use "a[i] = b; i--;" instead + * of "a[i--] = b;" due to performance issue. + */ + a[great] = ak; + --great; + } + } + + // Swap pivots into their final positions + a[left] = a[less - 1]; + a[less - 1] = pivot1; + a[right] = a[great + 1]; + a[great + 1] = pivot2; + + // Sort left and right parts recursively, excluding known pivots + SortingIntTestJMH.sort(a, left, less - 2, leftmost); + SortingIntTestJMH.sort(a, great + 2, right, false); + + /* + * If center part is too large (comprises > 4/7 of the array), + * swap internal pivot values to ends. + */ + if (less < e1 && e5 < great) { + /* + * Skip elements, which are equal to pivot values. + */ + while (a[less] == pivot1) { + ++less; + } + + while (a[great] == pivot2) { + --great; + } + + /* + * Partitioning: + * + * left part center part right part + * +----------------------------------------------------------+ + * | == pivot1 | pivot1 < && < pivot2 | ? | == pivot2 | + * +----------------------------------------------------------+ + * ^ ^ ^ + * | | | + * less k great + * + * Invariants: + * + * all in (*, less) == pivot1 + * pivot1 < all in [less, k) < pivot2 + * all in (great, *) == pivot2 + * + * Pointer k is the first index of ?-part. + */ + outer: + for (int k = less - 1; ++k <= great; ) { + int ak = a[k]; + if (ak == pivot1) { // Move a[k] to left part + a[k] = a[less]; + a[less] = ak; + ++less; + } + else if (ak == pivot2) { // Move a[k] to right part + while (a[great] == pivot2) { + if (great-- == k) { + break outer; + } + } + if (a[great] == pivot1) { // a[great] < pivot2 + a[k] = a[less]; + /* + * Even though a[great] equals to pivot1, the + * assignment a[less] = pivot1 may be incorrect, + * if a[great] and pivot1 are floating-point zeros + * of different signs. Therefore in float and + * double sorting methods we have to use more + * accurate assignment a[less] = a[great]. + */ + a[less] = pivot1; + ++less; + } + else { // pivot1 < a[great] < pivot2 + a[k] = a[great]; + } + a[great] = ak; + --great; + } + } + } + + // Sort center part recursively + SortingIntTestJMH.sort(a, less, great, false); + } + else { // Partitioning with one pivot + /* + * Use the third of the five sorted elements as pivot. + * This value is inexpensive approximation of the median. + */ + int pivot = a[e3]; + + /* + * Partitioning degenerates to the traditional 3-way + * (or "Dutch National Flag") schema: + * + * left part center part right part + * +-------------------------------------------------+ + * | < pivot | == pivot | ? | > pivot | + * +-------------------------------------------------+ + * ^ ^ ^ + * | | | + * less k great + * + * Invariants: + * + * all in (left, less) < pivot + * all in [less, k) == pivot + * all in (great, right) > pivot + * + * Pointer k is the first index of ?-part. + */ + for (int k = less; k <= great; ++k) { + if (a[k] == pivot) { + continue; + } + int ak = a[k]; + if (ak < pivot) { // Move a[k] to left part + a[k] = a[less]; + a[less] = ak; + ++less; + } + else { // a[k] > pivot - Move a[k] to right part + while (a[great] > pivot) { + --great; + } + if (a[great] < pivot) { // a[great] <= pivot + a[k] = a[less]; + a[less] = a[great]; + ++less; + } + else { // a[great] == pivot + /* + * Even though a[great] equals to pivot, the + * assignment a[k] = pivot may be incorrect, + * if a[great] and pivot are floating-point + * zeros of different signs. Therefore in float + * and double sorting methods we have to use + * more accurate assignment a[k] = a[great]. + */ + a[k] = pivot; + } + a[great] = ak; + --great; + } + } + + /* + * Sort left and right parts recursively. + * All elements from center part are equal + * and, therefore, already sorted. + */ + SortingIntTestJMH.sort(a, left, less - 1, leftmost); + SortingIntTestJMH.sort(a, great + 1, right, false); + } + } + + private static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } +} diff --git a/jdk/test/java/util/Arrays/SortingLongBenchmarkTestJMH.java b/jdk/test/java/util/Arrays/SortingLongBenchmarkTestJMH.java new file mode 100644 index 00000000000..d1e9ae60119 --- /dev/null +++ b/jdk/test/java/util/Arrays/SortingLongBenchmarkTestJMH.java @@ -0,0 +1,725 @@ +/* + * Copyright 2015 Goldman Sachs. + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +@State(Scope.Thread) +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +public class SortingLongBenchmarkTestJMH { + private static final int QUICKSORT_THRESHOLD = 286; + private static final int MAX_RUN_COUNT = 67; + private static final int INSERTION_SORT_THRESHOLD = 47; + public static final int MAX_VALUE = 1_000_000; + + @Param({"pairFlipZeroPairFlip", "descendingAscending", "zeroHi", "hiZeroLow", "hiFlatLow", "identical", + "randomDups", "randomNoDups", "sortedReversedSorted", "pairFlip", "endLessThan"}) + public String listType; + + private long[] array; + private static final int LIST_SIZE = 10_000_000; + public static final int NUMBER_OF_ITERATIONS = 10; + + @Setup + public void setUp() { + Random random = new Random(123456789012345L); + this.array = new long[LIST_SIZE]; + int threeQuarters = (int) (LIST_SIZE * 0.75); + if ("zeroHi".equals(this.listType)) { + for (int i = 0; i < threeQuarters; i++) { + this.array[i] = 0; + } + int k = 1; + for (int i = threeQuarters; i < LIST_SIZE; i++) { + this.array[i] = k; + k++; + } + } + else if ("hiFlatLow".equals(this.listType)) { + int oneThird = LIST_SIZE / 3; + for (int i = 0; i < oneThird; i++) { + this.array[i] = i; + } + int twoThirds = oneThird * 2; + int constant = oneThird - 1; + for (int i = oneThird; i < twoThirds; i++) { + this.array[i] = constant; + } + for (int i = twoThirds; i < LIST_SIZE; i++) { + this.array[i] = constant - i + twoThirds; + } + } + else if ("hiZeroLow".equals(this.listType)) { + int oneThird = LIST_SIZE / 3; + for (int i = 0; i < oneThird; i++) { + this.array[i] = i; + } + int twoThirds = oneThird * 2; + for (int i = oneThird; i < twoThirds; i++) { + this.array[i] = 0; + } + for (int i = twoThirds; i < LIST_SIZE; i++) { + this.array[i] = oneThird - i + twoThirds; + } + } + else if ("identical".equals(this.listType)) { + for (int i = 0; i < LIST_SIZE; i++) { + this.array[i] = 0; + } + } + else if ("randomDups".equals(this.listType)) { + for (int i = 0; i < LIST_SIZE; i++) { + this.array[i] = random.nextInt(1000); + } + } + else if ("randomNoDups".equals(this.listType)) { + Set set = new HashSet<>(); + while (set.size() < LIST_SIZE + 1) { + set.add(random.nextInt()); + } + List list = new ArrayList<>(LIST_SIZE); + list.addAll(set); + for (int i = 0; i < LIST_SIZE; i++) { + this.array[i] = list.get(i); + } + } + else if ("sortedReversedSorted".equals(this.listType)) { + for (int i = 0; i < LIST_SIZE / 2; i++) { + this.array[i] = i; + } + int num = 0; + for (int i = LIST_SIZE / 2; i < LIST_SIZE; i++) { + this.array[i] = LIST_SIZE - num; + num++; + } + } + else if ("pairFlip".equals(this.listType)) { + for (int i = 0; i < LIST_SIZE; i++) { + this.array[i] = i; + } + for (int i = 0; i < LIST_SIZE; i += 2) { + long temp = this.array[i]; + this.array[i] = this.array[i + 1]; + this.array[i + 1] = temp; + } + } + else if ("endLessThan".equals(this.listType)) { + for (int i = 0; i < LIST_SIZE - 1; i++) { + this.array[i] = 3; + } + this.array[LIST_SIZE - 1] = 1; + } + else if ("pairFlipZeroPairFlip".equals(this.listType)) { + //pairflip + for (int i = 0; i < 64; i++) { + this.array[i] = i; + } + for (int i = 0; i < 64; i += 2) { + long temp = this.array[i]; + this.array[i] = this.array[i + 1]; + this.array[i + 1] = temp; + } + //zero + for (int i = 64; i < this.array.length - 64; i++) { + this.array[i] = 0; + } + //pairflip + for (int i = this.array.length - 64; i < this.array.length; i++) { + this.array[i] = i; + } + for (int i = this.array.length - 64; i < this.array.length; i += 2) { + long temp = this.array[i]; + this.array[i] = this.array[i + 1]; + this.array[i + 1] = temp; + } + } + else if ("pairFlipOneHundredPairFlip".equals(this.listType)) { + //10, 5 + for (int i = 0; i < 64; i++) { + if (i % 2 == 0) { + this.array[i] = 10; + } + else { + this.array[i] = 5; + } + } + + //100 + for (int i = 64; i < this.array.length - 64; i++) { + this.array[i] = 100; + } + + //10, 5 + for (int i = this.array.length - 64; i < this.array.length; i++) { + if (i % 2 == 0) { + this.array[i] = 10; + } + else { + this.array[i] = 5; + } + } + } + } + + @Warmup(iterations = 20) + @Measurement(iterations = 10) + @Benchmark + public void sortNewWay() { + for (int i = 0; i < NUMBER_OF_ITERATIONS; i++) { + SortingLongTestJMH.sort(this.array, 0, this.array.length - 1, null, 0, 0); + } + } + + @Warmup(iterations = 20) + @Measurement(iterations = 10) + @Benchmark + public void sortOldWay() { + for (int i = 0; i < NUMBER_OF_ITERATIONS; i++) { + Arrays.sort(this.array); + } + } + + /** + * Sorts the specified range of the array using the given + * workspace array slice if possible for merging + * + * @param a the array to be sorted + * @param left the index of the first element, inclusive, to be sorted + * @param right the index of the last element, inclusive, to be sorted + * @param work a workspace array (slice) + * @param workBase origin of usable space in work array + * @param workLen usable size of work array + */ + static void sort(long[] a, int left, int right, + long[] work, int workBase, int workLen) { +// Use Quicksort on small arrays + if (right - left < QUICKSORT_THRESHOLD) { + SortingLongTestJMH.sort(a, left, right, true); + return; + } + + /* + * Index run[i] is the start of i-th run + * (ascending or descending sequence). + */ + int[] run = new int[MAX_RUN_COUNT + 1]; + int count = 0; + run[0] = left; + + // Check if the array is nearly sorted + for (int k = left; k < right; run[count] = k) { + while (k < right && a[k] == a[k + 1]) + k++; + if (k == right) break; + if (a[k] < a[k + 1]) { // ascending + while (++k <= right && a[k - 1] <= a[k]) ; + } + else if (a[k] > a[k + 1]) { // descending + while (++k <= right && a[k - 1] >= a[k]) ; + for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) { + long t = a[lo]; + a[lo] = a[hi]; + a[hi] = t; + } + } + if (run[count] > left && a[run[count]] >= a[run[count] - 1]) { + count--; + } + /* + * The array is not highly structured, + * use Quicksort instead of merge sort. + */ + if (++count == MAX_RUN_COUNT) { + sort(a, left, right, true); + return; + } + } + + // Check special cases + // Implementation note: variable "right" is increased by 1. + if (run[count] == right++) { + run[++count] = right; + } + if (count <= 1) { // The array is already sorted + return; + } + + // Determine alternation base for merge + byte odd = 0; + for (int n = 1; (n <<= 1) < count; odd ^= 1) { + } + + // Use or create temporary array b for merging + long[] b; // temp array; alternates with a + int ao, bo; // array offsets from 'left' + int blen = right - left; // space needed for b + if (work == null || workLen < blen || workBase + blen > work.length) { + work = new long[blen]; + workBase = 0; + } + if (odd == 0) { + System.arraycopy(a, left, work, workBase, blen); + b = a; + bo = 0; + a = work; + ao = workBase - left; + } + else { + b = work; + ao = 0; + bo = workBase - left; + } + + // Merging + for (int last; count > 1; count = last) { + for (int k = (last = 0) + 2; k <= count; k += 2) { + int hi = run[k], mi = run[k - 1]; + for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) { + if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) { + b[i + bo] = a[p++ + ao]; + } + else { + b[i + bo] = a[q++ + ao]; + } + } + run[++last] = hi; + } + if ((count & 1) != 0) { + for (int i = right, lo = run[count - 1]; --i >= lo; + b[i + bo] = a[i + ao] + ) { + } + run[++last] = right; + } + long[] t = a; + a = b; + b = t; + int o = ao; + ao = bo; + bo = o; + } + } + + /** + * Sorts the specified range of the array by Dual-Pivot Quicksort. + * + * @param a the array to be sorted + * @param left the index of the first element, inclusive, to be sorted + * @param right the index of the last element, inclusive, to be sorted + * @param leftmost indicates if this part is the leftmost in the range + */ + private static void sort(long[] a, int left, int right, boolean leftmost) { + int length = right - left + 1; + + // Use insertion sort on tiny arrays + if (length < INSERTION_SORT_THRESHOLD) { + if (leftmost) { + /* + * Traditional (without sentinel) insertion sort, + * optimized for server VM, is used in case of + * the leftmost part. + */ + for (int i = left, j = i; i < right; j = ++i) { + long ai = a[i + 1]; + while (ai < a[j]) { + a[j + 1] = a[j]; + if (j-- == left) { + break; + } + } + a[j + 1] = ai; + } + } + else { + /* + * Skip the longest ascending sequence. + */ + do { + if (left >= right) { + return; + } + } + while (a[++left] >= a[left - 1]); + + /* + * Every element from adjoining part plays the role + * of sentinel, therefore this allows us to avoid the + * left range check on each iteration. Moreover, we use + * the more optimized algorithm, so called pair insertion + * sort, which is faster (in the context of Quicksort) + * than traditional implementation of insertion sort. + */ + for (int k = left; ++left <= right; k = ++left) { + long a1 = a[k], a2 = a[left]; + + if (a1 < a2) { + a2 = a1; + a1 = a[left]; + } + while (a1 < a[--k]) { + a[k + 2] = a[k]; + } + a[++k + 1] = a1; + + while (a2 < a[--k]) { + a[k + 1] = a[k]; + } + a[k + 1] = a2; + } + long last = a[right]; + + while (last < a[--right]) { + a[right + 1] = a[right]; + } + a[right + 1] = last; + } + return; + } + + // Inexpensive approximation of length / 7 + int seventh = (length >> 3) + (length >> 6) + 1; + + /* + * Sort five evenly spaced elements around (and including) the + * center element in the range. These elements will be used for + * pivot selection as described below. The choice for spacing + * these elements was empirically determined to work well on + * a wide variety of inputs. + */ + int e3 = (left + right) >>> 1; // The midpoint + int e2 = e3 - seventh; + int e1 = e2 - seventh; + int e4 = e3 + seventh; + int e5 = e4 + seventh; + + // Sort these elements using insertion sort + if (a[e2] < a[e1]) { + long t = a[e2]; + a[e2] = a[e1]; + a[e1] = t; + } + + if (a[e3] < a[e2]) { + long t = a[e3]; + a[e3] = a[e2]; + a[e2] = t; + if (t < a[e1]) { + a[e2] = a[e1]; + a[e1] = t; + } + } + if (a[e4] < a[e3]) { + long t = a[e4]; + a[e4] = a[e3]; + a[e3] = t; + if (t < a[e2]) { + a[e3] = a[e2]; + a[e2] = t; + if (t < a[e1]) { + a[e2] = a[e1]; + a[e1] = t; + } + } + } + if (a[e5] < a[e4]) { + long t = a[e5]; + a[e5] = a[e4]; + a[e4] = t; + if (t < a[e3]) { + a[e4] = a[e3]; + a[e3] = t; + if (t < a[e2]) { + a[e3] = a[e2]; + a[e2] = t; + if (t < a[e1]) { + a[e2] = a[e1]; + a[e1] = t; + } + } + } + } + + // Pointers + int less = left; // The index of the first element of center part + int great = right; // The index before the first element of right part + + if (a[e1] != a[e2] && a[e2] != a[e3] && a[e3] != a[e4] && a[e4] != a[e5]) { + /* + * Use the second and fourth of the five sorted elements as pivots. + * These values are inexpensive approximations of the first and + * second terciles of the array. Note that pivot1 <= pivot2. + */ + long pivot1 = a[e2]; + long pivot2 = a[e4]; + + /* + * The first and the last elements to be sorted are moved to the + * locations formerly occupied by the pivots. When partitioning + * is complete, the pivots are swapped back into their final + * positions, and excluded from subsequent sorting. + */ + a[e2] = a[left]; + a[e4] = a[right]; + + /* + * Skip elements, which are less or greater than pivot values. + */ + while (a[++less] < pivot1) { + } + while (a[--great] > pivot2) { + } + + /* + * Partitioning: + * + * left part center part right part + * +--------------------------------------------------------------+ + * | < pivot1 | pivot1 <= && <= pivot2 | ? | > pivot2 | + * +--------------------------------------------------------------+ + * ^ ^ ^ + * | | | + * less k great + * + * Invariants: + * + * all in (left, less) < pivot1 + * pivot1 <= all in [less, k) <= pivot2 + * all in (great, right) > pivot2 + * + * Pointer k is the first index of ?-part. + */ + outer: + for (int k = less - 1; ++k <= great; ) { + long ak = a[k]; + if (ak < pivot1) { // Move a[k] to left part + a[k] = a[less]; + /* + * Here and below we use "a[i] = b; i++;" instead + * of "a[i++] = b;" due to performance issue. + */ + a[less] = ak; + ++less; + } + else if (ak > pivot2) { // Move a[k] to right part + while (a[great] > pivot2) { + if (great-- == k) { + break outer; + } + } + if (a[great] < pivot1) { // a[great] <= pivot2 + a[k] = a[less]; + a[less] = a[great]; + ++less; + } + else { // pivot1 <= a[great] <= pivot2 + a[k] = a[great]; + } + /* + * Here and below we use "a[i] = b; i--;" instead + * of "a[i--] = b;" due to performance issue. + */ + a[great] = ak; + --great; + } + } + + // Swap pivots into their final positions + a[left] = a[less - 1]; + a[less - 1] = pivot1; + a[right] = a[great + 1]; + a[great + 1] = pivot2; + + // Sort left and right parts recursively, excluding known pivots + SortingLongTestJMH.sort(a, left, less - 2, leftmost); + SortingLongTestJMH.sort(a, great + 2, right, false); + + /* + * If center part is too large (comprises > 4/7 of the array), + * swap internal pivot values to ends. + */ + if (less < e1 && e5 < great) { + /* + * Skip elements, which are equal to pivot values. + */ + while (a[less] == pivot1) { + ++less; + } + + while (a[great] == pivot2) { + --great; + } + + /* + * Partitioning: + * + * left part center part right part + * +----------------------------------------------------------+ + * | == pivot1 | pivot1 < && < pivot2 | ? | == pivot2 | + * +----------------------------------------------------------+ + * ^ ^ ^ + * | | | + * less k great + * + * Invariants: + * + * all in (*, less) == pivot1 + * pivot1 < all in [less, k) < pivot2 + * all in (great, *) == pivot2 + * + * Pointer k is the first index of ?-part. + */ + outer: + for (int k = less - 1; ++k <= great; ) { + long ak = a[k]; + if (ak == pivot1) { // Move a[k] to left part + a[k] = a[less]; + a[less] = ak; + ++less; + } + else if (ak == pivot2) { // Move a[k] to right part + while (a[great] == pivot2) { + if (great-- == k) { + break outer; + } + } + if (a[great] == pivot1) { // a[great] < pivot2 + a[k] = a[less]; + /* + * Even though a[great] equals to pivot1, the + * assignment a[less] = pivot1 may be incorrect, + * if a[great] and pivot1 are floating-point zeros + * of different signs. Therefore in float and + * double sorting methods we have to use more + * accurate assignment a[less] = a[great]. + */ + a[less] = pivot1; + ++less; + } + else { // pivot1 < a[great] < pivot2 + a[k] = a[great]; + } + a[great] = ak; + --great; + } + } + } + + // Sort center part recursively + SortingLongTestJMH.sort(a, less, great, false); + } + else { // Partitioning with one pivot + /* + * Use the third of the five sorted elements as pivot. + * This value is inexpensive approximation of the median. + */ + long pivot = a[e3]; + + /* + * Partitioning degenerates to the traditional 3-way + * (or "Dutch National Flag") schema: + * + * left part center part right part + * +-------------------------------------------------+ + * | < pivot | == pivot | ? | > pivot | + * +-------------------------------------------------+ + * ^ ^ ^ + * | | | + * less k great + * + * Invariants: + * + * all in (left, less) < pivot + * all in [less, k) == pivot + * all in (great, right) > pivot + * + * Pointer k is the first index of ?-part. + */ + for (int k = less; k <= great; ++k) { + if (a[k] == pivot) { + continue; + } + long ak = a[k]; + if (ak < pivot) { // Move a[k] to left part + a[k] = a[less]; + a[less] = ak; + ++less; + } + else { // a[k] > pivot - Move a[k] to right part + while (a[great] > pivot) { + --great; + } + if (a[great] < pivot) { // a[great] <= pivot + a[k] = a[less]; + a[less] = a[great]; + ++less; + } + else { // a[great] == pivot + /* + * Even though a[great] equals to pivot, the + * assignment a[k] = pivot may be incorrect, + * if a[great] and pivot are floating-point + * zeros of different signs. Therefore in float + * and double sorting methods we have to use + * more accurate assignment a[k] = a[great]. + */ + a[k] = pivot; + } + a[great] = ak; + --great; + } + } + + /* + * Sort left and right parts recursively. + * All elements from center part are equal + * and, therefore, already sorted. + */ + SortingLongTestJMH.sort(a, left, less - 1, leftmost); + SortingLongTestJMH.sort(a, great + 1, right, false); + } + } + + private static void swap(long[] arr, int i, int j) { + long tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } +} diff --git a/jdk/test/java/util/Arrays/SortingNearlySortedPrimitive.java b/jdk/test/java/util/Arrays/SortingNearlySortedPrimitive.java new file mode 100644 index 00000000000..99bd4cf04d2 --- /dev/null +++ b/jdk/test/java/util/Arrays/SortingNearlySortedPrimitive.java @@ -0,0 +1,274 @@ +/* + * Copyright 2015 Goldman Sachs. + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + * @test + * @summary Tests the sorting of a large array of sorted primitive values, + * predominently for cases where the array is nearly sorted. This tests + * code that detects patterns in the array to determine if it is nearly + * sorted and if so employs and optimizes merge sort rather than a + * Dual-Pivot QuickSort. + * + * @run testng SortingNearlySortedPrimitive + */ + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.function.Supplier; + +public class SortingNearlySortedPrimitive { + private static final int ARRAY_SIZE = 1_000_000; + + @DataProvider(name = "arrays") + public Object[][] createData() { + return new Object[][]{ + {"hiZeroLowTest", (Supplier) this::hiZeroLowData}, + {"endLessThanTest", (Supplier) this::endLessThanData}, + {"highFlatLowTest", (Supplier) this::highFlatLowData}, + {"identicalTest", (Supplier) this::identicalData}, + {"sortedReversedSortedTest", (Supplier) this::sortedReversedSortedData}, + {"pairFlipTest", (Supplier) this::pairFlipData}, + {"zeroHiTest", (Supplier) this::zeroHiData}, + }; + } + + @Test(dataProvider = "arrays") + public void runTests(String testName, Supplier dataMethod) throws Exception { + int[] intSourceArray = dataMethod.get(); + + // Clone source array to ensure it is not modified + this.sortAndAssert(intSourceArray.clone()); + this.sortAndAssert(floatCopyFromInt(intSourceArray)); + this.sortAndAssert(doubleCopyFromInt(intSourceArray)); + this.sortAndAssert(longCopyFromInt(intSourceArray)); + this.sortAndAssert(shortCopyFromInt(intSourceArray)); + this.sortAndAssert(charCopyFromInt(intSourceArray)); + } + + private float[] floatCopyFromInt(int[] src) { + float[] result = new float[src.length]; + for (int i = 0; i < result.length; i++) { + result[i] = src[i]; + } + return result; + } + + private double[] doubleCopyFromInt(int[] src) { + double[] result = new double[src.length]; + for (int i = 0; i < result.length; i++) { + result[i] = src[i]; + } + return result; + } + + private long[] longCopyFromInt(int[] src) { + long[] result = new long[src.length]; + for (int i = 0; i < result.length; i++) { + result[i] = src[i]; + } + return result; + } + + private short[] shortCopyFromInt(int[] src) { + short[] result = new short[src.length]; + for (int i = 0; i < result.length; i++) { + result[i] = (short) src[i]; + } + return result; + } + + private char[] charCopyFromInt(int[] src) { + char[] result = new char[src.length]; + for (int i = 0; i < result.length; i++) { + result[i] = (char) src[i]; + } + return result; + } + + private void sortAndAssert(int[] array) { + Arrays.sort(array); + for (int i = 1; i < ARRAY_SIZE; i++) { + if (array[i] < array[i - 1]) { + throw new AssertionError("not sorted"); + } + } + Assert.assertEquals(ARRAY_SIZE, array.length); + } + + private void sortAndAssert(char[] array) { + Arrays.sort(array); + for (int i = 1; i < ARRAY_SIZE; i++) { + if (array[i] < array[i - 1]) { + throw new AssertionError("not sorted"); + } + } + Assert.assertEquals(ARRAY_SIZE, array.length); + } + + private void sortAndAssert(short[] array) { + Arrays.sort(array); + for (int i = 1; i < ARRAY_SIZE; i++) { + if (array[i] < array[i - 1]) { + throw new AssertionError("not sorted"); + } + } + Assert.assertEquals(ARRAY_SIZE, array.length); + } + + private void sortAndAssert(double[] array) { + Arrays.sort(array); + for (int i = 1; i < ARRAY_SIZE; i++) { + if (array[i] < array[i - 1]) { + throw new AssertionError("not sorted"); + } + } + Assert.assertEquals(ARRAY_SIZE, array.length); + } + + private void sortAndAssert(float[] array) { + Arrays.sort(array); + for (int i = 1; i < ARRAY_SIZE; i++) { + if (array[i] < array[i - 1]) { + throw new AssertionError("not sorted"); + } + } + Assert.assertEquals(ARRAY_SIZE, array.length); + } + + private void sortAndAssert(long[] array) { + Arrays.sort(array); + for (int i = 1; i < ARRAY_SIZE; i++) { + if (array[i] < array[i - 1]) { + throw new AssertionError("not sorted"); + } + } + Assert.assertEquals(ARRAY_SIZE, array.length); + } + + private int[] zeroHiData() { + int[] array = new int[ARRAY_SIZE]; + + int threeQuarters = (int) (ARRAY_SIZE * 0.75); + for (int i = 0; i < threeQuarters; i++) { + array[i] = 0; + } + int k = 1; + for (int i = threeQuarters; i < ARRAY_SIZE; i++) { + array[i] = k; + k++; + } + + return array; + } + + private int[] hiZeroLowData() { + int[] array = new int[ARRAY_SIZE]; + + int oneThird = ARRAY_SIZE / 3; + for (int i = 0; i < oneThird; i++) { + array[i] = i; + } + int twoThirds = oneThird * 2; + for (int i = oneThird; i < twoThirds; i++) { + array[i] = 0; + } + for (int i = twoThirds; i < ARRAY_SIZE; i++) { + array[i] = oneThird - i + twoThirds; + } + return array; + } + + private int[] highFlatLowData() { + int[] array = new int[ARRAY_SIZE]; + + int oneThird = ARRAY_SIZE / 3; + for (int i = 0; i < oneThird; i++) { + array[i] = i; + } + int twoThirds = oneThird * 2; + int constant = oneThird - 1; + for (int i = oneThird; i < twoThirds; i++) { + array[i] = constant; + } + for (int i = twoThirds; i < ARRAY_SIZE; i++) { + array[i] = constant - i + twoThirds; + } + + return array; + } + + private int[] identicalData() { + int[] array = new int[ARRAY_SIZE]; + int listNumber = 24; + + for (int i = 0; i < ARRAY_SIZE; i++) { + array[i] = listNumber; + } + + return array; + } + + private int[] endLessThanData() { + int[] array = new int[ARRAY_SIZE]; + + for (int i = 0; i < ARRAY_SIZE - 1; i++) { + array[i] = 3; + } + array[ARRAY_SIZE - 1] = 1; + + return array; + } + + private int[] sortedReversedSortedData() { + int[] array = new int[ARRAY_SIZE]; + + for (int i = 0; i < ARRAY_SIZE / 2; i++) { + array[i] = i; + } + int num = 0; + for (int i = ARRAY_SIZE / 2; i < ARRAY_SIZE; i++) { + array[i] = ARRAY_SIZE - num; + num++; + } + + return array; + } + + private int[] pairFlipData() { + int[] array = new int[ARRAY_SIZE]; + + for (int i = 0; i < ARRAY_SIZE; i++) { + array[i] = i; + } + for (int i = 0; i < ARRAY_SIZE; i += 2) { + int temp = array[i]; + array[i] = array[i + 1]; + array[i + 1] = temp; + } + + return array; + } +}