2011-09-04 19 views
0

ベンチマークを行うために、memcpyを自分自身の最適化バージョンに置き換えたいと思います。 私はmemcpyを呼び出すコードの各場所を変更したくないです(これは大きなコードベースであり、多くの変更を避けたい)。それでは、私がやったことは以下の通りです:memcpyを安全な方法で置き換える

// in a "common" header file which is included everywhere 
#ifdef SHOULD_OPTIMIZE 
    #define memcpy my_on_steroids_memcpy 
#endif 

上記の作品と私の独自の実装でのmemcpyに置き換えられますが、それは、原油、強制とは全く安全ではないようです。それは私がライブラリのmemcpyを残りのコードを変更することなく置き換えることができるように他の代替手段ですか?私は上記のことを忘れるべきであり、他のすべてのファイルを(そして、なぜ)変更するのが助言できるようには見えないのですか?私はLinuxを実行していることを前提とするつもりだ

+7

Linuxではあなたは[LD_PRELOAD](http://stackoverflow.com/questions/426230/what-is-the-ld-preload-trick)を使用することができます。好奇心のために、最適化された 'memcpy'は何をしますか? –

+0

あなたはより良いmemcpyをしましたか?うわー!もしそれが 'memcpy'と同等(同じプロトタイプ、同じ振る舞い)であれば、私は' #define'を使う上で何の問題も見ません。MSは 'malloc'と' free'のデバッグバージョンでも同じことをします – xanatos

+2

@Kerrebしかし、しばしば 'memcpy'はインライン展開されます。彼は関数のインライン展開を無効にする必要があります。 – xanatos

答えて

1

memcpy関数呼び出しを置き換える別の方法が見つかりました。それはGCCでしか動作しません(私はまだVC++のために別の方法を見つける必要があります)が、それは原油よりもはるかに良いと思います#define。それは__REDIRECTマクロ(はfeatures.hに含まれています)を使用しています。これはglibcで広く使用されています。以下は、小さなテストでの例を次に示します。

// modified.h 
#pragma once 

#ifndef MODIF_H_INCLUDED_ 
#define MODIF_H_INCLUDED_ 

#include <cstddef> 
#include <features.h> 

extern "C" 
{ 
void test_memcpy(void* __restrict to, const void* __restrict from, size_t size); 
} 

#if defined(__GNUC__) 
void __REDIRECT(memcpy, (void* __restrict to, const void* __restrict from, size_t size), 
       test_memcpy); 
#endif /* __GNUC__ */ 

#endif /* MODIF_H_INCLUDED_ */ 

//modified.cpp 
extern "C" void test_memcpy(void* __restrict to, const void* __restrict from, 
          size_t size) 
{ 
    std::cout << "Dumb memcpy replacement!\n"; 
} 

//original.h 
#pragma once 

#ifndef ORIG_H_INCLUDED_ 
#define ORIG_H_INCLUDED_ 

void test_with_orig(); 

#endif /* ORIG_H_INCLUDED_ */ 

//original.cpp 
#include <cstring> 
#include <iostream> 

void test_with_orig() 
{ 
    int* testDest = new int[10]; 
    int* testSrc = new int[10]; 

    for (unsigned int i = 0; i < 10; ++i) 
    { 
      testSrc[i] = i; 
    } 

    memcpy(testDest, testSrc, 10 * sizeof(int)); 

    for (unsigned int i = 0; i < 10; ++i) 
    { 
      std::cout << std::hex << "\nAfter standard memcpy - " 
      << "Source: " << testSrc[i] << "\tDest: " << testDest[i] << "\n"; 
    } 
} 

// and a small test 
#include "modified.h" 
#include "original.h" 

#include <iostream> 
#include <cstring> 

int main() 
{ 
    int* testDest = new int[10]; 
    int* testSrc = new int[10]; 

    for (unsigned int i = 0; i < 10; ++i) 
    { 
      testSrc[i] = i; 
      testDest[i] = 0xDEADBEEF; 
    } 

    memcpy(testDest, testSrc, 10 * sizeof(int)); 

    for (unsigned int i = 0; i < 10; ++i) 
    { 
      std::cout << std::hex << "\nAfter memcpy replacement - " 
      << "Source: " << testSrc[i] << "\tDest: " << testDest[i] << "\n"; 
    } 

    test_with_orig(); 

    return 0; 
} 
1

...

添付のリンクは、アプリケーション内の既存の機能を置き換えるためにLD_PRELOADを使用する方法の例です。この例では、通常のmalloc呼び出しを行い、メモリがゼロになったことを確認します。これをmemcpyにどのように翻訳するかはかなり明白です。

https://gist.github.com/701897

+1

これは動作しません'memcpy'は非常によくインライン化されているためです。 –

+0

@Dietrich:はい。そのとおりです。 '-O0'で完了しない限り、介在コードは呼び出されませんでした。 –

0

既存のコードを変更したくない場合は、これが唯一の解決策であると思われます。あなた自身のmemcpyの署名がデフォルトのものと一致しない場合、コンパイラは文句を言うので、あまりにも悪くはありません。

私は、標準ライブラリに付属しているmemcpyよりもかなり優れたパフォーマンスを絞ることができるとはっきりと疑うでしょう。しかし、あなたはそれがまったく問題になるくらい多くの記憶をコピーしていますか?

+0

私は極端な量をコピーしていませんが、私は非常に頻繁にそれをやっています。 – celavek

2

一部のコンパイラには、コマンドラインからヘッダーを含める方法があります。たとえば、g++gccは、-includeオプションで呼び出すことができます。

しかし、あなたのコードが "謎の"コンパイラフラグなしで失敗する "悪いマナー"と考えられるので、あなたのコードが少なくともカスタムヘッダなしでコンパイルされ実行されることを確信しています。

また、通常memcpyの標準ライブラリ実装はすでにであり、SSE2の最適化などで最適化されているのはです。あなたはおそらくもっとうまく行かないでしょう。

+0

+1の 'include' – xanatos

+0

本当にSSEを使用します。ただし、コンパイラがアラインメントを静的に判断できない場合は、SSEコードパスを使用する前に実際にアライメントされているかどうかを確認するためにブランチを挿入する必要があります。あるいは、アライメントされていないSSEを使用することもできますが、処理速度はかなり遅くなりがちです。 – Mysticial

+0

ええ、あなたがカスタムmemcpyを呼び出すたびにたくさんの余分な作業をすることなくそれを改善することができるかどうかはわかりませんが、標準のmemcpyを代わりに使うことができます。例えば#define malloc(x)memalign(sizeof(size_t)、x)または何か。 –

1

もしあなたがLinuxを使っているなら、memcpyはすでに非常に最適化されているでしょう。おそらくあまりにも多すぎます(私はmemcpyを使ってページ境界にクラッシュすることに気がついたと思います)。

つまり、あなたのプログラムに交換memcpyを完全に定義することができます。これはCライブラリの代わりに呼び出されます。それ以外は何もする必要はありません。

関連する問題