C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[4]: ユーザ定義構造体配列のマーシャリング


(過去ログ 16 を表示中)

[トピック内 5 記事 (1 - 5 表示)]  << 0 >>

■5767 / inTopicNo.1)  ユーザ定義構造体配列のマーシャリング
  
□投稿者/ BBlue (3回)-(2007/07/23(Mon) 13:01:28)

分類:[C#] 

ユーザ定義構造体配列をアンマネージドメモリへ転送、
また、アンマネージドメモリから構造体配列へ復元する方法は、
Win32APIのCopyMemoryを使用する以外ないのでしょうか。
API呼び出しがべたでしっくりきません。他によい方法はないでしょうか?
ビデオカードへ頂点バッファをコピーする処理なため、パフォーマンスを優先させたいです。

ユーザ定義構造体をアンマネージドメモリへコピーするには、Marshal.StructureToPtr()。
アンマネージドメモリからマネージド構造体へコピーするには、Marshal.PtrToStructure()で
できることがわかりました。
それとも、構造体をひとつひとつ、ループしてコピーするのが常套手段なのでしょうか。

namespace TestStruct {
[Serializable]
[StructLayout(LayoutKind.Explicit)]
public unsafe struct Vector2 {
// フィールド
[FieldOffset(0)]
private fixed float pData[2];
[FieldOffset(0 * sizeof(float))] public float X;
[FieldOffset(1 * sizeof(float))] public float Y;
// コンストラクタ
public Vector2(float localx, float localy) {
this.X = localx;
this.Y = localy;
}
}
}
//////////////////////////
namespace TestStruct {
public unsafe class Program {
static void Main(string[] args) {
System.Diagnostics.Trace.WriteLine("--- Start ---");
// 構造体配列
try {
Vector2[] inArray = new Vector2[2];
inArray[0].X = 11.0f;
inArray[0].Y = 12.0f;
inArray[1].X = 21.0f;
inArray[1].Y = 22.0f;
foreach(Vector2 vector in inArray) {
System.Diagnostics.Trace.WriteLine(vector.ToString());
}

ptr = Marshal.AllocHGlobal(Marshal.SizeOf(inArray[0]) * inArray.Length);
fixed(void* pVoid = &inArray[0]) {
Program.CopyMemory(
ptr.ToPointer(), pVoid, Marshal.SizeOf(inArray[0]) * inArray.Length);
}

Vector2[] outArray = new Vector2[inArray.Length];
fixed(void* pVoid = &outArray[0]) {
Program.CopyMemory(
pVoid, ptr.ToPointer(), Marshal.SizeOf(inArray[0]) * inArray.Length);
}
foreach(Vector2 vector in outArray) {
System.Diagnostics.Trace.WriteLine(vector.ToString());
}
}
finally {
if(ptr != IntPtr.Zero) {
Marshal.FreeHGlobal(ptr);
ptr = IntPtr.Zero;
}
}
System.Diagnostics.Trace.WriteLine("--- E n d ---");
}

[DllImport("kernel32.dll", EntryPoint = "CopyMemory")]
public static extern void CopyMemory(
[Out] void* data,
[In] void* value,
int size);
}
}

引用返信 編集キー/
■5779 / inTopicNo.2)  Re[1]: ユーザ定義構造体配列のマーシャリング
□投稿者/ mあ (30回)-(2007/07/23(Mon) 15:47:14)
No5767 (BBlue さん) に返信
> ユーザ定義構造体配列をアンマネージドメモリへ転送、
> また、アンマネージドメモリから構造体配列へ復元する方法は、
> Win32APIのCopyMemoryを使用する以外ないのでしょうか。
> API呼び出しがべたでしっくりきません。他によい方法はないでしょうか?
> ビデオカードへ頂点バッファをコピーする処理なため、パフォーマンスを優先させたいです。

ベタベタなのは変わりませんが、自分でコンパイルする分だけ速度改善される
と考えます。
下の方の参考資料に、最適化が掛かると普通に MOVSD で展開される、とあるので
工夫すれば、C++オンリーでアセンブラと遜色ないコードが書けるかと思います。
ライブラリの CopyMemory は、汎用ですので、入力が何なのか分からないので
バイト単位のコピー(MOVSB) ですが。

public void extern MyCopyMemory(DWORD *srcEAX, DWORD *dstEBX, int size4nECX);
size4nECX = (sizeof(structureX) * N / sizeof(int))
if (size4nECX & 3) size4nECX++;
です。4n整合

push ESI,EDI,DS,ES
MOV ESI, srcEAX ;16 バイト整合しておくと速いかも
MOV EDI, dstEBX ;おなじく
mov ES, DS
;;mov ECX, ECX
REP MOVSD
pop ES,DS,EDI,ESI

大昔のまんまなら、EAX,EBX,ECX,EDX,ESI,EDI は汎用なので保存の必要無し
ただ、ESI,EDI ともワード整合以下だと、ペナルティが発生するので少し遅
くなります。大昔、80486 ハンドブックで見た記憶があります。

MOVSB/W/D は、命令1回で、DI=SI & SI+=4,DI=+=4 を行います(MOVSD の時)。
REP は、CX を デクリメントして 0 では無い間ループしなさい、って意味の
命令接頭語です。


スタック経由なら、
[ret addr]
push ebp
mov ebp, esp
pushf
mov esi, 4[ebp]
mov edi, 8[ebp]
mov ecx, 12[ebp]
cld
rep movsd
popf
pop ebp


マネージド・アンマネージドコードで構造体の持ち方が同じな場合に限ります。

でも、CopyMemory も、REPZ MOVSB だから、4倍速度が違うだけですからね。
末端処理や境界整合の問題があるので、4バイト一括転送が使われるケースは
稀なんじゃないかな。

C++で組んで、C# からインポートして使う、という感じになります。
src , dst 共、境界整合用に若干余分なバイト数を(3バイト以下)転送して
しまう可能性があるので、それらを考慮するなら、構造体はきっちり4n で
割り切れる(パディング含めて)大きさにするか、1つ余分に取っておくと
良いかもしれませんね。最悪実行中に未割り当て領域侵害でドカーンって
いっちゃうかもしれません。


http://www5c.biglobe.ne.jp/~ecb/assembler/7_2.html
STRING 命令云々

http://www.cyborg.ne.jp/~xelf/developer/MemoryCopy.html
MMX 命令が最速?MMXはわかりません。m(__)m

http://hp.vector.co.jp/authors/VA014520/asmhsp/chap7a.html

引用返信 編集キー/
■5781 / inTopicNo.3)  Re[2]: ユーザ定義構造体配列のマーシャリング
□投稿者/ Hongliang (157回)-(2007/07/23(Mon) 16:27:51)
Hongliang さんの Web サイト
// 低級な方向では書かれているので、高級な方向で。

私の知る限り、ポインタ(IntPtr)・基本値型配列間のコピーが限界ですね。
ユーザ定義型からだと一旦 float[] にコピーしてからになるかな。
// 基本値型のみを含む構造体配列・基本値型配列間なら Buffer.BlockCopy を使えます。

要素数が固定的なら、そういう構造体を定義して
Marshal.PtrToStructure/StructureToPtrって手もありますが。
struct Vector2x2 {
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=2)]
    public Vector2[] Vectors;
}
こんなの。
まあ、CopyMemory を呼び出すほうが素直だと思います。

ところで、Vector2 をわざわざ Explicit で定義する必要がなさそうですが。
普通に Sequential で float X と float Y を並べるだけで十分では?
CopyMemory の引数も Vector2[] と IntPtr を渡すオーバーロードにしたほうが便利そう。

引用返信 編集キー/
■5824 / inTopicNo.4)  Re[3]: ユーザ定義構造体配列のマーシャリング
□投稿者/ BBlue (4回)-(2007/07/24(Tue) 12:55:21)
アドバイスありがとうございます。
他にもいろいろな方法があることが分かりました。
し、CopyMemoryもありなこと分かりました。
マシン語で実装することまでは思いつきませんでした。(私にはちょっと敷居がたかいですが)
ユーザ定義構造体は、Explicitで定義する必要は、たしかにないですね。訂正しました。
また質問させていただくかもしれませんが、よろしくお願いします。

namespace TestStruct {
[Serializable]
[Sequential]
public unsafe struct Vector2 {
// フィールド
public float X;
public float Y;
// コンストラクタ
public Vector2(float localx, float localy) {
this.X = localx;
this.Y = localy;
}
public override string ToString() {
return "HogeHoge";
}
}
}

引用返信 編集キー/
■5855 / inTopicNo.5)  Re[4]: ユーザ定義構造体配列のマーシャリング
□投稿者/ BBlue (5回)-(2007/07/24(Tue) 22:31:46)
No5824 (BBlue さん) に返信
> アドバイスありがとうございます。
> 他にもいろいろな方法があることが分かりました。
> し、CopyMemoryもありなこと分かりました。
> マシン語で実装することまでは思いつきませんでした。(私にはちょっと敷居がたかいですが)
> ユーザ定義構造体は、Explicitで定義する必要は、たしかにないですね。訂正しました。
> また質問させていただくかもしれませんが、よろしくお願いします。
>
> namespace TestStruct {
> [Serializable]
> [Sequential]
> public unsafe struct Vector2 {
> // フィールド
> public float X;
> public float Y;
> // コンストラクタ
> public Vector2(float localx, float localy) {
> this.X = localx;
> this.Y = localy;
> }
> public override string ToString() {
> return "HogeHoge";
> }
> }
> }
>
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -