Различия в производительности между P / Invoke и C ++ Wrappers - PullRequest
11 голосов
/ 16 сентября 2009

В процессе изучения P / Invoke я задал этот предыдущий вопрос:

Как P / Invoke, когда задействованы указатели

Однако я не совсем понимаю последствия использования P / Invoke в C # по сравнению с созданием оболочки в Managed C ++. Создание той же самой DLL с использованием P / Invoke в C # определенно привело к более чистому интерфейсу, поскольку я мог бы использовать DLLImport на встроенном ресурсе, но будет ли у оболочки Managed C ++ для нативной DLL, где я выполняю маршалинг, лучшую производительность?

Ответы [ 2 ]

12 голосов
/ 16 сентября 2009

Оболочка C ++ должна быть быстрее, посмотрите на эту страницу MSDN :

C ++ Interop использует самый быстрый из возможных методов маршалинга данных, тогда как P / Invoke использует самый надежный метод. Это означает, что C ++ Interop (в типичной для C ++ манере) обеспечивает оптимальную производительность по умолчанию, и программист несет ответственность за устранение случаев, когда это поведение небезопасно или не подходит.

Таким образом, основная причина в том, что P / Invoke выполняет пиннинг, блиттинг, проверку ошибок, а взаимодействие C ++ просто помещает параметры в стек и вызывает функцию.

Еще один момент, о котором следует помнить, это то, что C ++ может вызывать несколько API-интерфейсов за один вызов, в то время как параметр P / Invoke КАЖДЫЙ, передаваемый по адресу, закрепляется и отменяется при КАЖДОМ вызове, копируется и скопировал обратно и т. д.

6 голосов
/ 16 сентября 2009

Получите ли вы лучшую производительность? Зависит от того, что вы делаете и как вы это делаете. Вообще говоря, ваш выигрыш в производительности, скорее всего, будет связан с выполнением управляемых / неуправляемых переходов, и чем больше из них вы сможете вырезать, тем лучше. В идеале, ваш интерфейс с неуправляемым кодом должен быть коротким и не болтать.

Допустим, у вас есть неуправляемый код с коллекцией из нескольких тысяч объектов. Вы можете представить API-интерфейс, подобный этому, управляемому коду:

int GetFooCount();
IntPtr GetFoo(int n);
void ReleaseFoo(IntPtr p);

и это все хорошо, пока вы не начнете использовать его в C # следующим образом:

int total = API.GetFooCount();
IntPtr[] objects = new IntPtr[total];
for (int i=0; i < total; i++) {
    objects[i] = GetFoo(i);
}
// and later:
foreach (IntPtr p in objects) { ReleaseFoo(p); }

, что в сумме == 1000, будет 4002 управляемых / неуправляемых переходов. Если вместо этого у вас есть это:

int GetFooCount();
void GetFoos(IntPtr[] arr, int start, int count);
void ReleaseFoos(IntPtr arr, int start, int count);

тогда вы можете сделать ту же работу с 6 переходами. Как вы думаете, что будет лучше?

Конечно, следующий важный вопрос, который нужно задать, - "стоит ли это повышение производительности?" поэтому не забудьте измерить в первую очередь.

Одна вещь, о которой вы также должны знать, это то, что с STL могут происходить забавные вещи, когда вы работаете с управляемым C ++. У меня есть некоторый неуправляемый библиотечный код, который использует STL. По моему опыту, если я когда-либо касался любого из типов STL в управляемом C ++, ВСЕ они становились управляемыми реализациями. Конечным результатом этого было то, что низкоуровневый код выполнял управляемые / неуправляемые переходы во время итерации списков. Хлоп. Я решил это, никогда не подвергая типы STL управляемому C ++.

По нашему опыту, гораздо лучше (если это возможно) перейти на C # -> управляемую оболочку C ++ -> статическую библиотеку, если у вас есть такая возможность.

...