Skip to content

Commit 875ef00

Browse files
authored
Add a workaround for the tests hanging while loading MKL. (#1076)
* Add a workaround for the tests hanging while loading MKL. The workaround is to ensure the MKL library is loaded very early in the test process, so it doesn't cause the deadlock. Workaround #1073 Another deadlock also occurs when running TestAutoInference and TestPipelineSweeper in parallel. Marking these tests to not run in parallel anymore. Workaround #1095 Moving back to the Azure Hosted VS2017 pool to run the tests now that we've narrowed the deadlocks down.
1 parent 70b3c3b commit 875ef00

File tree

12 files changed

+93
-60
lines changed

12 files changed

+93
-60
lines changed

.vsts-dotnet-ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ phases:
1010
name: Windows_NT
1111
buildScript: build.cmd
1212
queue:
13-
name: DotNetCore-Windows
13+
name: Hosted VS2017
1414

1515
- template: /build/ci/phase-template.yml
1616
parameters:

src/Microsoft.ML.PipelineInference/ColumnTypeInference.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,10 @@ public void Apply(IntermediateColumn[] columns)
132132
{
133133
if (!col.RawData.Skip(1)
134134
.All(x =>
135-
{
136-
bool value;
137-
return Conversions.Instance.TryParse(ref x, out value);
138-
})
135+
{
136+
bool value;
137+
return Conversions.Instance.TryParse(ref x, out value);
138+
})
139139
)
140140
{
141141
continue;
@@ -157,10 +157,10 @@ public void Apply(IntermediateColumn[] columns)
157157
{
158158
if (!col.RawData.Skip(1)
159159
.All(x =>
160-
{
161-
Single value;
162-
return Conversions.Instance.TryParse(ref x, out value);
163-
})
160+
{
161+
Single value;
162+
return Conversions.Instance.TryParse(ref x, out value);
163+
})
164164
)
165165
{
166166
continue;
@@ -240,7 +240,7 @@ private static InferenceResult InferTextFileColumnTypesCore(IHostEnvironment env
240240
ch.AssertValue(fileSource);
241241
ch.AssertValue(args);
242242

243-
if (args.ColumnCount==0)
243+
if (args.ColumnCount == 0)
244244
{
245245
ch.Error("Too many empty columns for automatic inference.");
246246
return InferenceResult.Fail();

src/Microsoft.ML.PipelineInference/PurposeInference.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ public static InferenceResult InferPurposes(IHostEnvironment env, IDataView data
339339
if (dataRoles != null)
340340
{
341341
var items = dataRoles.Schema.GetColumnRoles();
342-
foreach(var item in items)
342+
foreach (var item in items)
343343
{
344344
Enum.TryParse(item.Key.Value, out ColumnPurpose purpose);
345345
var col = cols.Find(x => x.ColumnName == item.Value.Name);

src/Microsoft.ML.PipelineInference/RecipeInference.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ public static SuggestedRecipe.SuggestedLearner[] AllowedLearners(IHostEnvironmen
547547
LearnerName = tt.Name
548548
};
549549

550-
if (sl.PipelineNode != null && availableLearnersList.FirstOrDefault(l=> l.Name.Equals(sl.PipelineNode.GetEpName())) != null)
550+
if (sl.PipelineNode != null && availableLearnersList.FirstOrDefault(l => l.Name.Equals(sl.PipelineNode.GetEpName())) != null)
551551
learners.Add(sl);
552552
}
553553

test/Microsoft.ML.Predictor.Tests/Global.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using Microsoft.ML.Runtime.Internal.Internallearn.Test;
56
using Xunit;
67

7-
namespace Microsoft.ML.Runtime.Internal.Internallearn.Test
8+
namespace Microsoft.ML.Runtime.RunTests
89
{
9-
public sealed class GlobalRT
10+
public sealed class Global
1011
{
12+
// See https://github.com/dotnet/machinelearning/issues/1095
13+
public const string AutoInferenceAndPipelineSweeperTestCollectionName = "TestPipelineSweeper and TestAutoInference should not be run at the same time since it causes deadlocks";
14+
1115
[Fact(Skip = "Disabled")]
1216
public void AssertHandlerTest()
1317
{

test/Microsoft.ML.Predictor.Tests/TestAutoInference.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
namespace Microsoft.ML.Runtime.RunTests
1818
{
19+
[Collection(Global.AutoInferenceAndPipelineSweeperTestCollectionName)]
1920
public sealed class TestAutoInference : BaseTestBaseline
2021
{
2122
public TestAutoInference(ITestOutputHelper helper)

test/Microsoft.ML.Predictor.Tests/TestPipelineSweeper.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,17 @@
1515

1616
namespace Microsoft.ML.Runtime.RunTests
1717
{
18+
[Collection(Global.AutoInferenceAndPipelineSweeperTestCollectionName)]
1819
public sealed class TestPipelineSweeper : BaseTestBaseline
1920
{
2021
public TestPipelineSweeper(ITestOutputHelper helper)
2122
: base(helper)
2223
{
2324
}
2425

25-
protected override void InitializeCore()
26+
protected override void Initialize()
2627
{
27-
base.InitializeCore();
28+
base.Initialize();
2829
Env.ComponentCatalog.RegisterAssembly(typeof(AutoInference).Assembly);
2930
}
3031

test/Microsoft.ML.Predictor.Tests/TestPredictors.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ namespace Microsoft.ML.Runtime.RunTests
2828
/// </summary>
2929
public sealed partial class TestPredictors : BaseTestPredictors
3030
{
31-
protected override void InitializeCore()
31+
protected override void Initialize()
3232
{
33-
base.InitializeCore();
33+
base.Initialize();
3434
InitializeEnvironment(Env);
3535
}
3636

test/Microsoft.ML.TestFramework/BaseTestBaseline.cs

Lines changed: 16 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
using System;
1010
using System.Collections.Generic;
1111
using System.IO;
12-
using System.Reflection;
1312
using System.Runtime.InteropServices;
1413
using System.Text;
1514
using System.Text.RegularExpressions;
@@ -22,23 +21,12 @@ namespace Microsoft.ML.Runtime.RunTests
2221
/// <summary>
2322
/// This is a base test class designed to support baseline comparison.
2423
/// </summary>
25-
public abstract partial class BaseTestBaseline : BaseTestClass, IDisposable
24+
public abstract partial class BaseTestBaseline : BaseTestClass
2625
{
2726
public const decimal Tolerance = 10_000_000;
28-
private readonly ITestOutputHelper _output;
2927

30-
protected BaseTestBaseline(ITestOutputHelper helper) : base(helper)
28+
protected BaseTestBaseline(ITestOutputHelper output) : base(output)
3129
{
32-
_output = helper;
33-
ITest test = (ITest)helper.GetType().GetField("test", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(helper);
34-
FullTestName = test.TestCase.TestMethod.TestClass.Class.Name + "." + test.TestCase.TestMethod.Method.Name;
35-
TestName = test.TestCase.TestMethod.Method.Name;
36-
Init();
37-
}
38-
39-
void IDisposable.Dispose()
40-
{
41-
Cleanup();
4230
}
4331

4432
internal const string RawSuffix = ".raw";
@@ -93,11 +81,10 @@ void IDisposable.Dispose()
9381
private bool _normal;
9482
private bool _passed;
9583

96-
public string TestName { get; set; }
97-
public string FullTestName { get; set; }
98-
99-
public void Init()
84+
protected override void Initialize()
10085
{
86+
base.Initialize();
87+
10188
// Create the output and log directories.
10289
Contracts.Check(Directory.Exists(Path.Combine(RootDir, TestDir, "BaselineOutput")));
10390
string logDir = Path.Combine(OutDir, _logRootRelPath);
@@ -111,15 +98,17 @@ public void Init()
11198
_passed = true;
11299
Env = new ConsoleEnvironment(42, outWriter: LogWriter, errWriter: LogWriter)
113100
.AddStandardComponents();
114-
InitializeCore();
115101
}
116102

117-
public void Cleanup()
103+
// This method is used by subclass to dispose of disposable objects
104+
// such as LocalEnvironment.
105+
// It is called as a first step in test clean up.
106+
protected override void Cleanup()
118107
{
119-
// REVIEW: Is there a way to determine whether the test completed normally?
120-
// Requiring tests to call Done() is hokey.
108+
if (Env != null)
109+
Env.Dispose();
110+
Env = null;
121111

122-
CleanupCore();
123112
Contracts.Assert(IsActive);
124113
Log("Test {0}: {1}: {2}", TestName,
125114
_normal ? "completed normally" : "aborted",
@@ -128,20 +117,8 @@ public void Cleanup()
128117
Contracts.AssertValue(LogWriter);
129118
LogWriter.Dispose();
130119
LogWriter = null;
131-
}
132-
133-
protected virtual void InitializeCore()
134-
{
135-
}
136120

137-
// This method is used by subclass to dispose of disposable objects
138-
// such as LocalEnvironment.
139-
// It is called as a first step in test clean up.
140-
protected virtual void CleanupCore()
141-
{
142-
if (Env != null)
143-
Env.Dispose();
144-
Env = null;
121+
base.Cleanup();
145122
}
146123

147124
protected bool IsActive { get { return LogWriter != null; } }
@@ -210,15 +187,15 @@ protected void Log(string msg)
210187
Contracts.Assert(IsActive);
211188
Contracts.AssertValue(LogWriter);
212189
LogWriter.WriteLine(msg);
213-
_output.WriteLine(msg);
190+
Output.WriteLine(msg);
214191
}
215192

216193
protected void Log(string fmt, params object[] args)
217194
{
218195
Contracts.Assert(IsActive);
219196
Contracts.AssertValue(LogWriter);
220197
LogWriter.WriteLine(fmt, args);
221-
_output.WriteLine(fmt, args);
198+
Output.WriteLine(fmt, args);
222199
}
223200

224201
protected string GetBaselineDir(string subDir)
@@ -898,4 +875,4 @@ public void TestTimeRegex()
898875
Done();
899876
}
900877
}
901-
}
878+
}

test/Microsoft.ML.TestFramework/BaseTestClass.cs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,24 @@
33
// See the LICENSE file in the project root for more information.
44

55
using Microsoft.ML.Runtime.Internal.Internallearn.Test;
6+
using System;
67
using System.Globalization;
78
using System.IO;
9+
using System.Reflection;
810
using System.Threading;
911
using Xunit.Abstractions;
1012

1113
namespace Microsoft.ML.TestFramework
1214
{
13-
public class BaseTestClass
15+
public class BaseTestClass : IDisposable
1416
{
1517
private readonly string _rootDir;
1618
private readonly string _outDir;
1719
private readonly string _dataRoot;
1820

21+
public string TestName { get; set; }
22+
public string FullTestName { get; set; }
23+
1924
static BaseTestClass() => GlobalBase.AssemblyInit();
2025

2126
public BaseTestClass(ITestOutputHelper output)
@@ -31,6 +36,28 @@ public BaseTestClass(ITestOutputHelper output)
3136
Directory.CreateDirectory(_outDir);
3237
_dataRoot = Path.Combine(_rootDir, "test", "data");
3338
Output = output;
39+
40+
ITest test = (ITest)output.GetType().GetField("test", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(output);
41+
FullTestName = test.TestCase.TestMethod.TestClass.Class.Name + "." + test.TestCase.TestMethod.Method.Name;
42+
TestName = test.TestCase.TestMethod.Method.Name;
43+
44+
// write to the console when a test starts and stops so we can identify any test hangs/deadlocks in CI
45+
Console.WriteLine($"Starting test: {FullTestName}");
46+
Initialize();
47+
}
48+
49+
void IDisposable.Dispose()
50+
{
51+
Cleanup();
52+
Console.WriteLine($"Finished test: {FullTestName}");
53+
}
54+
55+
protected virtual void Initialize()
56+
{
57+
}
58+
59+
protected virtual void Cleanup()
60+
{
3461
}
3562

3663
protected string RootDir => _rootDir;

test/Microsoft.ML.TestFramework/DataPipe/Parquet.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ namespace Microsoft.ML.Runtime.RunTests
1010
{
1111
public sealed partial class TestParquet : TestDataPipeBase
1212
{
13-
protected override void InitializeCore()
13+
protected override void Initialize()
1414
{
15-
base.InitializeCore();
15+
base.Initialize();
1616
Env.ComponentCatalog.RegisterAssembly(typeof(ParquetLoader).Assembly);
1717
}
1818

test/Microsoft.ML.TestFramework/GlobalBase.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// }
1212

1313
using System;
14+
using System.Runtime.InteropServices;
1415
using Xunit;
1516

1617
namespace Microsoft.ML.Runtime.Internal.Internallearn.Test
@@ -22,6 +23,28 @@ public static void AssemblyInit()
2223
System.Diagnostics.Debug.WriteLine("*** Setting test assertion handler");
2324
var prev = Contracts.SetAssertHandler(AssertHandler);
2425
Contracts.Check(prev == null, "Expected to replace null assertion handler!");
26+
27+
// HACK: ensure MklImports is loaded very early in the tests so it doesn't deadlock while loading it later.
28+
// See https://github.com/dotnet/machinelearning/issues/1073
29+
Mkl.PptrfInternal(Mkl.Layout.RowMajor, Mkl.UpLo.Up, 0, Array.Empty<double>());
30+
}
31+
32+
private static class Mkl
33+
{
34+
public enum Layout
35+
{
36+
RowMajor = 101,
37+
ColMajor = 102
38+
}
39+
40+
public enum UpLo : byte
41+
{
42+
Up = (byte)'U',
43+
Lo = (byte)'L'
44+
}
45+
46+
[DllImport("MklImports", EntryPoint = "LAPACKE_dpptrf")]
47+
public static extern int PptrfInternal(Layout layout, UpLo uplo, int n, double[] ap);
2548
}
2649

2750
public static void AssemblyCleanup()

0 commit comments

Comments
 (0)