[Я вижу, я действительно не ответил на вопрос о том, как вы будете измерять; лучший способ измерить это просто с помощью некоторого инструментария, либо с помощью классов инструментария (см .: http://msdn.microsoft.com/en-us/library/aa645516(v=vs.71).aspx), или даже с помощью чего-то столь же простого, как размещение в некоторых таймерах любых нужных вам вызовов. Итак, самое грубое Например, когда мы пытались найти снижение производительности, полученное, например, при нашем вызове между C # и ATL COM, мы просто помещали таймеры вокруг пустого вызова функции, запускали таймер, работали в тесном цикле между C # и пустая функция ATL COM, сделать достаточное количество циклов, чтобы мы могли получить разумно согласованные ответы между прогонами, а затем сделать то же самое в C ++. Тогда разница между этими двумя числами - это издержки для выполнения вызова через эту границу.]
У меня нет точных цифр, но я могу ответить из предыдущего опыта, что до тех пор, пока вы используете вещи эффективно, C # работает с очень небольшими, если вообще таковыми, накладными расходами по сравнению с тем, что можно ожидать в C ++, в зависимости от того, что вы пытаетесь сделать.
Я работал над несколькими приложениями, которые собирали очень большие объемы ультразвуковых данных с очень высокой частотой (платы АЦП 100 МГц-3 ГГц) и, выполняя определенные действия, как вы предлагаете (например, массивы байтов [], выделенные в управляемом коде и затем блокируются как указатели и передаются как буферы для данных, передавая большие объемы данных и обрабатывая их для формирования различных частей).
Давным-давно, когда мы общались с кодом C ++ на VB6, мы оборачивали C ++ в простые COM-объекты ATL и передавали указатели назад и вперед, когда это необходимо для данных и обработки изображений. Мы практиковали аналогичные методы намного позже с C # в VS.NET 2003. Кроме того, я написал здесь библиотеку для вопроса, который учитывает массивное неуправляемое хранилище данных, которое может обеспечить поддержку очень больших массивов и операций с массивами, а также даже много функциональности типа LINQ! Использование полей массива вместо огромного количества объектов . (примечание: есть некоторые проблемы с подсчетом ссылок, которые были в последней версии, и я еще не выследил.)
Кроме того, я провел некоторый интерфейс с использованием ATL COM с библиотекой FFTW, чтобы обеспечить высокую производительность высокопроизводительного DSP, хотя эта библиотека еще не совсем готова для прайм-тайма, она была основой решения для всплеска, которое я создал для ссылка выше, и всплеск дал мне большую часть информации, которую я искал, чтобы завершить мой гораздо более полноценный неуправляемый распределитель памяти и обработку быстрого массива, поддерживающую как внешнее, так и неуправляемое распределение из неуправляемой кучи, которая в конечном итоге будет замените обработку, которая в настоящее время существует в библиотеке 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 инструкций.
Примеры заголовков разделов: «Делать короткие вызовы», «Использовать циклы для итерации строк», «Следите за возможностями асинхронного ввода-вывода».
Некоторые фрагменты из указанной библиотеки быстрой памяти:
в 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;
от 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
//---------------------------------------------------------------------------------------------------------------