2012-05-08 4 views
4

私はstackoverflowに新しいです。私はC#構造体とそのレイアウトに関する質問があります。 構造体へのポインタのFieldOffsetを固定バイト配列と同じ値に設定

はのは、以下の構造体を想定してみましょう:

[StructLayout(LayoutKind.Sequential, Pack = 1)] 
public unsafe struct Link 
{ 
    // some primitive data (2 integers for example) 
} 

[StructLayout(LayoutKind.Explicit, Pack = 1)] 
public unsafe struct Node 
{ 
    [FieldOffset(0)] 
    public int LinkCount; 
    [FieldOffset(4)] 
    public Link* Links; 
    [FieldOffset(4)] 
    private fixed byte _linksData[10 * sizeof(Link)]; 
} 

その理由は、私はIO-パフォーマンスのためのblittable型タイプを必要とするということです。 私は数GBの大きさの非常に大きな(ノード当たり最大10のリンク)グラフに対処しなければなりません。 グラフはノード構造体の配列として表されます。 上記のような設定では、グラフファイルからバイトポインタ(もちろんバイトバッファを指しています)に100 MBを読み込み、それをNode *型のポインタにキャストすることを期待していました非常に良好な性能をもたらす。 最初に私のNode-structには、Link(Link0、...、Link10)型の別々の変数が10個しかありませんでした。しかし、コンパイル時にこれを設定可能にして、上記のNode-structにつなげるとよいでしょう。

私は望んでいたのは、リンクは同じフィールド位置を持っているので、_linksDataと同じメモリ位置を指しているだけです。 しかし、実際にはLinksポインタは常にnullポインタです。

私の質問は: リンクが_linksDataと同じメモリ位置を指す方法はありますか、別の構造体に埋め込まれた構造体の固定サイズの配列を持つ別の方法がありますか?事前にすべての答えを

おかげ - マルクス

はベンフォークトの記事を読んだ後、私はクラスに構造体を変更する必要なく、類似した何かをしようと試みました。以下は、どのように私のために働いています:

[StructLayout(LayoutKind.Explicit, Pack = 1)] 
public unsafe struct Node 
{ 
    [FieldOffset(0)] 
    public int LinkCount; 
    [FieldOffset(4)] 
    private fixed byte _linksData[10 * sizeof(Link)]; 

    public Link* GetLinks() 
    { 
     fixed(byte* pLinksData = _linksData) 
     { 
      return (Link*)pLinksData; 
     } 
    } 
} 
+2

最後のスニペットは深刻な問題を引き起こします。ポインタは固定ブロック内でのみ有効です。あなたが出てくると、返されたポインタは構造体が格納されているどこにでもぶら下がります。そして.NETの中には良いところがありません。良いところはスタックもgcヒープもありません。構造体を非管理メモリに明示的に整列化しない限り。 –

+0

@ハンス:私は、Markusはオブジェクトがなくなった後にポインタを使用しないほどスマートだと思います。これはスタック変数にとって十分です。しかし、クラスインスタンスのメンバーにとって、実際には問題があり、私もこれについて言及しました。 –

+0

@Hans Uhhh - 本当に感謝しました。しかし、私の実際のプロジェクトでは、私はとにかくベンのような彼の例で示された1つのリンクだけを返します。 – Markus

答えて

1

実際にはポインタを格納しようとしていないと思います。ちょうど10個の要素にアクセスするための正しい型付けされた方法があります。どのように:

[StructLayout(LayoutKind.Explicit, Pack = 1)] 
public unsafe struct Node 
{ 
    [FieldOffset(0)] 
    public int LinkCount; 
    [FieldOffset(4)] 
    private fixed byte _linksData[10 * sizeof(Link)]; 

    public Link* Links { get { return _linksData; } }; 
} 

いいえ、.NETは内部ポインタをサポートしていますが、C#はサポートしていませんので、動作しません。固定したりスタックに配置したりすると、.NETオブジェクトへのポインタしか持てません。ここに該当するかどうかはわかりません。

:(

フルにラッパー時間

:私はクラスにNodeを変更しなければならなかった

public class LinkCollection 
{ 
    Node peer; 
    public LinkCollection(Node node) { peer = node; } 
    void CheckIndex(int index) { if (index < 0 || index >= 10) throw new ArgumentOutOfRangeException(); } 
    public Link default[int index] { 
     get { CheckIndex(index); return peer.GetLink(index); } 
     set { CheckIndex(index); peer.SetLink(index, value); } 
    } 
} 

[StructLayout(LayoutKind.Explicit, Pack = 1)] 
public unsafe class Node 
{ 
    [FieldOffset(0)] 
    public int LinkCount; 
    [FieldOffset(4)] 
    private fixed byte _linksData[10 * sizeof(Link)]; 

    unsafe Link GetLink(int index) { fixed(Link* plink = (Link*)&_linksData[0]) return plink[index]; } 
    unsafe void SetLink(int index, Link newvalue) { fixed(Link* plink = (Link*)&linksData[0]) plink[index] = newvalue; } 
    public LinkCollection Links { get { return new LinkCollection(this); } }; 
} 

注... P /呼び出しはまだかかわらず、ほとんど同じに行動しなければならない

これをしたくない場合は、拡張メソッドが答えになる可能性があります。

+0

こんにちは - 答えにも感謝しています(私はどれくらい速くこのことができますか)。このアプローチでは、固定サイズのバッファは、Aloisで述べたようなプリミティブ型でしか動作しないというメッセージを示しています。 – Markus

+0

@Markus:彼が言ったのは、あなたがblitable型が必要なことです。これはこれです。プリミティブ型の構造を除いて、プリミティブ型への制限は狂っています。私は回避策を見ていると思う...実際にはない。 C#では内部ポインタがサポートされていません。 –

+0

あなたのアイデアは実際に私に何かをテストさせました。私は次の分以内に投稿します。私は値の型が必要なので、クラスに変更することはできません。 – Markus

0

あなたはマーシャルblitable型のみです。これにより、IntPtr、string、byte、char int、float、double、decimal、および符号なしのインスタンスに制限されます。興味のある他のデータ構造体へのポインタを定義することはできません。これは現在サポートされていません。

Marhsalerは、最終的なインスタンスがどれくらいのスペースを取るかを知る必要があり、停止する時期を知る必要があります。マネージ型へのポインタを定義するときには、ポインターをマーシャリングする必要があります。これは、マネージ型になっているので、常にコピーバックされるためです。これらの問題はある程度解決できますが、私が知る限り、これは現在サポートされていません。おそらく.NET 4.5はいくつかの新しい機能をもたらします。

EDIT1:

は、あなたがのIntPtrとしてポインタを残して、単に自分で重い物を持ち上げる(マーシャリング)を行うために拡張メソッドを使用することができ、あなたの構造から何かを得るために。元のリンクが固定されているかどうかはわかりません。これは、リンクが移動不能(管理されていないデータまたは固定された管理オブジェクトのいずれか)ではないことを前提としています。

using System; 
using System.Collections.Generic; 
using System.Runtime.InteropServices; 


unsafe class Program 
{ 
    static void Main(string[] args) 
    { 
     Link[] arr = new Link[] { 
      new Link(1), 
      new Link(2), 
      new Link(3), 
      new Link(4), 
     }; 

     fixed (Link* pLinks = arr) // for demo purposes create a node instance 
     { 
      var nde = new Node 
      { 
       LinkCount = arr.Length, 
       Links = new IntPtr(pLinks) // Marshal as IntPtr is safe, later the data can be retrieved via an Extension method. 
      }; 

      foreach (var link in nde.GetLinks()) 
      { 
       Console.WriteLine("Link {0}", link.I); 
      } 
     }; 
    } 
} 

[StructLayout(LayoutKind.Sequential, Pack = 1)] 
public unsafe struct Link 
{ 
    // some primitive data (2 integers for example) 
    public int I; 

    public Link(int i) 
    { 
     I = i; 
    } 
} 

[StructLayout(LayoutKind.Explicit, Pack = 1)] 
public unsafe struct Node 
{ 
    [FieldOffset(0)] 
    public int LinkCount; 
    [FieldOffset(4)] 
    public IntPtr Links; // this assumes that the Links is some unsafe buffer which is not under GC control or it is pinned 
} 


static class LinkExtensions 
{ 
    public static IEnumerable<Link> GetLinks(this Node node) 
    { 
     for (int i = 0; i < node.LinkCount; i++) // very efficient if you want to traverse millions of nodes without marshalling all of them at once 
     { 
      // alternatively you can also use a memcpy (PInvoke to msvcrt.dll) to fill in the data from a given offset. 
      // it is up to you to decide which is faster 
      yield return (Link)Marshal.PtrToStructure(node.Links + IntPtr.Size * i, typeof(Link)); 
     } 
    } 
} 
+0

こんにちは、(本当に速い)答えに感謝します。しかし実際には、NodesArrayがNode []型の固定ポインタ(Node * pNodes = NodesArray)のようなポインタを既に定義しています。このポインタをネイティブのFileWriteメソッドで使用されたバイトポインタにキャストしました。それとも、まったく違うのですか? – Markus

関連する問題