Вопросы производительности взаимодействия (C++)
Обновлен: Ноябрь 2007
В этом разделе описаны рекомендации по уменьшении влияния управляемых и неуправляемых переходов взаимодействия на производительность во время выполнения.
В Visual C++ поддерживается тот же механизм взаимодействия, что и в других языках .NET, таких как Visual Basic и C# (P/Invoke), но также поддерживается взаимодействие, характерное только для Visual C++ (взаимодействие C++). Для приложений, в которых необходимо обеспечить высокую производительность, важно понимание влияния каждого метода взаимодействия на производительность.
Вне зависимости от используемого метода взаимодействия, специальные последовательности передачи, называемые преобразователями, требуются при каждом вызове управляемой функцией неуправляемой функции и наоборот. Эти преобразователи автоматически вставляются компилятором Visual C++, но важно помнить, что в итоге эти передачи могут снизить производительность.
Уменьшение количества передач
Один из способов снижения влияния преобразователей взаимодействия заключается в реструктуризации интерфейсов, используемых для минимизации управляемых и неуправляемых передач. Значительного повышения производительности можно достичь, обработав активно используемые интерфейсы, вовлеченные в частые вызовы между управляемыми и неуправляемыми функциями. Например, управляемая функция, вызывающая неуправляемую функцию в непрерывном цикле, является хорошим кандидатом для оптимизации кода. Если цикл переместить на неуправляемую сторону или если создается управляемая версия неуправляемого вызова (возможно с помощью постановки данных в очередь на управляемой стороне и последующего маршалинга их в неуправляемый API после цикла), число передач можно значительно уменьшить.
Выбор между P/Invoke и взаимодействиями C++
Для языков .NET, таких как Visual Basic и C#, предписанным методом для взаимодействия с собственными компонентами является P/Invoke. Так как P/Invoke поддерживается платформой .NET Framework, Visual C++ также поддерживает его, но в Visual C++ также представлена поддержка взаимодействия, называемая взаимодействием C++. Использование взаимодействия C++ предпочтительнее P/Invoke, так как интерфейс P/Invoke не является строго типизированным. В результате возникают ошибки во время выполнения, но взаимодействие C++ также позволяет получить улучшенную производительность по сравнению с P/Invoke.
Для обоих методов требуется выполнение нескольких действий при вызове управляемой функцией неуправляемой функции.
Аргументы функции маршалируются из типов среды CLR в собственные типы.
Выполняется преобразователь из управляемого кода в неуправляемый.
Вызывается неуправляемая функция (с помощью собственных версий аргументов).
Выполняется преобразователь из неуправляемого кода в управляемый.
Тип возвращаемого значения и любые входные или выходные аргументы функции маршалируются из собственных типов в типы среды CLR.
Преобразователи из управляемого кода в неуправляемый требуются для общей работы взаимодействия, но необходимое маршалинг данных зависит от используемых типов данных, подписи функции и того, как используются данные.
Маршалинг данных при взаимодействии C++ является самым простым: выполняется простое побитовое копирование параметров из управляемой версии в управляемую (или наоборот), и никакие преобразования не осуществляются. В интерфейсе P/Invoke это происходит, только если все параметры являются простыми преобразуемыми типами. В противном случае P/Invoke выполняет очень сложные действия для преобразования каждого управляемого параметра в соответствующий собственный тип и наоборот, если аргументы помечены, как "out" или "in,out".
Другими словами, взаимодействие C++ использует самый быстрый из возможных методов маршалинга данных, в то время как P/Invoke использует самый трудоемкий метод. Это означает, что взаимодействие C++ (обычным для C++ способом) по умолчанию обеспечивает оптимальную производительность, а программист несет ответственность за определение случаев, в которых подобное поведение не является безопасным или подходящим.
Для взаимодействия C++ требуется, чтобы маршалинг данных происходило явно, но преимущество для программиста состоит в том, что он свободно может выбирать, что является более подходящим для конкретного вида данных и как их лучше использовать. Кроме того, хотя механизм маршалинга данных P/Invoke можно изменить определенным образом, взаимодействие C++ позволяет настраивать маршалинг данных для каждого вызова. При использовании интерфейса P/Invoke это невозможно.
Дополнительные сведения о взаимодействии C++ см. в разделе Использование взаимодействия языка C++ (неявный PInvoke).