diff --git a/CClash/CClash.csproj b/CClash/CClash.csproj index 3613fb2..1aa466f 100644 --- a/CClash/CClash.csproj +++ b/CClash/CClash.csproj @@ -39,6 +39,7 @@ + @@ -68,6 +69,7 @@ + diff --git a/CClash/CClash.csproj.user b/CClash/CClash.csproj.user index 3da5017..cad999d 100644 --- a/CClash/CClash.csproj.user +++ b/CClash/CClash.csproj.user @@ -1,6 +1,6 @@  - --cclash-server + --cclash-server --debug --pdb-to-z7 \ No newline at end of file diff --git a/CClash/CClashMessage.cs b/CClash/CClashMessage.cs index dc130b3..68629d3 100644 --- a/CClash/CClashMessage.cs +++ b/CClash/CClashMessage.cs @@ -60,6 +60,7 @@ public class CClashRequest : CClashMessage public IList argv; public string compiler; public int tag; + public int pid; } [Serializable] diff --git a/CClash/CClashServer.cs b/CClash/CClashServer.cs index 73ccf93..706a35b 100644 --- a/CClash/CClashServer.cs +++ b/CClash/CClashServer.cs @@ -14,7 +14,15 @@ public sealed class CClashServer : IDisposable bool quitnow = false; DirectCompilerCacheServer cache; - int connections = 0; + /// + /// The maximum number of pending requests. + /// + public const int MaxServerThreads = 20; + + public const int QuitAfterIdleMinutes = 10; + + List serverPipes = new List(); + List serverThreads = new List(); string mydocs = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); @@ -23,108 +31,175 @@ public CClashServer() Directory.SetCurrentDirectory(mydocs); } - DateTime lastYield = DateTime.Now; - void YieldLocks() + int busyThreads = 0; + + public int BusyThreadCount { - if (DateTime.Now.Subtract(lastYield).TotalSeconds > 5) + get { - cache.YieldLocks(); - lastYield = DateTime.Now; + return busyThreads; } } - public void Listen(string cachedir) + void ThreadIsBusy() { - - var mtx = new Mutex(false, "cclash_serv_" + cachedir.ToLower().GetHashCode()); + lock (serverThreads) + { + busyThreads++; + } + } - try { - if (!mtx.WaitOne(500)) { - return; // some other process is holding it - } - } catch (AbandonedMutexException) { - // past server must have died! + void ThreadIsIdle() + { + lock (serverThreads) + { + busyThreads--; + } + } + + DateTime lastRequest = DateTime.Now; + + void ThreadBeforeProcessRequest() + { + lastRequest = DateTime.Now; + if (BusyThreadCount > Environment.ProcessorCount) + { + System.Threading.Thread.Sleep(60/Environment.ProcessorCount); } + } - try { - Logging.Emit("server listening.."); - - using (var nss = new NamedPipeServerStream(MakePipeName(cachedir), PipeDirection.InOut, 1, PipeTransmissionMode.Message, PipeOptions.WriteThrough | PipeOptions.Asynchronous)) { - cache = new DirectCompilerCacheServer(cachedir); - var msgbuf = new List(); - var rxbuf = new byte[256 * 1024]; - DateTime lastConnection = DateTime.Now; - - do { - // don't hog folders - System.IO.Directory.SetCurrentDirectory(mydocs); - Logging.Emit("server waiting.."); - YieldLocks(); - try { - connections++; - - if (!nss.IsConnected) { - var w = nss.BeginWaitForConnection(null, null); - while (!w.AsyncWaitHandle.WaitOne(5000)) { - try { - YieldLocks(); - } catch { } - if (quitnow) { - return; - } - if (DateTime.Now.Subtract(lastConnection).TotalSeconds > 90) - Stop(); - } - nss.EndWaitForConnection(w); - lastConnection = DateTime.Now; + public void ConnectionThreadFn(object con) + { + using (var nss = con as NamedPipeServerStream) + { + try + { + + while (!quitnow) + { + var w = nss.BeginWaitForConnection(null, null); + Logging.Emit("waiting for client.."); + while (!w.AsyncWaitHandle.WaitOne(1000)) + { + if (quitnow) + { + return; } + } + nss.EndWaitForConnection(w); + Logging.Emit("got client"); + if (nss.IsConnected) + { + Logging.Emit("server connected"); + ThreadBeforeProcessRequest(); + ThreadIsBusy(); + ServiceRequest(nss); + ThreadIsIdle(); + } + } + } + catch (IOException ex) + { + Logging.Error("server thread got {0}, {1}", ex.GetType().Name, ex.Message); + Logging.Error(":{0}", ex.ToString()); + } + } + } - Logging.Emit("server connected.."); + public void ServiceRequest(NamedPipeServerStream nss) + { + var msgbuf = new List(8192); + var rxbuf = new byte[256 * 1024]; + int count = 0; + do + { + count = nss.Read(rxbuf, msgbuf.Count, rxbuf.Length); + if (count > 0) + { + msgbuf.AddRange(rxbuf.Take(count)); + } - msgbuf.Clear(); - int count = 0; - do { - count = nss.Read(rxbuf, msgbuf.Count, rxbuf.Length); - if (count > 0) { - msgbuf.AddRange(rxbuf.Take(count)); - } + } while (!nss.IsMessageComplete); - } while (!nss.IsMessageComplete); + Logging.Emit("server read {0} bytes", msgbuf.Count); - Logging.Emit("server read {0} bytes", msgbuf.Count); + // deserialize message from msgbuf + var req = CClashMessage.Deserialize(msgbuf.ToArray()); + cache.Setup(); // needed? + Logging.Emit("processing request"); + var resp = ProcessRequest(req); + Logging.Emit("request complete: supported={0}, exitcode={1}", resp.supported, resp.exitcode); + var tx = resp.Serialize(); + nss.Write(tx, 0, tx.Length); + nss.Flush(); + Logging.Emit("server written {0} bytes", tx.Length); - // deserialize message from msgbuf - var req = CClashMessage.Deserialize(msgbuf.ToArray()); - cache.Setup(); - var resp = ProcessRequest(req); - var tx = resp.Serialize(); - nss.Write(tx, 0, tx.Length); - nss.Flush(); + nss.WaitForPipeDrain(); + nss.Disconnect(); + Logging.Emit("request done"); + } - // don't hog folders - cache.Finished(); + void NewServerThread(string cachedir) + { + var t = new Thread(new ParameterizedThreadStart(ConnectionThreadFn)); + t.IsBackground = true; + serverThreads.Add(t); + var nss = new NamedPipeServerStream(MakePipeName(cachedir), PipeDirection.InOut, MaxServerThreads, PipeTransmissionMode.Message, PipeOptions.WriteThrough | PipeOptions.Asynchronous); + t.Start(nss); + Logging.Emit("server thread started"); + } + + public void Listen(string cachedir) + { + Environment.CurrentDirectory = mydocs; + var mtx = new Mutex(false, "cclash_serv_" + cachedir.ToLower().GetHashCode()); + try + { + if (!mtx.WaitOne(1000)) + { + quitnow = true; + Logging.Error("another server is already running"); + return; // some other process is holding it! + } + } + catch (AbandonedMutexException) + { + Logging.Warning("previous instance did not exit cleanly!"); + } + cache = new DirectCompilerCacheServer(cachedir); + Logging.Emit("starting server threads.."); - nss.WaitForPipeDrain(); - nss.Disconnect(); - Logging.Emit("server disconnected.."); - } catch (IOException) { - Logging.Warning("error on client pipe"); - nss.Disconnect(); + while (serverThreads.Count < MaxServerThreads) + { + NewServerThread(cachedir); + } - } catch (Exception e) { - Logging.Error("server exception {0}", e); - Stop(); - } - } while (!quitnow); - Logging.Emit("server quitting"); + // maintain the threadpool + while (!quitnow) + { + foreach (var t in serverThreads.ToArray()) + { + if (t.Join(1000)) + { + serverThreads.Remove(t); + NewServerThread(cachedir); + } + if (DateTime.Now.Subtract(lastRequest).TotalMinutes > QuitAfterIdleMinutes) + { + quitnow = true; + } } - } catch (IOException ex) { - Logging.Emit("{0}", ex); - return; - } finally { - mtx.ReleaseMutex(); } + foreach (var t in serverThreads) + { + t.Join(2000); + } + + + Logging.Emit("server quitting"); + mtx.ReleaseMutex(); + } public static string MakePipeName(string cachedir) @@ -151,7 +226,7 @@ public CClashResponse ProcessRequest(CClashRequest req) break; case Command.Run: - cache.SetCompiler(req.compiler, req.workdir, new Dictionary( req.envs )); + cache.SetCompilerEx(req.pid, req.compiler, req.workdir, new Dictionary( req.envs )); rv.exitcode = cache.CompileOrCache(req.argv); System.IO.Directory.SetCurrentDirectory(mydocs); rv.supported = true; diff --git a/CClash/CClashServerClient.cs b/CClash/CClashServerClient.cs index 0b61ae0..8e40c1d 100644 --- a/CClash/CClashServerClient.cs +++ b/CClash/CClashServerClient.cs @@ -33,19 +33,23 @@ void Connect() if (ncs == null) Open(); - for (int i = 0; i < 2; i++) + try { - try { - if (!ncs.IsConnected) - ncs.Connect(100); - ncs.ReadMode = PipeTransmissionMode.Message; - return; - } catch (IOException ex) { - Logging.Emit("error connecting {0}", ex.Message); - try { ncs.Dispose(); Open(); } catch { } - } catch (TimeoutException) { - } + if (!ncs.IsConnected) + ncs.Connect(100); + ncs.ReadMode = PipeTransmissionMode.Message; + return; + } + catch (IOException ex) + { + Logging.Emit("error connecting {0}", ex.Message); + try { ncs.Dispose(); Open(); } + catch { } } + catch (TimeoutException) + { + } + // start the server, but lets not try to use it here, the next instance can try { @@ -59,11 +63,10 @@ void Connect() p.StartInfo.WorkingDirectory = Environment.CurrentDirectory; p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; p.Start(); - + throw new CClashWarningException("starting new server"); } catch (Exception e) { Logging.Emit("error starting cclash server process", e.Message); } - throw new CClashWarningException("failed to connect to server"); } public ICacheInfo Stats @@ -149,7 +152,7 @@ public CClashResponse Transact(CClashRequest req) { Connect(); CClashResponse resp = null; - + req.pid = System.Diagnostics.Process.GetCurrentProcess().Id; var txbuf = req.Serialize(); ncs.Write(txbuf, 0, txbuf.Length); diff --git a/CClash/CacheManifest.cs b/CClash/CacheManifest.cs index 51c7185..1c2861e 100644 --- a/CClash/CacheManifest.cs +++ b/CClash/CacheManifest.cs @@ -35,6 +35,11 @@ public static CacheManifest Deserialize(Stream stream) /// public string CommonHash { get; set; } + /// + /// Hash of the pre-existing PDB file before this object was created. + /// + public string EarlierPdbHash { get; set; } + /// /// non-null if this entry was made by preprocessing the source /// diff --git a/CClash/Compiler.cs b/CClash/Compiler.cs index a9d1c1d..68750aa 100644 --- a/CClash/Compiler.cs +++ b/CClash/Compiler.cs @@ -14,6 +14,7 @@ namespace CClash public sealed class Compiler { static Regex findLineInclude = new Regex("#line\\s+\\d+\\s+\"([^\"]+)\""); + public const string InternalResponseFileSuffix = "cclash"; [DllImport("kernel32.dll", CharSet = CharSet.Auto)] static unsafe extern IntPtr GetEnvironmentStringsA(); @@ -170,6 +171,11 @@ public string CompilerExe /// public string[] CommandLine { get; set; } + /// + /// The arguments that should be sent to the compiler by us or our caller. + /// + public string[] CompileArgs { get; set; } + /// /// The first source file. /// @@ -211,6 +217,8 @@ public string[] SourceFiles } } + public int ParentPid { get; set; } + public string ObjectTarget { get; set; } public string PdbFile { get; set; } @@ -218,12 +226,16 @@ public string[] SourceFiles public bool PrecompiledHeaders { get; set; } public bool GeneratePdb { get; set; } public bool AttemptPdb { get; set; } + public bool PdbExistsAlready { get; set; } public string ResponseFile { get; set; } + + List srcs = new List(); List incs = new List(); List cliincs = new List(); + public List CliIncludePaths { get @@ -244,7 +256,8 @@ bool IsSupported { get { - return (!Linking && + return ( + !Linking && !PrecompiledHeaders && SingleSource && ((!GeneratePdb) || AttemptPdb ) && @@ -378,6 +391,7 @@ public bool ProcessArguments(string[] args) var opt = getOption(args[i]); var full = getFullOption(args[i]); + #region switch process each argument type switch (opt) { case "/o": @@ -423,6 +437,12 @@ public bool ProcessArguments(string[] args) case "/Fd": GeneratePdb = true; PdbFile = Path.Combine(WorkingDirectory, full.Substring(3)); + // openssl gives us a posix path here.. + PdbFile = PdbFile.Replace('/', '\\'); + if (!PdbFile.ToLower().EndsWith(".pdb")) + { + PdbFile = PdbFile + ".pdb"; + } break; case "/Fo": @@ -448,7 +468,7 @@ public bool ProcessArguments(string[] args) break; default: - + #region positional or other flag options if (full.StartsWith("/E")) { return NotSupported("/E"); @@ -462,7 +482,15 @@ public bool ProcessArguments(string[] args) if (opt.StartsWith("@")) { + #region response file ResponseFile = full.Substring(1); + + if (ResponseFile.EndsWith(InternalResponseFileSuffix)) + { + Logging.Emit("cclash misshelper internal response file"); + return false; + } + if (!Path.IsPathRooted(ResponseFile)) ResponseFile = Path.Combine(WorkingDirectory, ResponseFile); var rsptxt = File.ReadAllText(ResponseFile); @@ -486,6 +514,7 @@ public bool ProcessArguments(string[] args) } return NotSupported("response file error"); + #endregion } if (!full.StartsWith("/")) @@ -509,23 +538,28 @@ public bool ProcessArguments(string[] args) if (d == "..") d = Path.GetDirectoryName(WorkingDirectory); - if (!Path.IsPathRooted(d)) { + if (!Path.IsPathRooted(d)) + { d = Path.Combine(WorkingDirectory, d); } if (Directory.Exists(d)) { Logging.Emit("cli include '{0}' => {1}", full, d); - + cliincs.Add(d); continue; } } +#endregion break; } + #endregion + } + if (SingleSource) { if (ObjectTarget == null) @@ -542,12 +576,36 @@ public bool ProcessArguments(string[] args) } if (GeneratePdb) + { + if (Settings.ConvertObjPdbToZ7) + { + // append /Z7 to the arg list + var newargs = new List(args); + newargs.Add("/Z7"); + AttemptPdb = false; + PdbFile = null; + GeneratePdb = false; + PdbExistsAlready = false; + args = newargs.ToArray(); + } + } + + if (GeneratePdb) { if (Settings.AttemptPDBCaching) { if (PdbFile != null) { AttemptPdb = true; + if (PdbFile.EndsWith("\\")) + { + AttemptPdb = false; + return NotSupported("Implicit PDB folder+file requested"); + } + if (FileUtils.Exists(PdbFile)) + { + PdbExistsAlready = true; + } } else { @@ -559,16 +617,14 @@ public bool ProcessArguments(string[] args) return NotSupported("PDB file requested"); } } - } - } catch (Exception e) { Console.Error.WriteLine(e); return NotSupported("option parser exception '{0}'", e); } - + CompileArgs = args.ToArray(); return IsSupported; } diff --git a/CClash/CompilerCacheBase.cs b/CClash/CompilerCacheBase.cs index ccefc3b..78bc690 100644 --- a/CClash/CompilerCacheBase.cs +++ b/CClash/CompilerCacheBase.cs @@ -71,6 +71,12 @@ public CompilerCacheBase(string cacheFolder) public abstract void Setup(); public abstract void Finished(); + public void SetCompilerEx(int parentpid, string compiler, string workdir, Dictionary envs) + { + SetCompiler(compiler, workdir, envs); + comp.ParentPid = parentpid; + } + public void SetCompiler(string compiler, string workdir, Dictionary envs) { if (string.IsNullOrEmpty(compiler)) throw new ArgumentNullException("compiler"); @@ -268,6 +274,7 @@ public virtual int CompileOrCache(IEnumerable args) { if (IsSupported(args)) { + args = comp.CompileArgs; var hc = DeriveHashKey(args); if (hc.Result == DataHashResult.Ok) { diff --git a/CClash/DirectCompilerCache.cs b/CClash/DirectCompilerCache.cs index f3a50c7..a97d5da 100644 --- a/CClash/DirectCompilerCache.cs +++ b/CClash/DirectCompilerCache.cs @@ -78,37 +78,28 @@ protected override bool CheckCache( IEnumerable args, DataHash commonkey } } - foreach (var f in new string[] { F_Manifest, F_Object, F_Stderr, F_Stdout }) + if (comp.AttemptPdb) { - if (!FileUtils.Exists(outputCache.MakePath(commonkey.Hash, f))) + if (comp.PdbExistsAlready) { - outputCache.Remove(commonkey.Hash); - Logging.Miss(DataHashResult.CacheCorrupt, commonkey.Hash, comp.SingleSourceFile, ""); - return false; + var pdbhash = hasher.DigestBinaryFile(comp.PdbFile); + if (pdbhash.Hash != manifest.EarlierPdbHash) + { + outputCache.Remove(commonkey.Hash); + Logging.Miss(DataHashResult.FileChanged, commonkey.Hash, comp.PdbFile, ""); + return false; + } } } - if (comp.AttemptPdb && comp.PdbFile != null) + foreach (var f in new string[] { F_Manifest, F_Object, F_Stderr, F_Stdout }) { - var cachedpdb = outputCache.MakePath(commonkey.Hash, F_Pdb); - if (!FileUtils.Exists(cachedpdb)) + if (!FileUtils.Exists(outputCache.MakePath(commonkey.Hash, f))) { - Logging.Miss(DataHashResult.CacheCorrupt, commonkey.Hash, comp.PdbFile, ""); + outputCache.Remove(commonkey.Hash); + Logging.Miss(DataHashResult.CacheCorrupt, commonkey.Hash, comp.SingleSourceFile, ""); return false; } - - // check if the target pdb exists already and it's contents differs from our cached copy. - // If so, fail the cache hit as pdbs get merged not overwritten :( - if (FileUtils.Exists(comp.PdbFile)) - { - - var pdbhash = hasher.DigestBinaryFile(comp.PdbFile); - if (pdbhash.Hash != manifest.PdbHash) - { - Logging.Miss(DataHashResult.FileChanged, comp.WorkingDirectory, comp.PdbFile, ""); - return false; - } - } } return true; // cache hit, all includes match and no new files added @@ -193,6 +184,8 @@ protected override int OnCacheMissLocked(DataHash hc, IEnumerable args, return rv; } + static object RelatedMissLock = new object(); + protected virtual void DoCacheMiss( Compiler c, DataHash hc, IEnumerable args, CacheManifest m, List ifiles) { @@ -214,6 +207,17 @@ protected virtual void DoCacheMiss( Compiler c, DataHash hc, IEnumerable m.IncludeFiles = new Dictionary(); m.TimeStamp = DateTime.Now.ToString("s"); m.CommonHash = hc.Hash; + if (c.AttemptPdb) + { + if (c.PdbExistsAlready) + { + var ph = hasher.DigestBinaryFile(c.PdbFile); + if (ph.Result == DataHashResult.Ok) + { + m.EarlierPdbHash = ph.Hash; + } + } + } #endregion diff --git a/CClash/DirectCompilerCacheServer.cs b/CClash/DirectCompilerCacheServer.cs index 5792cae..7df6c5a 100644 --- a/CClash/DirectCompilerCacheServer.cs +++ b/CClash/DirectCompilerCacheServer.cs @@ -154,9 +154,13 @@ public override Dictionary GetHashes(IEnumerable fname { foreach (var filename in tmp.Keys) { - hashcache[filename.ToLower()] = tmp[filename]; - rv[filename.ToLower()] = tmp[filename]; - WatchFile(filename.ToLower()); + var flow = filename.ToLower(); + hashcache[flow] = tmp[filename]; + rv[flow] = tmp[filename]; + if (!flow.StartsWith( Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(comp.CompilerExe))))) + { + WatchFile(flow); + } } } } diff --git a/CClash/Program.cs b/CClash/Program.cs index db78120..3860a87 100644 --- a/CClash/Program.cs +++ b/CClash/Program.cs @@ -41,6 +41,14 @@ public static int Main(string[] args) if (args.Contains("--cclash-server")) { var server = new CClashServer(); + if (args.Contains("--attempt-pdb")) + { + Environment.SetEnvironmentVariable("CCLASH_ATTEMPT_PDB_CACHE", "yes"); + } + if (args.Contains("--pdb-to-z7")) + { + Environment.SetEnvironmentVariable("CCLASH_Z7_OBJ", "yes"); + } if (args.Contains("--debug")) { if (Settings.DebugFile == null) { @@ -63,17 +71,28 @@ public static int Main(string[] args) if (Settings.ServiceMode) { for (int i = 0; i < 3; i++ ) { - try { + try + { var cc = new CClashServerClient(Settings.CacheDirectory); - if (args.Contains("--stop")) { + if (args.Contains("--stop")) + { cc.Transact(new CClashRequest() { cmd = Command.Quit }); - } else { + } + else + { Console.Out.WriteLine(cc.GetStats(compiler)); return 0; } - } catch (CClashWarningException) { + } + catch (CClashWarningException) + { System.Threading.Thread.Sleep(2000); } + catch (InvalidOperationException) + { + Logging.Error("server not running"); + return -1; + } } } else diff --git a/CClash/Settings.cs b/CClash/Settings.cs index 59f7484..7782da6 100644 --- a/CClash/Settings.cs +++ b/CClash/Settings.cs @@ -57,6 +57,18 @@ public static bool AttemptPDBCaching } } + /// + /// When an object compilation with pdb generation (Zi) is requested. Instead + /// generate embedded debug info (Z7). + /// + public static bool ConvertObjPdbToZ7 + { + get + { + return Environment.GetEnvironmentVariable("CCLASH_Z7_OBJ") == "yes"; + } + } + static bool EnabledByConditions() { return ConditionVarsAreTrue("CCLASH_ENABLE_WHEN"); diff --git a/Installer/Installer/Express/DVD-5/DiskImages/DISK1/Setup.ini b/Installer/Installer/Express/DVD-5/DiskImages/DISK1/Setup.ini index a3d5627..add1b0b 100644 Binary files a/Installer/Installer/Express/DVD-5/DiskImages/DISK1/Setup.ini and b/Installer/Installer/Express/DVD-5/DiskImages/DISK1/Setup.ini differ diff --git a/README.md b/README.md index c37576b..e408a7e 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ CClash is a (.net based) compiler cache for the Microsoft 'cl' compiler. -It is aimed at fairly simple use ( eg, `cl /c file.c /Fofile.o` ) and will cache object and pdb files rather like ccache does. +It is aimed at fairly simple use ( eg, `cl /c file.c /Fofile.o` ) and will cache object files rather like ccache does. Due to the nature of PDB files on windows cclash can't easily cache these so you might be out of luck. However you might want to experiment with the CCLASH_Z7_OBJ=yes environment setting to see if it gives you what you want. CClash has been inspired by clcache (https://github.com/frerich/clcache) and of course ccache (http://ccache.samba.org). @@ -15,24 +15,21 @@ On subsequent builds, the key will match, giving us a the manifest file, we chec Those who know ccache will recognise this as quite similar to the ccache 'direct mode'. ( I read the ccache man page before writing this - http://ccache.samba.org/manual.html#_the_direct_mode ). CClash has a preprocessor mode but it does not work very well and is best avoided at the moment. -CClash has a couple of extra twists to get a little more speed, if you set the `CCLASH_SERVER` environment variable, a short lived ( 5 mins max ) server will be spawned. This server will take over the running of the compiler and the hashing of input data, so common files will only be hashed once! The stats below are from using the server mode. -Also, you can set `CCLASH_HARDLINK` and cclash will attempt to make NTFS hardlinks rather than copy files which should also be faster. Don't try this if your files are on a different drive or ntfs volume to `CCLASH_DIR`. If your system has a significant disk I/O overhead this might help you. - ## How well does it work? -On my (not very good) windows 8 machine, I have a simple test by building openssl under nmake. These tests were all done _after_ running the Configure step. +On my (not very good) windows 8.1 machine, I have a simple test by building openssl under nmake. These tests were all done _after_ running the Configure and do_ms steps. OpenSSL windows build | Duration | Time Difference | ----------------------------------------------------------|----------|-----------------| -average build time without cclash | 288s | | -cclash enabled first run | 464s | +61% | -cclash enabled second run | **128s** | **-56% ** | -cclash enabled first run (CCLASH_SERVER) | 391s | +35% | -cclash enabled second run (CCLASH_SERVER) | **108s** | **-63%** | +average build time without cclash | 405s | | +cclash enabled first run (CCLASH_SERVER) | 428s | +6% | +cclash enabled second run (CCLASH_SERVER) | **123s** | **-71%** | + +The first build with a clean cache costs about an extra 5-15% in build time for each file. Subsequent builds (with no source changes) give about a 50-80% reduction in build time for some operations. -The first build with a clean cache costs about an extra 30-60% in build time for each file. Subsequent builds (with no source changes) give about a 50-65% reduction in build time. +This build was done with CCLASH_Z7_OBJ=yes to prevent openssl trying to make pdb files for each .obj it creates (you still get the desired pdb files after the linker calls) -So.. For cclash to make a difference to you, you would want to be in a situation where you will compile most of your files more than 2 or 3 times. In my case, I wanted cclash for a continuous integration build so it _should_ pay off quite quickly day or so +So.. For cclash to make a difference to you, you would want to be in a situation where you will compile most of your files more than 2 or 3 times. In my case, I wanted cclash for a continuous integration build so it _should_ pay off quite quickly day or so. Compared to good old ccache this isn't too bad (ccache with gcc costs about 25% on a clean cache). Your milage may of course vary greatly! On a multi-core machine with GNU Make things go quite well too. @@ -40,7 +37,7 @@ Compared to good old ccache this isn't too bad (ccache with gcc costs about 25% There are two ways. The first is easier if you can adjust your environment. The latter might be your only choice if you wish to use the Visual Studio IDE. - * Add the folder that contains cclash's cl.exe to %PATH% before the visual studio compiler + * Add the folder that contains cclash's cl.exe to %PATH% before the visual studio compiler. or diff --git a/cclash.v11.suo b/cclash.v11.suo index 35b7139..9d0f7ec 100644 Binary files a/cclash.v11.suo and b/cclash.v11.suo differ