2008-11-13 5 views
1

У меня есть библиотека C с многочисленными математическими программами для работы с векторами, матрицами, кватернионами и так далее. Он должен оставаться в C, потому что я часто использую его для встроенной работы и как расширение Lua. Кроме того, у меня есть оболочки класса C++, позволяющие более удобное управление объектами и перегрузка операторов для математических операций с использованием C API. Оболочка состоит только из файла заголовка, и как можно больше используется для наложения.Как синхронизировать библиотеки C & C++ с минимальным штрафом за производительность?

Есть ли заметное наказание за упаковку кода C от портирования и встраивание реализации непосредственно в класс C++? Эта библиотека используется во временных критичных приложениях. Таким образом, ускоряет ли устранение косвенности компенсировать головную боль обслуживания двух портов?

Пример интерфейса C:

typedef float VECTOR3[3]; 

void v3_add(VECTOR3 *out, VECTOR3 lhs, VECTOR3 rhs); 

Пример оболочки C++:

class Vector3 
{ 
private: 
    VECTOR3 v_; 

public: 
    // copy constructors, etc... 

    Vector3& operator+=(const Vector3& rhs) 
    { 
     v3_add(&this->v_, this->v_, const_cast<VECTOR3> (rhs.v_)); 
     return *this; 
    } 

    Vector3 operator+(const Vector3& rhs) const 
    { 
     Vector3 tmp(*this); 
     tmp += rhs; 
     return tmp; 
    } 

    // more methods... 
}; 

ответ

2

Сама ваша обертка будет встроена, однако вызов метода в библиотеку C обычно не будет. (Это потребует технически возможной оптимизации времени соединения, но в лучшем случае AFAIK в современных инструментах)

Как правило, вызов функции как таковой не очень дорог. За последние годы стоимость цикла значительно уменьшилась, и его можно легко предсказать, поэтому штраф за казнь как таковой пренебрежимо мал.

Однако, вставка открывает дверь для большей оптимизации: если у вас есть v = a + b + c, ваш класс-оболочка заставляет генерировать переменные стека, тогда как для встроенных вызовов большинство данных могут храниться в Стек FPU. Кроме того, встроенный код позволяет упростить инструкции, учитывая постоянные значения и многое другое.

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


Типичное решение довести C деле ввода в формат, который может быть использован либо в качестве встроенных функций или тела «C»:

// V3impl.inl 
void V3DECL v3_add(VECTOR3 *out, VECTOR3 lhs, VECTOR3 rhs) 
{ 
    // here you maintain the actual implementations 
    // ... 
} 

// C header 
#define V3DECL 
void V3DECL v3_add(VECTOR3 *out, VECTOR3 lhs, VECTOR3 rhs); 

// C body 
#include "V3impl.inl" 


// CPP Header 
#define V3DECL inline 
namespace v3core { 
    #include "V3impl.inl" 
} // namespace 

class Vector3D { ... } 

Это, вероятно, имеет смысл только для выбранных методов по сравнению с простыми телами. Я бы переместил методы в отдельное пространство имен для реализации на C++, поскольку вам обычно не нужны они напрямую.

(Обратите внимание, что встроенный просто компилятор намек, это не заставит метод быть встраиваемыми Но это хорошо. А если размер кода внутреннего цикла превышает кэш команд, встраивание легко вредит производительность)

ли передача/вернуться по ссылке может быть решена, зависит от силы вашего компилятора, я видел много где Foo (X * из) силы стек переменных, в то время как X Foo() действительно держит значения в регистрах.

+0

Использование встроенных C++ или C функций с макросами - хорошая идея, которую я не рассматривал. –

1

Я не думаю, что вы заметите много перфорация разницу. Если предположить, что поддержку платформы целевой все свои типы данных,

я кодирование для DS и несколько других устройств ARM и плавающая точка зла ... Я должен был поплавок определения типа во FixedPoint < 16,8>

+0

На самом деле я использую typedef вместо float, но оставил его здесь для простоты. –

4

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

2

Как обычно со всем, что связано с оптимизацией, ответ заключается в том, что вы должны сами оценить производительность, прежде чем знать, стоит ли оптимизировать.

  • Бенчмарк состоит из двух разных функций, один из которых вызывает функции C-стиля напрямую и другого вызова через обертку. Посмотрите, какой из них работает быстрее, или если разница находится в пределах погрешности вашего измерения (что означало бы, что нет никакой разницы, которую вы можете измерить).
  • Посмотрите на код сборки, сгенерированный двумя функциями на предыдущем шаге (на gcc, используйте -S или -save-temps). Посмотрите, сделал ли компилятор что-то глупое, или если у ваших оберток есть ошибка производительности.

Если разница в производительности слишком велика для того, чтобы не использовать оболочку, переопределение не является хорошей идеей, поскольку вы рискуете ввести ошибки (что может даже привести к результатам, которые выглядят разумно, но не так). Даже если разница большая, было бы проще и менее рискованно просто помнить, что C++ очень совместим с C и использует вашу библиотеку в стиле C даже в коде C++.

1

Если вы обеспокоены тем, что накладные расходы на вызывающие функции замедляют работу, почему бы не попробовать вставить код C или превратить его в макросы?

Кроме того, почему бы не улучшить корректность константы кода C во время ее нахождения - const_cast действительно следует использовать экономно, особенно на интерфейсах, которыми вы управляете.

+0

Я избегал конфликтов со встроенной системной библиотекой, которая не использовала const. Добавление const вызвало бы массу неудобств, но точка была бы хорошо принята. Я рассматриваю исправление этого, так как в настоящее время у меня нет таких проблем. –

+0

Возможно, вы могли бы использовать MYCONST вместо const в заголовке, а затем #define его ни к чему при использовании из встроенного кода и const при использовании из const-correct C++-приложения. Это предполагает, что вы компилируете vector.c дважды, разумеется, один раз в каждом проекте, иначе он не будет связываться. –

+0

На самом деле, может быть, он будет связан, так как он предположительно будет extern «C» в проекте C++. Но я по-прежнему рекомендую компилировать vector.c с константой, включенной в проекте C++, хотя бы потому, что вы знаете, что это работает ... –

3

Как и любой вопрос о производительности, вам будет предложено измерить, чтобы получить ответ (и это строго правильный ответ).

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

Однако, даже если ваши методы обертки не были встроены, я подозреваю, что вы не заметите никакого штрафа за производительность - даже не измеримого - если только метод обертки не был вызван в критическом цикле. Даже тогда это, вероятно, будет только измеримым, если сама обернутая функция не приложит много усилий.

Этот вопрос касается последней вещи, о которой нужно беспокоиться. Сначала подумайте о том, как сделать код правильным, поддерживаемым и использовать соответствующие алгоритмы.

 Смежные вопросы

  • Нет связанных вопросов^_^