Dryad/LinqToDryad/DryadVertexEnv.cs

316 lines
12 KiB
C#
Raw Blame History

/*
Copyright (c) Microsoft Corporation
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License
at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER
EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions and
limitations under the License.
*/
//
// <20> Microsoft Corporation. All rights reserved.
//
using System;
using System.Collections.Generic;
using System.IO;
using System.Globalization;
using System.Reflection;
using System.Runtime;
using System.Diagnostics;
using Microsoft.Research.DryadLinq;
namespace Microsoft.Research.DryadLinq.Internal
{
// The class encapsulates the external environment in which a
// managed query operator executes.
public class HpcLinqVertexEnv
{
private const string VERTEX_EXCEPTION_FILENAME = @"VertexException.txt";
private IntPtr m_nativeHandle;
private UInt32 m_numberOfInputs;
private UInt32 m_numberOfOutputs;
private Int32 m_nextInputPort;
private Int32 m_nextOutputPort;
private string[] m_argList;
private HpcLinqVertexParams m_vertexParams;
private bool m_useLargeBuffer;
private bool m_keepInputPortOrder;
private bool m_multiThreading;
public HpcLinqVertexEnv(string args, HpcLinqVertexParams vertexParams)
{
this.m_argList = args.Split('|');
this.m_nativeHandle = new IntPtr(Int64.Parse(this.m_argList[0], NumberStyles.HexNumber));
this.m_numberOfInputs = HpcLinqNative.GetNumOfInputs(this.m_nativeHandle);
this.m_numberOfOutputs = HpcLinqNative.GetNumOfOutputs(this.m_nativeHandle);
this.m_nextInputPort = 0;
this.m_nextOutputPort = 0;
this.m_vertexParams = vertexParams;
this.m_useLargeBuffer = vertexParams.UseLargeBuffer;
this.m_keepInputPortOrder = vertexParams.KeepInputPortOrder;
this.m_multiThreading = vertexParams.MultiThreading;
if (this.m_numberOfOutputs > 0)
{
this.SetInitialWriteSizeHint();
}
Debug.Assert(vertexParams.InputArity <= this.m_numberOfInputs);
Debug.Assert(vertexParams.OutputArity <= this.m_numberOfOutputs);
}
public bool MultiThreading
{
get { return m_multiThreading; }
set { m_multiThreading = value; }
}
internal IntPtr NativeHandle
{
get { return this.m_nativeHandle; }
}
public UInt32 NumberOfInputs
{
get { return this.m_numberOfInputs; }
}
public UInt32 NumberOfOutputs
{
get { return this.m_numberOfOutputs; }
}
public Int32 NumberOfArguments
{
get { return this.m_argList.Length; }
}
public string GetArgument(Int32 idx)
{
return this.m_argList[idx];
}
private bool UseLargeBuffer
{
get { return this.m_useLargeBuffer; }
}
internal bool KeepInputPortOrder
{
get { return this.m_keepInputPortOrder; }
}
public Int64 VertexId
{
get {
return HpcLinqNative.GetVertexId(this.m_nativeHandle);
}
}
public HpcVertexReader<T> MakeReader<T>(HpcLinqFactory<T> readerFactory)
{
if (this.m_nextInputPort + 1 < this.m_vertexParams.InputArity)
{
UInt32 portNum = (UInt32)this.m_nextInputPort++;
return new HpcVertexReader<T>(this, readerFactory, portNum);
}
else
{
UInt32 startPort = (UInt32)this.m_nextInputPort;
UInt32 endPort = this.NumberOfInputs;
return new HpcVertexReader<T>(this, readerFactory, startPort, endPort);
}
}
public HpcVertexWriter<T> MakeWriter<T>(HpcLinqFactory<T> writerFactory)
{
if (this.m_nextOutputPort + 1 < this.m_vertexParams.OutputArity)
{
UInt32 portNum = (UInt32)this.m_nextOutputPort++;
return new HpcVertexWriter<T>(this, writerFactory, portNum);
}
else
{
UInt32 startPort = (UInt32)this.m_nextOutputPort;
UInt32 endPort = this.NumberOfOutputs;
return new HpcVertexWriter<T>(this, writerFactory, startPort, endPort);
}
}
public static HpcBinaryReader MakeBinaryReader(NativeBlockStream nativeStream)
{
return new HpcBinaryReader(nativeStream);
}
public static HpcBinaryReader MakeBinaryReader(IntPtr handle, UInt32 port)
{
return new HpcBinaryReader(handle, port);
}
public static HpcBinaryWriter MakeBinaryWriter(NativeBlockStream nativeStream)
{
return new HpcBinaryWriter(nativeStream);
}
public static HpcBinaryWriter MakeBinaryWriter(IntPtr handle, UInt32 port, Int32 buffSize)
{
return new HpcBinaryWriter(handle, port, buffSize);
}
private static Exception s_lastReportedException;
internal static int ErrorCode { get; set; }
//
// This method gets called by the generated vertex code, as well as VertexBridge to report exceptions.
// The exception will be dumped to "VertexException.txt" in the working directory.
//
public static void ReportVertexError(Exception e)
{
// We first need to check whether the same exception object was already reported recently,
// and ignore the second call.
// This will be the case for most vertex exceptions because 1) the generated vertex code catches the exceptions,
// calls ReportVertexError and rethrows, and right after that 2) VertexBridge will receive the same exception
// wrapped in a TargetInvocationException, and call ReportVertexError again after extracting the inner exception.
//
// The second call from the VertexBridge is necessary because some exceptions
// (particularly TypeLoadException due to static ctors) happen in the vertex DLL,
// but just before the try/catch blocks in the vertex entry point (therefore are missed by 1).
if (s_lastReportedException == e) return;
s_lastReportedException = e;
// add to HpcLog
DryadLinqLog.Add("Vertex failed with the following exception:");
DryadLinqLog.Add("{0}", e.ToString());
// also write out to the standalone vertex exception file in the working directory
using (StreamWriter exceptionFile = new StreamWriter(VERTEX_EXCEPTION_FILENAME))
{
exceptionFile.WriteLine(e.ToString());
}
if (ErrorCode == 0) throw e;
}
internal unsafe Int32 GetWriteBuffSize()
{
MEMORYSTATUSEX memStatus = new MEMORYSTATUSEX();
memStatus.dwLength = (UInt32)sizeof(MEMORYSTATUSEX);
UInt64 maxSize = 512 * 1024 * 1024UL;
if (HpcLinqNative.GlobalMemoryStatusEx(ref memStatus))
{
maxSize = memStatus.ullAvailPhys / 4;
}
if (this.m_vertexParams.RemoteArch == "i386")
{
maxSize = Math.Min(maxSize, 1024 * 1024 * 1024UL);
}
if (this.NumberOfOutputs > 0)
{
maxSize = maxSize / this.NumberOfOutputs;
}
UInt64 buffSize = (this.UseLargeBuffer) ? (256 * 1024 * 1024UL) : (1024 * 1024UL);
if (buffSize > maxSize) buffSize = maxSize;
if (buffSize < (8 * 1024UL)) buffSize = 8 * 1024;
return (Int32)buffSize;
}
internal Int64 GetInputSize()
{
Int64 totalSize = 0;
for (UInt32 i = 0; i < this.m_numberOfInputs; i++)
{
Int64 channelSize = HpcLinqNative.GetExpectedLength(this.NativeHandle, i);
if (channelSize == -1) return -1;
totalSize += channelSize;
}
return totalSize;
}
internal void SetInitialWriteSizeHint()
{
Int64 inputSize = this.GetInputSize();
UInt64 hsize = (inputSize == -1) ? (5 * 1024 * 1024 * 1024UL) : (UInt64)inputSize;
hsize /= this.NumberOfOutputs;
for (UInt32 i = 0; i < this.NumberOfOutputs; i++)
{
HpcLinqNative.SetInitialSizeHint(this.m_nativeHandle, i, hsize);
}
}
//
// The Vertex Host native layer will use this bridge method to invoke the vertex entry point
// instead of invoking it directly through the CLR host.
// This has the advantage of doing all the assembly load and invoke work for the generated
// vertex assembly to happen in a managed context, so that any type or assembly load exceptions
// can be caught and reported in full detail.
//
private static void VertexBridge(string vertexBridgeArgs)
{
DryadLinqLog.IsOn = true;
DryadLinqLog.Add(".NET runtime version = v{0}.{1}.{2}",
Environment.Version.Major,
Environment.Version.Minor,
Environment.Version.Build);
DryadLinqLog.Add(".NET runtime GC = {0}({1})",
(GCSettings.IsServerGC) ? "ServerGC" : "WorkstationGC",
GCSettings.LatencyMode);
try
{
string[] splitArgs = vertexBridgeArgs.Split(',');
if (splitArgs.Length != 4)
{
throw new ArgumentException(string.Format(SR.VertexBridgeBadArgs, vertexBridgeArgs), "vertexBridgeArgs");
}
// @TODO: Temporary hack to find the vertex DLL from the job dir (which is currently always one level up from the WD).
// As part of bug 12618 we need to pass this down from the VH.
string moduleName = Path.Combine("..", splitArgs[0]);
string className = splitArgs[1];
string methodName = splitArgs[2];
string nativeChannelString = splitArgs[3];
Assembly vertexAssembly = Assembly.LoadFrom(moduleName);
DryadLinqLog.Add("Vertex Bridge loaded assembly {0}", vertexAssembly.Location);
MethodInfo vertexMethod = vertexAssembly.GetType(className).GetMethod(methodName, BindingFlags.Static | BindingFlags.Public);
vertexMethod.Invoke(null, new object[] { nativeChannelString });
}
catch (Exception e)
{
//
// Any exception that happens in the vertex code will come wrapped in a TargetInvocationException since we're using Invoke().
// We only want to report the inner exception in this case.
// If the exception is of another type (most likely one coming from the Assembly.LoadFrom() call), then we will report it as is.
//
if (e is TargetInvocationException && e.InnerException != null)
{
ReportVertexError(e.InnerException);
if (ErrorCode == 0) throw e.InnerException;
}
else
{
ReportVertexError(e);
if (ErrorCode == 0) throw;
}
}
}
}
}