Dan*_*dor 8 .net performance unmanaged
在.NET中,有几个地方必须保留托管代码并进入非托管又名本机代码领域.仅举几例:
总是有关于从一侧跳到另一侧的开销的评论,我的问题是,如果有人测量了正在发生的确切开销,并且可以解释如何计算它.例如,也许byte[]可以转换为IntPtr甚至可以转换为byte*.NET,并帮助编组程序节省一些CPU周期.
获取托管阵列的地址确实是可能的.
首先,您必须使用System.Runtime.InteropServices.GCHandle固定数组,以便垃圾收集器不会移动数组.只要非托管代码可以访问托管阵列,就必须保持分配此句柄.
byte[] the_array = ... ;
GCHandle pin = GCHandle.Alloc(the_array, GCHandleType.Pinned);
Run Code Online (Sandbox Code Playgroud)
然后,您应该能够使用System.Runtime.InteropServices.Marshal.UnsafeAddrOfPinnedArrayElement将IntPtr获取到数组中的任何元素.
IntPtr p = Marshal.UnsafeAddrOfPinnedArrayElement(the_array,0);
Run Code Online (Sandbox Code Playgroud)
重要:固定对象会严重影响GC操作.能够在堆中移动对象是现代GC可以(稍微)跟上手动内存管理的原因之一.通过固定托管堆中的对象,GC失去了它相对于手动内存管理的一个性能优势:相对未分段的堆.
因此,如果您计划将这些数组"保留在非托管端"一段时间,请考虑复制该数组.复制内存的速度非常快.使用Marshal.Copy(*)方法从托管内存复制到非托管内存,反之亦然.
[我看到我并没有真正回答你将如何衡量的问题; 测量的最佳方法是使用一些仪器,或者使用仪器类(参见:http://msdn.microsoft.com/en-us/library/aa645516(v = vs.71).aspx)或者甚至是某些东西就像在一些定时器中放置你感兴趣的任何调用一样简单.所以,在最粗略的形式中,当我们试图找到所遇到的性能时,例如,在C#和ATL COM之间的调用中,我们只会放置在一个空函数调用周围的定时器,我们将启动一个计时器,在C#和空ATL COM函数之间的紧密循环中运行,做足够的循环,我们能够在运行之间得到合理一致的答案,然后在C++中做同样的事情.然后,这两个数字之间的差异是跨越该边界进行调用的开销.
我真的没有任何硬数字,但我可以回答以前的经验,只要你以有效的方式使用东西,C#执行的开销很小,如果有的话,超出了人们对C++的期望,取决于你试图做的确切性质.
我研究了几种通过非常高的频率(100MHz-3GHz A/D板)收集大量超声波数据的应用程序,并按照你的建议做某些事情(比如托管代码中分配的byte []数组然后锁定作为指针并作为数据的缓冲区传递;传输大量数据并处理它以对各个部分进行成像).
回过头来,我们将C++代码与VB6进行通信,我们将C++包装在ATL Simple COM对象中,并在需要数据和成像时来回传递指针.我们在VS.NET 2003中使用C#实现了类似的技术.另外,我在这里写了一个问题库,允许大量的非托管数据存储,可以支持非常大的数组和数组操作,甚至很多LINQ类功能!使用数组字段而不是大量的对象.(注意:最新版本中的引用计数存在一些问题,我还没有跟踪过.)
此外,我已经使用ATL COM与FFTW库进行了一些接口,以便执行高性能DSP以达到良好的效果,尽管该库还没有为黄金时段做好准备,它是我为上面的链接创建的尖峰解决方案的基础和spike给了我大部分信息,我正在寻找完成我更全面的非托管内存分配器和快速阵列处理,支持来自非托管堆的外部分配和非托管分配,最终将取代处理目前存在于FFTW C#库中.
所以,重点是,我认为性能损失非常夸大,特别是我们现在拥有的处理能力.事实上,如果你只是注意避免一些陷阱(比如调用许多小的跨界函数而不是传递缓冲区,或者多次分配字符串等),你可以使用C#本身获得非常好的性能.但是当谈到高速处理时,C#仍然适合我提到的所有场景.是否需要一点预见,是的,有时候.但是,在开发速度,可维护性和可理解性方面获得的优势,我花时间了解如何获得我需要的性能,这一直远远少于在C++中主要或完全开发所花费的时间.
我的两位.(哦,有一点需要注意,我特别提到了ATL COM,因为你在使用MFC时所取得的性能并不值得.我记得,当通过MFC COM对象调用它时,它大约慢了两个数量级. ATL一个,并没有满足我们的需求.另一方面,ATL只比在C++中直接调用等效函数慢一点.对不起,我不记得任何特定的数字,除了那个,即使有大量的我们收集和移动的超声波数据,我们没有发现它是瓶颈.)
哦,我发现了这个:http://msdn.microsoft.com/en-us/library/ms973839.aspx ".NET应用程序中的性能提示和技巧".我觉得这句话非常有趣:
要加快转换时间,请尽量使用P/Invoke.如果需要数据编组,则开销仅为31条指令加上编组成本,否则只有8条.COM互操作更加昂贵,需要超过65条指令.
示例部分标题:"Make Chunky Calls","Use for Forops for String Iteration","Be on the Lookout for Asynchronous IO Opportunities".
参考快速存储库中的一些片段:
在 MemoryArray.cs
public MemoryArray(int parElementCount, int parElementSize_bytes)
{
Descriptor =
new MemoryArrayDescriptor
(
Marshal.AllocHGlobal(parElementCount * parElementSize_bytes),
parElementSize_bytes,
parElementCount
);
}
protected override void OnDispose()
{
if (Descriptor.StartPointer != IntPtr.Zero)
Marshal.FreeHGlobal(Descriptor.StartPointer);
base.OnDispose();
}
// this really should only be used for random access to the items, if you want sequential access
// use the enumerator which uses pointer math via the array descriptor's TryMoveNext call.
//
// i haven't figured out exactly where it would go, but you could also do something like
// having a member MemoryArrayItem that gets updated here rather than creating a new one each
// time; that would break anything that was trying to hold on to a reference to the item because
// it will no longer be immutable.
//
// that could be remedied by something like a call that would return a new copy of the item if it
// was to be held onto. i would definitely need to see that i needed the performance boost and
// that it was significant enough before i would contradict the users expectations on that one.
public MemoryArrayItem this[int i]
{
get
{
return new MemoryArrayItem(this, Descriptor.GetElementPointer(i), Descriptor.ElementSize_bytes);
}
}
// you could also do multiple dimension indexing; to do so you would have to pass in dimensions somehow in
// the constructor and store them.
//
// there's all sorts of stuff you could do with this; take various slices, etc, do switching between
// last-to-first/first-to-last/custom dimension ordering, etc, but i didn't tackle that for the example.
//
// if you don't need to error check here then just you could always do something like:
public MemoryArrayItem this[int x, int y]
{
get
{
if (myDimensions == null)
throw new ArrayTypeMismatchException("attempted to index two dimensional array without calling SetDimensions()");
if (myDimensions.Length != 2)
throw new ArrayTypeMismatchException("currently set dimensions do not provide a two dimensional array. [dimension: " + myDimensions.Length + "]");
int RowSize_bytes = myDimensions[0] * Descriptor.ElementSize_bytes;
return new MemoryArrayItem(this, Descriptor.StartPointer + (y * RowSize_bytes) + x * Descriptor.ElementSize_bytes, Descriptor.ElementSize_bytes);
}
}
public void SetDimensions(int[] parDimensions)
{
if (parDimensions.Length <= 0)
throw new Exception("unable to set array to dimension of zero.");
for (int i = 0; i < parDimensions.Length; ++i)
if (parDimensions[i] <= 0)
throw new ArgumentOutOfRangeException("unable to set dimension at index " + i.ToString() + " to " + parDimensions[i] + ".");
myDimensions = new int[parDimensions.Length];
parDimensions.CopyTo(myDimensions, 0);
}
private int[] myDimensions = null;
Run Code Online (Sandbox Code Playgroud)
从 MemoryArrayEnumerator.cs
public class MemoryArrayEnumerator :
IEnumerator<MemoryArrayItem>
{
// handles reference counting for the main array
private AutoReference<MemoryArray> myArray;
private MemoryArray Array { get { return myArray; } }
private IntPtr myCurrentPosition = IntPtr.Zero;
public MemoryArrayEnumerator(MemoryArray parArray)
{
myArray = AutoReference<MemoryArray>.CreateFromExisting(parArray);
}
//---------------------------------------------------------------------------------------------------------------
#region IEnumerator<MemoryArrayItem> implementation
//---------------------------------------------------------------------------------------------------------------
public MemoryArrayItem Current
{
get
{
if (Array.Descriptor.CheckPointer(myCurrentPosition))
return new MemoryArrayItem(myArray, myCurrentPosition, Array.Descriptor.ElementSize_bytes);
else
throw new IndexOutOfRangeException("Enumerator Error: Current() was out of range");
}
}
public void Dispose()
{
myArray.Dispose();
}
object System.Collections.IEnumerator.Current
{
get { throw new NotImplementedException(); }
}
public bool MoveNext()
{
bool RetVal = true;
if (myCurrentPosition == IntPtr.Zero)
myCurrentPosition = Array.Descriptor.StartPointer;
else
RetVal = Array.Descriptor.TryMoveNext(ref myCurrentPosition);
return RetVal;
}
public void Reset()
{
myCurrentPosition = IntPtr.Zero;
}
//---------------------------------------------------------------------------------------------------------------
#endregion IEnumerator<MemoryArrayItem> implementation
//---------------------------------------------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1762 次 |
| 最近记录: |