Fix lock contention during script compilation (#3064)

In rare cases, it was possible to cause high contention on a lock used
while compiling code to run in the interpreter.

This fixes the problem in a couple different ways:

* Use ConditionalWeakTable which has finer granularity in locking
* Avoid dictionary lookups in the most common cases
  - There really aren't too many real cases, I could have covered them
    all, but keeping the code generic is useful for the future.

Not quite related, but I noticed we didn't cache certain interpreter
instructions and it made sense to add a cache because we generate a lot
of array-init instructions.
This commit is contained in:
Jason Shirk 2017-02-06 23:09:55 -08:00 committed by GitHub
parent 5871e1ac4b
commit cc664bc639
2 changed files with 60 additions and 34 deletions

View file

@ -13,6 +13,8 @@
*
* ***************************************************************************/
using System.Collections.Concurrent;
#if !CLR2
using BigInt = System.Numerics.BigInteger;
#endif
@ -22,7 +24,8 @@ using BigInt = System.Numerics.BigInteger;
using System.Reflection;
#endif
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
//using Microsoft.Scripting.Math;
@ -30,45 +33,34 @@ namespace System.Management.Automation.Interpreter
{
internal abstract class InstructionFactory
{
// TODO: weak table for types in a collectible assembly?
private static Dictionary<Type, InstructionFactory> s_factories;
private static ConditionalWeakTable<Type, InstructionFactory> s_factories;
internal static InstructionFactory GetFactory(Type type)
{
if (s_factories == null)
{
s_factories = new Dictionary<Type, InstructionFactory>() {
{ typeof(object), InstructionFactory<object>.Factory },
{ typeof(bool), InstructionFactory<bool>.Factory },
{ typeof(byte), InstructionFactory<byte>.Factory },
{ typeof(sbyte), InstructionFactory<sbyte>.Factory },
{ typeof(short), InstructionFactory<short>.Factory },
{ typeof(ushort), InstructionFactory<ushort>.Factory },
{ typeof(int), InstructionFactory<int>.Factory },
{ typeof(uint), InstructionFactory<uint>.Factory },
{ typeof(long), InstructionFactory<long>.Factory },
{ typeof(ulong), InstructionFactory<ulong>.Factory },
{ typeof(float), InstructionFactory<float>.Factory },
{ typeof(double), InstructionFactory<double>.Factory },
{ typeof(char), InstructionFactory<char>.Factory },
{ typeof(string), InstructionFactory<string>.Factory },
#if !CLR2
{ typeof(BigInt), InstructionFactory<BigInt>.Factory },
#endif
//{ typeof(BigInteger), InstructionFactory<BigInteger>.Factory }
};
var factories = new ConditionalWeakTable<Type, InstructionFactory>();
factories.Add(typeof(object), InstructionFactory<object>.Factory);
factories.Add(typeof(bool), InstructionFactory<bool>.Factory);
factories.Add(typeof(byte), InstructionFactory<byte>.Factory);
factories.Add(typeof(sbyte), InstructionFactory<sbyte>.Factory);
factories.Add(typeof(short), InstructionFactory<short>.Factory);
factories.Add(typeof(ushort), InstructionFactory<ushort>.Factory);
factories.Add(typeof(int), InstructionFactory<int>.Factory);
factories.Add(typeof(uint), InstructionFactory<uint>.Factory);
factories.Add(typeof(long), InstructionFactory<long>.Factory);
factories.Add(typeof(ulong), InstructionFactory<ulong>.Factory);
factories.Add(typeof(float), InstructionFactory<float>.Factory);
factories.Add(typeof(double), InstructionFactory<double>.Factory);
factories.Add(typeof(char), InstructionFactory<char>.Factory);
factories.Add(typeof(string), InstructionFactory<string>.Factory);
factories.Add(typeof(BigInt), InstructionFactory<BigInt>.Factory);
Interlocked.CompareExchange(ref s_factories, factories, null);
}
lock (s_factories)
{
InstructionFactory factory;
if (!s_factories.TryGetValue(type, out factory))
{
factory = (InstructionFactory)typeof(InstructionFactory<>).MakeGenericType(type).GetField("Factory").GetValue(null);
s_factories[type] = factory;
}
return factory;
}
return s_factories.GetValue(type,
t => (InstructionFactory)typeof(InstructionFactory<>).MakeGenericType(t).GetField("Factory").GetValue(null));
}
protected internal abstract Instruction GetArrayItem();
@ -91,6 +83,10 @@ namespace System.Management.Automation.Interpreter
private Instruction _defaultValue;
private Instruction _newArray;
private Instruction _typeAs;
private Instruction[] _newArrayInit;
// This number is somewhat arbitrary - trying to avoid some gc without keeping
// objects (instructions) around that aren't used that often.
private const int MaxArrayInitElementCountCache = 32;
private InstructionFactory() { }
@ -126,6 +122,15 @@ namespace System.Management.Automation.Interpreter
protected internal override Instruction NewArrayInit(int elementCount)
{
if (elementCount < MaxArrayInitElementCountCache)
{
if (_newArrayInit == null)
{
_newArrayInit = new Instruction[MaxArrayInitElementCountCache];
}
return _newArrayInit[elementCount] ?? (_newArrayInit[elementCount] = new NewArrayInitInstruction<T>(elementCount));
}
return new NewArrayInitInstruction<T>(elementCount);
}
}

View file

@ -749,7 +749,28 @@ namespace System.Management.Automation.Interpreter
public void EmitNewArrayInit(Type elementType, int elementCount)
{
Emit(InstructionFactory.GetFactory(elementType).NewArrayInit(elementCount));
// To avoid lock contention in InstructionFactory.GetFactory, we special case the most common
// types of arrays that the compiler creates.
if (elementType == typeof(CommandParameterInternal))
{
Emit(InstructionFactory<CommandParameterInternal>.Factory.NewArrayInit(elementCount));
}
else if (elementType == typeof(CommandParameterInternal[]))
{
Emit(InstructionFactory<CommandParameterInternal[]>.Factory.NewArrayInit(elementCount));
}
else if (elementType == typeof(object))
{
Emit(InstructionFactory<object>.Factory.NewArrayInit(elementCount));
}
else if (elementType == typeof(string))
{
Emit(InstructionFactory<string>.Factory.NewArrayInit(elementCount));
}
else
{
Emit(InstructionFactory.GetFactory(elementType).NewArrayInit(elementCount));
}
}
#endregion