Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Kotas <jkotas@microsoft.com>2015-12-27 11:23:48 +0300
committerJan Kotas <jkotas@microsoft.com>2015-12-27 11:24:57 +0300
commite264fcb9c69a6671c4d455b1fa12675b97d9a3e1 (patch)
tree0516086d3e27613f8803e24ab3c3133fa465dc96 /src/ILCompiler.Compiler
parentbf8670422b294df760e54639c68a40b738ab4eda (diff)
Improve performance of MethodIL provider
- Explicitly use memory mapped files for Ecma metadata reader. System.Reflection.Metadata has heuristic that tries to save virtual address space. This heuristic does not work well for us since it can make IL access very slow (call to OS for each method IL query). Explicitly using memory mapped files gives us reliably the desired performance characteristics. - Implement caching in MethodILProvider. - Refactor command line parsing - move TypeSystemContext instantiation from the driver .exe into the compiler These performance improvements bring hello world native compilation from multiple seconds back to sub second range (with release build of RyuJIT and NGened compiler).
Diffstat (limited to 'src/ILCompiler.Compiler')
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/Compilation.cs74
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.cs199
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs8
-rw-r--r--src/ILCompiler.Compiler/src/CppCodeGen/CppWriter.cs6
-rw-r--r--src/ILCompiler.Compiler/src/project.json1
5 files changed, 209 insertions, 79 deletions
diff --git a/src/ILCompiler.Compiler/src/Compiler/Compilation.cs b/src/ILCompiler.Compiler/src/Compiler/Compilation.cs
index a858aa207..e36f8d7bf 100644
--- a/src/ILCompiler.Compiler/src/Compiler/Compilation.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/Compilation.cs
@@ -3,16 +3,13 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
+using System.Reflection.Metadata.Ecma335;
using System.IO;
-using System.Linq;
-using System.Text;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
using Internal.IL;
-using Internal.IL.Stubs;
using Internal.JitInterface;
using ILCompiler.DependencyAnalysis;
@@ -20,8 +17,18 @@ using ILCompiler.DependencyAnalysisFramework;
namespace ILCompiler
{
- public struct CompilationOptions
+ public class CompilationOptions
{
+ public IReadOnlyDictionary<string, string> InputFilePaths;
+ public IReadOnlyDictionary<string, string> ReferenceFilePaths;
+
+ public string OutputFilePath;
+
+ public string SystemModuleName;
+
+ public TargetOS TargetOS;
+ public TargetArchitecture TargetArchitecture;
+
public bool IsCppCodeGen;
public bool NoLineNumbers;
public string DgmlLog;
@@ -41,11 +48,16 @@ namespace ILCompiler
private ILCompiler.CppCodeGen.CppWriter _cppWriter = null;
- public Compilation(CompilerTypeSystemContext typeSystemContext, CompilationOptions options)
+ public Compilation(CompilationOptions options)
{
- _typeSystemContext = typeSystemContext;
_options = options;
+ _typeSystemContext = new CompilerTypeSystemContext(new TargetDetails(options.TargetArchitecture, options.TargetOS));
+ _typeSystemContext.InputFilePaths = options.InputFilePaths;
+ _typeSystemContext.ReferenceFilePaths = options.ReferenceFilePaths;
+
+ _typeSystemContext.SetSystemModule(_typeSystemContext.GetModuleForSimpleName(options.SystemModuleName));
+
_nameMangler = new NameMangler(this);
}
@@ -79,18 +91,6 @@ namespace ILCompiler
set;
}
- public string OutputPath
- {
- get;
- set;
- }
-
- public TextWriter Out
- {
- get;
- set;
- }
-
private MethodDesc _mainMethod;
internal MethodDesc MainMethod
@@ -117,19 +117,21 @@ namespace ILCompiler
}
}
- private ILProvider _ilProvider = new ILProvider();
+ private ILProvider _methodILCache = new ILProvider();
public MethodIL GetMethodIL(MethodDesc method)
{
- return _ilProvider.GetMethodIL(method);
+ // Flush the cache when it grows too big
+ if (_methodILCache.Count > 1000)
+ _methodILCache= new ILProvider();
+
+ return _methodILCache.GetMethodIL(method);
}
private CorInfoImpl _corInfo;
- public void CompileSingleFile(MethodDesc mainMethod)
+ public void CompileSingleFile()
{
- _mainMethod = mainMethod;
-
NodeFactory.NameMangler = NameMangler;
_nodeFactory = new NodeFactory(_typeSystemContext, _options.IsCppCodeGen);
@@ -177,7 +179,7 @@ namespace ILCompiler
var nodes = _dependencyGraph.MarkedNodeList;
- ObjectWriter.EmitObject(OutputPath, nodes, _nodeFactory);
+ ObjectWriter.EmitObject(_options.OutputFilePath, nodes, _nodeFactory);
}
if (_options.DgmlLog != null)
@@ -192,20 +194,30 @@ namespace ILCompiler
private void AddCompilationRoots()
{
- if (_mainMethod != null)
- {
- AddCompilationRoot(_mainMethod, "Main method", "__managed__Main");
- }
-
foreach (var inputFile in _typeSystemContext.InputFilePaths)
{
var module = _typeSystemContext.GetModuleFromPath(inputFile.Value);
+
+ if (module.PEReader.PEHeaders.IsExe)
+ AddCompilationRootsForMainMethod(module);
+
AddCompilationRootsForRuntimeExports(module);
}
AddCompilationRootsForRuntimeExports((EcmaModule)_typeSystemContext.SystemModule);
}
+ private void AddCompilationRootsForMainMethod(EcmaModule module)
+ {
+ if (_mainMethod != null)
+ throw new Exception("Multiple entrypoint modules");
+
+ int entryPointToken = module.PEReader.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress;
+ _mainMethod = module.GetMethod(MetadataTokens.EntityHandle(entryPointToken));
+
+ AddCompilationRoot(_mainMethod, "Main method", "__managed__Main");
+ }
+
private void AddCompilationRootsForRuntimeExports(EcmaModule module)
{
foreach (var type in module.GetAllTypes())
@@ -250,7 +262,7 @@ namespace ILCompiler
string methodName = method.ToString();
Log.WriteLine("Compiling " + methodName);
- var methodIL = _ilProvider.GetMethodIL(method);
+ var methodIL = GetMethodIL(method);
if (methodIL == null)
return;
diff --git a/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.cs b/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.cs
index 9ad58afea..1154c7714 100644
--- a/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.cs
@@ -3,12 +3,13 @@
using System;
using System.IO;
-using System.Linq;
+using System.IO.MemoryMappedFiles;
using System.Diagnostics;
using System.Collections.Generic;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
+using System.Runtime.InteropServices;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
@@ -59,27 +60,83 @@ namespace ILCompiler
private MetadataStringDecoder _metadataStringDecoder;
- private Dictionary<string, EcmaModule> _modules = new Dictionary<string, EcmaModule>(StringComparer.OrdinalIgnoreCase);
-
private class ModuleData
{
- public string Path;
+ public string SimpleName;
+ public string FilePath;
+
+ public EcmaModule Module;
+ public MemoryMappedViewAccessor MappedViewAccessor;
+
public Microsoft.DiaSymReader.ISymUnmanagedReader PdbReader;
}
- private Dictionary<EcmaModule, ModuleData> _moduleData = new Dictionary<EcmaModule, ModuleData>();
+
+ private class ModuleHashtable : LockFreeReaderHashtable<EcmaModule, ModuleData>
+ {
+ protected override int GetKeyHashCode(EcmaModule key)
+ {
+ return key.GetHashCode();
+ }
+ protected override int GetValueHashCode(ModuleData value)
+ {
+ return value.Module.GetHashCode();
+ }
+ protected override bool CompareKeyToValue(EcmaModule key, ModuleData value)
+ {
+ return Object.ReferenceEquals(key, value.Module);
+ }
+ protected override bool CompareValueToValue(ModuleData value1, ModuleData value2)
+ {
+ return Object.ReferenceEquals(value1.Module, value2.Module);
+ }
+ protected override ModuleData CreateValueFromKey(EcmaModule key)
+ {
+ Debug.Assert(false, "CreateValueFromKey not supported");
+ return null;
+ }
+ }
+ private ModuleHashtable _moduleHashtable = new ModuleHashtable();
+
+ private class SimpleNameHashtable : LockFreeReaderHashtable<string, ModuleData>
+ {
+ StringComparer _comparer = StringComparer.OrdinalIgnoreCase;
+
+ protected override int GetKeyHashCode(string key)
+ {
+ return _comparer.GetHashCode(key);
+ }
+ protected override int GetValueHashCode(ModuleData value)
+ {
+ return _comparer.GetHashCode(value.SimpleName);
+ }
+ protected override bool CompareKeyToValue(string key, ModuleData value)
+ {
+ return _comparer.Equals(key, value.SimpleName);
+ }
+ protected override bool CompareValueToValue(ModuleData value1, ModuleData value2)
+ {
+ return _comparer.Equals(value1.SimpleName, value2.SimpleName);
+ }
+ protected override ModuleData CreateValueFromKey(string key)
+ {
+ Debug.Assert(false, "CreateValueFromKey not supported");
+ return null;
+ }
+ }
+ private SimpleNameHashtable _simpleNameHashtable = new SimpleNameHashtable();
public CompilerTypeSystemContext(TargetDetails details)
: base(details)
{
}
- public IDictionary<string, string> InputFilePaths
+ public IReadOnlyDictionary<string, string> InputFilePaths
{
get;
set;
}
- public IDictionary<string, string> ReferenceFilePaths
+ public IReadOnlyDictionary<string, string> ReferenceFilePaths
{
get;
set;
@@ -113,9 +170,9 @@ namespace ILCompiler
public EcmaModule GetModuleForSimpleName(string simpleName)
{
- EcmaModule existingModule;
- if (_modules.TryGetValue(simpleName, out existingModule))
- return existingModule;
+ ModuleData existing;
+ if (_simpleNameHashtable.TryGetValue(simpleName, out existing))
+ return existing.Module;
string filePath;
if (!InputFilePaths.TryGetValue(simpleName, out filePath))
@@ -124,51 +181,101 @@ namespace ILCompiler
throw new FileNotFoundException("Assembly not found: " + simpleName);
}
- EcmaModule module = new EcmaModule(this, new PEReader(File.OpenRead(filePath)));
-
- MetadataReader metadataReader = module.MetadataReader;
- string actualSimpleName = metadataReader.GetString(metadataReader.GetAssemblyDefinition().Name);
- if (!actualSimpleName.Equals(simpleName, StringComparison.OrdinalIgnoreCase))
- throw new FileNotFoundException("Assembly name does not match filename " + filePath);
-
- AddModule(simpleName, filePath, module);
-
- return module;
+ return AddModule(filePath, simpleName);
}
public EcmaModule GetModuleFromPath(string filePath)
{
// This method is not expected to be called frequently. Linear search is acceptable.
- foreach (var entry in _moduleData)
+ foreach (var entry in ModuleHashtable.Enumerator.Get(_moduleHashtable))
{
- if (entry.Value.Path == filePath)
- return entry.Key;
+ if (entry.FilePath == filePath)
+ return entry.Module;
}
- EcmaModule module = new EcmaModule(this, new PEReader(File.OpenRead(filePath)));
-
- MetadataReader metadataReader = module.MetadataReader;
- string simpleName = metadataReader.GetString(metadataReader.GetAssemblyDefinition().Name);
- if (_modules.ContainsKey(simpleName))
- throw new FileNotFoundException("Module with same simple name already exists " + filePath);
-
- AddModule(simpleName, filePath, module);
-
- return module;
+ return AddModule(filePath, null);
}
- private void AddModule(string simpleName, string filePath, EcmaModule module)
+ private unsafe static PEReader OpenPEFile(string filePath, out MemoryMappedViewAccessor mappedViewAccessor)
{
- _modules.Add(simpleName, module);
+ // System.Reflection.Metadata has heuristic that tries to save virtual address space. This heuristic does not work
+ // well for us since it can make IL access very slow (call to OS for each method IL query). We will map the file
+ // ourselves to get the desired performance characteristics reliably.
+
+ MemoryMappedFile mappedFile = null;
+ MemoryMappedViewAccessor accessor = null;
- ModuleData moduleData = new ModuleData()
+ try
{
- Path = filePath
- };
+ mappedFile = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open, null, 0, MemoryMappedFileAccess.Read);
+ accessor = mappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read);
+
+ var safeBuffer = accessor.SafeMemoryMappedViewHandle;
+ var peReader = new PEReader((byte*)safeBuffer.DangerousGetHandle(), (int)safeBuffer.ByteLength);
- InitializeSymbolReader(moduleData);
+ // MemoryMappedFile does not need to be kept around. MemoryMappedViewAccessor is enough.
- _moduleData.Add(module, moduleData);
+ mappedViewAccessor = accessor;
+ accessor = null;
+
+ return peReader;
+ }
+ finally
+ {
+ if (accessor != null)
+ accessor.Dispose();
+ if (mappedFile != null)
+ mappedFile.Dispose();
+ }
+ }
+
+ private EcmaModule AddModule(string filePath, string expectedSimpleName)
+ {
+ MemoryMappedViewAccessor mappedViewAccessor = null;
+ try
+ {
+ PEReader peReader = OpenPEFile(filePath, out mappedViewAccessor);
+
+ EcmaModule module = new EcmaModule(this, peReader);
+
+ MetadataReader metadataReader = module.MetadataReader;
+ string simpleName = metadataReader.GetString(metadataReader.GetAssemblyDefinition().Name);
+
+ if (expectedSimpleName != null && !simpleName.Equals(expectedSimpleName, StringComparison.OrdinalIgnoreCase))
+ throw new FileNotFoundException("Assembly name does not match filename " + filePath);
+
+ ModuleData moduleData = new ModuleData()
+ {
+ SimpleName = simpleName,
+ FilePath = filePath,
+ Module = module,
+ MappedViewAccessor = mappedViewAccessor
+ };
+
+ lock (this)
+ {
+ ModuleData actualModuleData = _simpleNameHashtable.AddOrGetExisting(moduleData);
+ if (actualModuleData != moduleData)
+ {
+ if (actualModuleData.FilePath != filePath)
+ throw new FileNotFoundException("Module with same simple name already exists " + filePath);
+ return actualModuleData.Module;
+ }
+ mappedViewAccessor = null; // Ownership has been transfered
+
+ _moduleHashtable.AddOrGetExisting(moduleData);
+ }
+
+ // TODO: Thread-safety for symbol reading
+ InitializeSymbolReader(moduleData);
+
+ return module;
+ }
+ finally
+ {
+ if (mappedViewAccessor != null)
+ mappedViewAccessor.Dispose();
+ }
}
public override FieldLayoutAlgorithm GetLayoutAlgorithmForType(DefType type)
@@ -208,7 +315,7 @@ namespace ILCompiler
if (_pdbSymbolProvider == null)
_pdbSymbolProvider = new PdbSymbolProvider();
- moduleData.PdbReader = _pdbSymbolProvider.GetSymbolReaderForFile(moduleData.Path);
+ moduleData.PdbReader = _pdbSymbolProvider.GetSymbolReaderForFile(moduleData.FilePath);
}
public IEnumerable<ILSequencePoint> GetSequencePointsForMethod(MethodDesc method)
@@ -217,7 +324,10 @@ namespace ILCompiler
if (ecmaMethod == null)
return null;
- ModuleData moduleData = _moduleData[ecmaMethod.Module];
+ ModuleData moduleData;
+ _moduleHashtable.TryGetValue(ecmaMethod.Module, out moduleData);
+ Debug.Assert(moduleData != null);
+
if (moduleData.PdbReader == null)
return null;
@@ -230,7 +340,10 @@ namespace ILCompiler
if (ecmaMethod == null)
return null;
- ModuleData moduleData = _moduleData[ecmaMethod.Module];
+ ModuleData moduleData;
+ _moduleHashtable.TryGetValue(ecmaMethod.Module, out moduleData);
+ Debug.Assert(moduleData != null);
+
if (moduleData.PdbReader == null)
return null;
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs
index bd63843b1..839e90baa 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs
@@ -235,9 +235,9 @@ namespace ILCompiler.DependencyAnalysis
private IntPtr _nativeObjectWriter = IntPtr.Zero;
- public ObjectWriter(string outputPath, NodeFactory factory)
+ public ObjectWriter(string objectFilePath, NodeFactory factory)
{
- _nativeObjectWriter = InitObjWriter(outputPath);
+ _nativeObjectWriter = InitObjWriter(objectFilePath);
if (_nativeObjectWriter == IntPtr.Zero)
{
throw new IOException("Fail to initialize Native Object Writer");
@@ -274,9 +274,9 @@ namespace ILCompiler.DependencyAnalysis
Dispose(false);
}
- public static void EmitObject(string OutputPath, IEnumerable<DependencyNode> nodes, NodeFactory factory)
+ public static void EmitObject(string objectFilePath, IEnumerable<DependencyNode> nodes, NodeFactory factory)
{
- using (ObjectWriter objectWriter = new ObjectWriter(OutputPath, factory))
+ using (ObjectWriter objectWriter = new ObjectWriter(objectFilePath, factory))
{
string currentSection = "";
diff --git a/src/ILCompiler.Compiler/src/CppCodeGen/CppWriter.cs b/src/ILCompiler.Compiler/src/CppCodeGen/CppWriter.cs
index e53db6cdd..e12a68f32 100644
--- a/src/ILCompiler.Compiler/src/CppCodeGen/CppWriter.cs
+++ b/src/ILCompiler.Compiler/src/CppCodeGen/CppWriter.cs
@@ -35,6 +35,8 @@ namespace ILCompiler.CppCodeGen
{
_compilation = compilation;
+ _out = new StreamWriter(File.Create(compilation.Options.OutputFilePath));
+
SetWellKnownTypeSignatureName(WellKnownType.Void, "void");
SetWellKnownTypeSignatureName(WellKnownType.Boolean, "uint8_t");
SetWellKnownTypeSignatureName(WellKnownType.Char, "uint16_t");
@@ -394,10 +396,12 @@ namespace ILCompiler.CppCodeGen
{
get
{
- return _compilation.Out;
+ return _out;
}
}
+ private StreamWriter _out;
+
private Dictionary<TypeDesc, List<MethodDesc>> _methodLists;
private StringBuilder _statics;
diff --git a/src/ILCompiler.Compiler/src/project.json b/src/ILCompiler.Compiler/src/project.json
index 7d0d272ed..20fb94127 100644
--- a/src/ILCompiler.Compiler/src/project.json
+++ b/src/ILCompiler.Compiler/src/project.json
@@ -6,6 +6,7 @@
"System.Diagnostics.Debug": "4.0.10",
"System.IO": "4.0.10",
"System.IO.FileSystem": "4.0.0",
+ "System.IO.MemoryMappedFiles": "4.0.0-beta-23419",
"System.Collections": "4.0.10",
"System.Text.Encoding": "4.0.10",
"System.Runtime.InteropServices": "4.0.20",