C # производительность условного распределения памяти - PullRequest
0 голосов
/ 08 июня 2018

Рассмотрим следующий код C #:

IntPtr native = GetNativeError(/* parameters here */);
return new ManagedError(native);

Затем клиентский код проверяет наличие ошибок следующим образом:

ManagedError err = /* get it with the code above */;
if(err.IsOk()) {
    /* Success */
}

Распределение памяти здесь можно сохранить за счетоператор if:

IntPtr native = GetNativeError(/* parameters here */);
if(native == IntPtr.Zero) {
    return null;
} else {
    return new ManagedError(native);
}

Тогда клиентский код будет проверять наличие ошибки следующим образом:

ManagedError err = /* get it with the code above */;
if(err == null) {
    /* Success */
}

Мой вопрос: какой из подходов быстрее?Первый имеет дополнительное выделение памяти.Второй имеет дополнительный оператор if.

ОБНОВЛЕНИЕ: я имею в виду, в сценарии успеха.Сценарии ошибок случаются редко и могут быть медленными.

Ответы [ 2 ]

0 голосов
/ 08 июня 2018

Во-первых, вы уже написали код в обе стороны .Если вы хотите узнать, какой путь имеет лучшую производительность , запустите обе программы и определите, какая из них имеет лучшую производительность .Это только способ точно ответить на вопрос о производительности, поэтому возьмите свой секундомер.

Если вы не можете измерить, какая из них имеет лучшую производительность, то, очевидно, не имеет значения, какая из них имеетлучшая производительность, потому что разница не может быть обнаружена.Невидимая разница не имеет значения.

Теперь давайте рассмотрим вашу конкретную проблему.Предположим, вы решили сохранить выделение памяти в общем случае успеха. Правильное решение - не придавать специальному значению ноль .Правильное решение - использовать шаблон нулевого объекта.То есть создает специальный экземпляр объекта, который всегда используется там, где в противном случае вы бы использовали null .

class ManagedError 
{
  public static readonly Success = new ManagedError(IntPtr.Zero);
  private ManagedError(IntPtr hr) { ... }
  public ManagedError FromNative(IntPtr hr) 
  {
    if (hr == IntPtr.Zero) return Success;
    return new ManagedError(hr);
  }
}
...
IntPtr native = GetNativeError(/* parameters here */);
return ManagedError.FromNative(native);

Готово.Вы всегда получаете действительный объект, и вы не делаете никаких распределений в общем случае.

Кроме того, как упоминалось в другом ответе: почему это не структура? Почему вы вообще выделяете кучу памяти ссылочного типа ?Если это просто оболочка для intptr, то это должна быть структура;передать его будет так же дешево, как и intptr!

Если вы сделаете его структурой, это станет еще проще, потому что вы просто используете экземпляр структуры по умолчанию в качестве нулевого объекта!

struct ManagedError 
{
  public static readonly Success = default(ManagedError);
  private readonly IntPtr hr;
  public ManagedError(IntPtr hr) { this.hr = hr }
}

И все готово;нет выделения кучи вообще.Вы просто заверните инттр.

0 голосов
/ 08 июня 2018

В общем, второй способ более понятен, для меня: ManagedError, который не является ошибкой, но является успешным, является ManagedResult (см., Например, COM API, использующий HRESULT, который может бытьS_OK (или вообще S_*) или E_* (например, E_FAIL).

Я скажу, что в общем случае выделение бесполезного объекта не является хорошей идеей ... Ноэто даже не плохая идея (накладные расходы очень малы, потому что если объект очень недолговечен, GC немедленно уничтожит его). Тем не менее, он против шаблонов .NET (которые используют Exception для возврата ошибки или bool) ... Так что даже здесь я не вижу большого плюса или большого минуса.

Обратите внимание, что если sizeof(ManagedError) <= IntPtr.Size, то если сделать его struct, это сделает его «свободным» для распределения иобойти (потому что struct с sizeof <= IntPtr.Size не имеет никаких издержек против IntPtr или вообще против управляемой ссылки)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...