3

私は、プリミティブ/値型のプロパティへのアクセスを (合理的に) 可能な限り高速にし、できれば C++/CLR に保持することに重点を置いて、テンプレートをジェネリックに結合しようとしています。次の単純なテンプレートを検討してください。

template< typename type >
class value_wrapper {
public:
    type value;
};

およびその対応物:

generic< typename T >
public ref class ValueWrapper {
    ...
public:
    property T Value {
        T get() {
            if( T::typeid == System::Int32::typeid )
                return ( T )( ( value_wrapper< int > * )ptr )->value;
            ...branches for some other types...
            // if all else fails:
            return ( T )( Object ^ )( ( value_wrapper< gcroot< Object ^ > > * )ptr )->value;
        }
        ...
    }
    ...
private:
    void *ptr;
};

質問 1.ジェネリックの MSIL が値型の特殊化に変わると、コードはさらに最適化されますか? ValueWrapper<int>たとえば、非 int 型の分岐や型比較自体が最適化されないように、ここで型をチェックすることは可能ですか?

ここで、すべてのメソッドでサポートされているすべての型をリストするのはやや面倒なので、このために別の関数を作成しました。

template< typename action, typename context_type >
__forceinline
static bool apply( System::Type ^type, void *value, System::Object ^*box, context_type context ) {
    if( type == System::Int32::typeid )
        return action().operator()< int >( ( int * )value, context ), true;
    ...branches for some other types...
    // if all else fails:
    return action().operator()< gcroot< System::Object ^ > >( box, context ), false;
}

struct get_value {
    template< typename type, typename result_type >
    void operator () ( result_type *result, void *ptr ) {
        *result = ( ( value_wrapper< type > * )ptr )->value;
    }
};

generic< typename T >
public ref class ValueWrapper {
    ...
    property T Value {
        T get() {
            T result;
            System::Object ^box;
            return apply< get_value >( T::typeid, &result, &box, ptr ) ? result : ( T )box;
        }
        ...
    }
    ...
};

これは、元のコードよりも約 3 倍遅いことがわかりました。

質問 2.オプティマイザーが 2 番目の実装の速度を最初の実装に近づけるために、ここで何を変更できますか (理想的には、速度の差は 10% から 20% 以内)?

PS これは主に VC 2010 に関するものです。ただし、VC 2012 がこの点で何らかの違いがある場合は、それも知っておくとよいでしょう。

4

1 に答える 1

1

いろいろいじくり回し、MSIL に注目した結果、2 番目の質問に対する答えが見つかりました。それ自体typeidではなく、getter 関数を渡すだけです。typeidフレームワークが比較ごとに型情報を要求する方が、変数 (古いtypeパラメーター) に格納して再利用するよりもはるかに簡単なようです。

このアプローチでは、速度低下が 3 倍から 5 ~ 10% 程度 (!) に低下します。

質問 2 終了、質問 1 保留中。

結果のコード:

template< typename action, typename context_type >
__forceinline
static bool apply( System::Type ^type(), void *value, System::Object ^*box, context_type context ) {
    if( type() == System::Int32::typeid )
        return action().operator()< int >( ( int * )value, context ), true;
    if( type() == SupportedStruct::typeid )
        return action().operator()< SupportedStruct >( ( SupportedStruct * )value, context ), true;
    if( type() == System::String::typeid )
        return action().operator()< std::wstring >( ( System::String ^* )value, context ), true;
    // for both reference types and unknown value types:
    return action().operator()< gcroot< System::Object ^ > >( box, context ), false;
}

struct get_value {
    template< typename type, typename result_type >
    void operator () ( result_type *result, void *ptr ) {
        *result = ( ( value_wrapper< type > * )ptr )->value;
    }
    template< typename type >
    void operator () ( System::String ^*result, void *ptr ) {
        *result = gcnew System::String( ( ( value_wrapper< type > * )ptr )->value.c_str() );
    }
};

generic< typename T >
public ref class ValueWrapper {
    ...
public:
    property T Value {
        T get() {
            T result;
            System::Object ^box;
            return apply< get_value >( TypeGetter, &result, &box, ptr ) ? result : ( T )box;
        }
        ...
    }
    ...
private:
    void *ptr;
private:
    static System::Type ^TypeGetter() {
        return T::typeid;
    }
};
于 2012-08-14T11:56:23.277 に答える