Path: blob/aarch64-shenandoah-jdk8u272-b10/langtools/src/share/classes/com/sun/tools/sjavac/CompileJavaPackages.java
38899 views
/*1* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.tools.sjavac;2627import java.net.URI;28import java.util.Arrays;29import java.util.Random;30import java.util.Set;31import java.util.Map;3233import com.sun.tools.sjavac.server.JavacServer;34import com.sun.tools.sjavac.server.SysInfo;35import java.io.PrintStream;3637/**38* This transform compiles a set of packages containing Java sources.39* The compile request is divided into separate sets of source files.40* For each set a separate request thread is dispatched to a javac server41* and the meta data is accumulated. The number of sets correspond more or42* less to the number of cores. Less so now, than it will in the future.43*44* <p><b>This is NOT part of any supported API.45* If you write code that depends on this, you do so at your own46* risk. This code and its internal interfaces are subject to change47* or deletion without notice.</b></p>48*/49public class CompileJavaPackages implements Transformer {5051// The current limited sharing of data between concurrent JavaCompilers52// in the server will not give speedups above 3 cores. Thus this limit.53// We hope to improve this in the future.54final static int limitOnConcurrency = 3;5556String serverSettings;57public void setExtra(String e) {58serverSettings = e;59}6061String[] args;62public void setExtra(String[] a) {63args = a;64}6566public boolean transform(Map<String,Set<URI>> pkgSrcs,67Set<URI> visibleSources,68Map<URI,Set<String>> visibleClasses,69Map<String,Set<String>> oldPackageDependents,70URI destRoot,71final Map<String,Set<URI>> packageArtifacts,72final Map<String,Set<String>> packageDependencies,73final Map<String,String> packagePubapis,74int debugLevel,75boolean incremental,76int numCores,77PrintStream out,78PrintStream err)79{80boolean rc = true;81boolean concurrentCompiles = true;8283// Fetch the id.84String id = Util.extractStringOption("id", serverSettings);85if (id == null || id.equals("")) {86// No explicit id set. Create a random id so that the requests can be87// grouped properly in the server.88id = "id"+(((new Random()).nextLong())&Long.MAX_VALUE);89}90// Only keep portfile and sjavac settings..91String psServerSettings = Util.cleanSubOptions("--server:", Util.set("portfile","sjavac","background","keepalive"), serverSettings);9293// Get maximum heap size from the server!94SysInfo sysinfo = JavacServer.connectGetSysInfo(psServerSettings, out, err);95if (sysinfo.numCores == -1) {96Log.error("Could not query server for sysinfo!");97return false;98}99int numMBytes = (int)(sysinfo.maxMemory / ((long)(1024*1024)));100Log.debug("Server reports "+numMBytes+"MiB of memory and "+sysinfo.numCores+" cores");101102if (numCores <= 0) {103// Set the requested number of cores to the number of cores on the server.104numCores = sysinfo.numCores;105Log.debug("Number of jobs not explicitly set, defaulting to "+sysinfo.numCores);106} else if (sysinfo.numCores < numCores) {107// Set the requested number of cores to the number of cores on the server.108Log.debug("Limiting jobs from explicitly set "+numCores+" to cores available on server: "+sysinfo.numCores);109numCores = sysinfo.numCores;110} else {111Log.debug("Number of jobs explicitly set to "+numCores);112}113// More than three concurrent cores does not currently give a speedup, at least for compiling the jdk114// in the OpenJDK. This will change in the future.115int numCompiles = numCores;116if (numCores > limitOnConcurrency) numCompiles = limitOnConcurrency;117// Split the work up in chunks to compiled.118119int numSources = 0;120for (String s : pkgSrcs.keySet()) {121Set<URI> ss = pkgSrcs.get(s);122numSources += ss.size();123}124125int sourcesPerCompile = numSources / numCompiles;126127// For 64 bit Java, it seems we can compile the OpenJDK 8800 files with a 1500M of heap128// in a single chunk, with reasonable performance.129// For 32 bit java, it seems we need 1G of heap.130// Number experimentally determined when compiling the OpenJDK.131// Includes space for reasonably efficient garbage collection etc,132// Calculating backwards gives us a requirement of133// 1500M/8800 = 175 KiB for 64 bit platforms134// and 1G/8800 = 119 KiB for 32 bit platform135// for each compile.....136int kbPerFile = 175;137String osarch = System.getProperty("os.arch");138String dataModel = System.getProperty("sun.arch.data.model");139if ("32".equals(dataModel)) {140// For 32 bit platforms, assume it is slightly smaller141// because of smaller object headers and pointers.142kbPerFile = 119;143}144int numRequiredMBytes = (kbPerFile*numSources)/1024;145Log.debug("For os.arch "+osarch+" the empirically determined heap required per file is "+kbPerFile+"KiB");146Log.debug("Server has "+numMBytes+"MiB of heap.");147Log.debug("Heuristics say that we need "+numRequiredMBytes+"MiB of heap for all source files.");148// Perform heuristics to see how many cores we can use,149// or if we have to the work serially in smaller chunks.150if (numMBytes < numRequiredMBytes) {151// Ouch, cannot fit even a single compile into the heap.152// Split it up into several serial chunks.153concurrentCompiles = false;154// Limit the number of sources for each compile to 500.155if (numSources < 500) {156numCompiles = 1;157sourcesPerCompile = numSources;158Log.debug("Compiling as a single source code chunk to stay within heap size limitations!");159} else if (sourcesPerCompile > 500) {160// This number is very low, and tuned to dealing with the OpenJDK161// where the source is >very< circular! In normal application,162// with less circularity the number could perhaps be increased.163numCompiles = numSources / 500;164sourcesPerCompile = numSources/numCompiles;165Log.debug("Compiling source as "+numCompiles+" code chunks serially to stay within heap size limitations!");166}167} else {168if (numCompiles > 1) {169// Ok, we can fit at least one full compilation on the heap.170float usagePerCompile = (float)numRequiredMBytes / ((float)numCompiles * (float)0.7);171int usage = (int)(usagePerCompile * (float)numCompiles);172Log.debug("Heuristics say that for "+numCompiles+" concurrent compiles we need "+usage+"MiB");173if (usage > numMBytes) {174// Ouch it does not fit. Reduce to a single chunk.175numCompiles = 1;176sourcesPerCompile = numSources;177// What if the relationship betweem number of compile_chunks and num_required_mbytes178// is not linear? Then perhaps 2 chunks would fit where 3 does not. Well, this is179// something to experiment upon in the future.180Log.debug("Limiting compile to a single thread to stay within heap size limitations!");181}182}183}184185Log.debug("Compiling sources in "+numCompiles+" chunk(s)");186187// Create the chunks to be compiled.188final CompileChunk[] compileChunks = createCompileChunks(pkgSrcs, oldPackageDependents,189numCompiles, sourcesPerCompile);190191if (Log.isDebugging()) {192int cn = 1;193for (CompileChunk cc : compileChunks) {194Log.debug("Chunk "+cn+" for "+id+" ---------------");195cn++;196for (URI u : cc.srcs) {197Log.debug(""+u);198}199}200}201202// The return values for each chunked compile.203final int[] rn = new int[numCompiles];204// The requets, might or might not run as a background thread.205final Thread[] requests = new Thread[numCompiles];206207final Set<URI> fvisible_sources = visibleSources;208final Map<URI,Set<String>> fvisible_classes = visibleClasses;209210long start = System.currentTimeMillis();211212for (int i=0; i<numCompiles; ++i) {213final int ii = i;214final CompileChunk cc = compileChunks[i];215216// Pass the num_cores and the id (appended with the chunk number) to the server.217final String cleanedServerSettings = psServerSettings+",poolsize="+numCores+",id="+id+"-"+ii;218final PrintStream fout = out;219final PrintStream ferr = err;220221requests[ii] = new Thread() {222@Override223public void run() {224rn[ii] = JavacServer.useServer(cleanedServerSettings,225Main.removeWrapperArgs(args),226cc.srcs,227fvisible_sources,228fvisible_classes,229packageArtifacts,230packageDependencies,231packagePubapis,232null,233fout, ferr);234}235};236237if (cc.srcs.size() > 0) {238String numdeps = "";239if (cc.numDependents > 0) numdeps = "(with "+cc.numDependents+" dependents) ";240if (!incremental || cc.numPackages > 16) {241String info = "("+cc.pkgFromTos+")";242if (info.equals("( to )")) {243info = "";244}245Log.info("Compiling "+cc.srcs.size()+" files "+numdeps+"in "+cc.numPackages+" packages "+info);246} else {247Log.info("Compiling "+cc.pkgNames+numdeps);248}249if (concurrentCompiles) {250requests[ii].start();251}252else {253requests[ii].run();254// If there was an error, then stop early when running single threaded.255if (rn[i] != 0) {256return false;257}258}259}260}261if (concurrentCompiles) {262// If there are background threads for the concurrent compiles, then join them.263for (int i=0; i<numCompiles; ++i) {264try { requests[i].join(); } catch (InterruptedException e) { }265}266}267268// Check the return values.269for (int i=0; i<numCompiles; ++i) {270if (compileChunks[i].srcs.size() > 0) {271if (rn[i] != 0) {272rc = false;273}274}275}276long duration = System.currentTimeMillis() - start;277long minutes = duration/60000;278long seconds = (duration-minutes*60000)/1000;279Log.debug("Compilation of "+numSources+" source files took "+minutes+"m "+seconds+"s");280281return rc;282}283284285/**286* Split up the sources into compile chunks. If old package dependents information287* is available, sort the order of the chunks into the most dependent first!288* (Typically that chunk contains the java.lang package.) In the future289* we could perhaps improve the heuristics to put the sources into even more sensible chunks.290* Now the package are simple sorted in alphabetical order and chunked, then the chunks291* are sorted on how dependent they are.292*293* @param pkgSrcs The sources to compile.294* @param oldPackageDependents Old package dependents, if non-empty, used to sort the chunks.295* @param numCompiles The number of chunks.296* @param sourcesPerCompile The number of sources per chunk.297* @return298*/299CompileChunk[] createCompileChunks(Map<String,Set<URI>> pkgSrcs,300Map<String,Set<String>> oldPackageDependents,301int numCompiles,302int sourcesPerCompile) {303304CompileChunk[] compileChunks = new CompileChunk[numCompiles];305for (int i=0; i<compileChunks.length; ++i) {306compileChunks[i] = new CompileChunk();307}308309// Now go through the packages and spread out the source on the different chunks.310int ci = 0;311// Sort the packages312String[] packageNames = pkgSrcs.keySet().toArray(new String[0]);313Arrays.sort(packageNames);314String from = null;315for (String pkgName : packageNames) {316CompileChunk cc = compileChunks[ci];317Set<URI> s = pkgSrcs.get(pkgName);318if (cc.srcs.size()+s.size() > sourcesPerCompile && ci < numCompiles-1) {319from = null;320ci++;321cc = compileChunks[ci];322}323cc.numPackages++;324cc.srcs.addAll(s);325326// Calculate nice package names to use as information when compiling.327String justPkgName = Util.justPackageName(pkgName);328// Fetch how many packages depend on this package from the old build state.329Set<String> ss = oldPackageDependents.get(pkgName);330if (ss != null) {331// Accumulate this information onto this chunk.332cc.numDependents += ss.size();333}334if (from == null || from.trim().equals("")) from = justPkgName;335cc.pkgNames.append(justPkgName+"("+s.size()+") ");336cc.pkgFromTos = from+" to "+justPkgName;337}338// If we are compiling serially, sort the chunks, so that the chunk (with the most dependents) (usually the chunk339// containing java.lang.Object, is to be compiled first!340// For concurrent compilation, this does not matter.341Arrays.sort(compileChunks);342return compileChunks;343}344}345346347