13

私がこれを行う場合:-

class Thing  
{
    ...
    void function (const std::string& message);
};

std::list<std::function<void()>> work;

そして「モノ」の一部のメンバーで

work.push_back(std::bind(&Thing::function, this, "Hello"));

std::bindの呼び出しまたはstd::function <>の使用のいずれかにより、newまたはその他を使用した動的メモリ割り当てが発生しますか?または、コンパイル時にすべてのストレージが割り当てられていますか?標準で何も言われていない場合、私のプログラムはそこに構築するだけでよいので、Visual Studio 2012ではどうでしょうか。効率を上げるには、このメカニズムの使用を考えている場所での動的なメモリ割り当てを避ける必要があります。

4

3 に答える 3

17

std::function標準では指定されていませんが、一般に、少なくとも場合によってはメモリを割り当てる必要があることが簡単にわかります。

struct huge { char c[10000]; };
void foo(const huge &);
std::function<void()>{std::bind(foo, huge{})};

一方、functionオブジェクトのフットプリント内の事前に割り当てられたバッファ内に関数オブジェクトを配置することにより、少なくとも一部のケースでは割り当てを回避することができます。他の用途ではより多くのスタックメモリを使用する可能性があるため、明らかにトレードオフがあります。function優れた実装では、raw関数ポインタをオブジェクトに格納するときにメモリ割り当てを回避できます。また、場合によってはmem_fn、も回避できますが、。に対してそうする可能性は低くなりbindます。

たとえば、libstdc ++(g ++)インライン(ファンクター)オブジェクトポインター、関数ポインター、(非仮想)メンバー関数ポインター、および同じフットプリントに収まるその他のもの、たとえばステートレスファンクター(union _Nocopy_types)。

可能であれば、制御フローを逆にして、代わりにテンプレート化されたファンクターオブジェクトを受け入れることでfunction、余分なメモリ割り当てを回避できます。

template<typename F>
void my_algorithm(const F &);
my_algorithm(std::bind(foo, huge{}));
于 2012-11-09T11:21:27.820 に答える
6

g ++の場合について、これについて調査しました。

std :: functionと動的メモリ割り当てに関しては、2つの重要なポイントがあります。

  1. std :: functionは任意のサイズのオブジェクトを格納できます。つまり、場合によっては動的メモリ割り当てを実行する必要があります。
  2. std::functionが例外をスローしないことが保証されている特定のタイプがあります。これは、動的メモリ割り当てなしで保存する必要がある特定のタイプがあることを意味します。

gccslibstd+++でのstd::functionの実装は、動的メモリ割り当てなしで、格納する必要のあるもののサイズ/アライメント要件以下のサイズ/アライメント要件を持つ他のものを格納します。

動的メモリ割り当てなしで格納する必要がある最大のものは、メンバー関数へのポインタです。「itaniumc++ABI」*に基づくコンパイラでは、これは通常のポインタの2倍のサイズです。したがって、動的メモリ割り当てをトリガーせずに、最大2つのポインタのサイズをg++のstd::functionに格納できます。

std :: bindは、ものを1つのオブジェクトに連結するだけなので、メンバー関数に何かをバインドすると、少なくとも3つのポインターのサイズのオブジェクトになります。このオブジェクトをstd::functionに割り当てると、動的メモリ割り当てが行われます。

より良いオプションは、ラムダを使用することです。これは、メンバー関数を静的に参照し、動的メモリ割り当てをトリガーせずに最大2つのポインターをキャプチャするためのスペースを提供します。

実証するために、私はあなたのテストコードに大まかに基づいていくつかのテストコードを書きました。文字列とリストを削除し、代わりにconst char *(std :: string関連のメモリ割り当てを回避するため)と新しい配置(このコードはビルドのみを目的としており、実行することを目的としていません)を使用してgodboltにフィードしました。

#include <functional>
using namespace std;

class Thing  
{
    void foo();
    void bar();
    void function (const char * message);
};

char baz[1024];

void Thing::foo() {
    new (baz) std::function<void()>(std::bind(&Thing::function, this, "Hello"));
}


void Thing::bar() {
    const char * s = "Hello";
    new (baz) std::function<void()>([this,s](){function(s);});
}

結果はでした。

Thing::foo():
        mov     r3, #0
        push    {r4, r5, r6, lr}
        ldr     r4, .L34
        mov     r6, r0
        sub     sp, sp, #16
        mov     r0, #16
        str     r3, [r4, #8]
        bl      operator new(unsigned int)
        ldr     r2, .L34+4
        mov     r1, #0
        mov     r3, r0
        str     r2, [sp]
        mov     r2, sp
        ldr     r5, .L34+8
        ldr     lr, .L34+12
        ldr     ip, .L34+16
        str     r1, [sp, #4]
        str     r6, [r0, #12]
        str     r0, [r4]
        str     r5, [r3, #8]
        ldm     r2, {r0, r1}
        str     lr, [r4, #12]
        stm     r3, {r0, r1}
        str     ip, [r4, #8]
        add     sp, sp, #16
        pop     {r4, r5, r6, pc}
        ldr     r3, [r4, #8]
        cmp     r3, #0
        beq     .L27
        ldr     r1, .L34
        mov     r2, #3
        mov     r0, r1
        blx     r3
.L27:
        bl      __cxa_end_cleanup
.L34:
        .word   .LANCHOR1
        .word   Thing::function(char const*)
        .word   .LC0
        .word   std::_Function_handler<void (), std::_Bind<void (Thing::*(Thing*,     char const*))(char const*)> >::_M_invoke(std::_Any_data const&)
        .word   std::_Function_base::_Base_manager<std::_Bind<void (Thing::*(Thing*, char const*))(char const*)> >::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
Thing::bar():
            ldr     r2, .L38
            sub     sp, sp, #8
            stm     sp, {r0, r2}
            add     r2, sp, #8
            ldr     r3, .L38+4
            ldmdb   r2, {r0, r1}
            ldr     ip, .L38+8
            ldr     r2, .L38+12
            stm     r3, {r0, r1}
            str     ip, [r3, #12]
            str     r2, [r3, #8]
            add     sp, sp, #8
            bx      lr
    .L38:
            .word   .LC0
            .word   .LANCHOR1
            .word   std::_Function_handler<void (), Thing::bar()::{lambda()#1}>::_M_invoke(std::_Any_data const&)
            .word   std::_Function_base::_Base_manager<Thing::bar()::{lambda()#1}>::_M_manager(std::_Any_data&, std::_Function_base::_Base_manager<Thing::bar()::{lambda()#1}> const&, std::_Manager_operation)

バインドの場合にはメモリ割り当てがあることがはっきりとわかりますが、ラムダの場合にはありません。

*この名前にもかかわらず、多くの異なるアーキテクチャでg++とclang++によって使用されています。

于 2019-11-18T14:22:07.000 に答える
0

これについてはよくわかりません。ecatmurが示唆しているように、それはそのプラットフォームのstdの実装に依存していると思います。同様の問題については、コードプロジェクトからのこの実装を使用して成功しました。かなりの数のプラットフォームをサポートしています。非常によく文書化されており、動的メモリ割り当てはありません。

http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible

ゲームまたはシミュレーションでの実行時の汎用動的メモリ割り当ては避ける必要があります。問題は、常に断片化や大きなボトルネック(どちらも避けるべき正当な理由)ではなく、時間の長さが決定論的でないことが多いという事実でもあります。ここでは、「プーリング」や「フレーミング」など、よりドメイン固有のメモリ割り当て戦略が有利です。

http://g.oswego.edu/dl/html/malloc.html

于 2013-02-05T14:12:34.470 に答える