2016-10-20 16 views
0

我々缶pnativeptr<'a>あるのでSystem.Numerics.Vectorにnativeptr <T>から読み出し<T>チャンクF#の

open FSharp.NativeInterop 
let x = NativePtr.read p 

ようなタイプ'aの値を指し示すポインタ間接参照。

このポインタが'a値の配列を指しており、System.Numerics.Vector<_>でSIMDを使用してこの配列を処理したいとします。そのためにはnの連続した'aの値をVector<'a>構造体にロードする必要があります。 .NET /マネージド配列の場合、これは適切なVector<_>コンストラクタを使用することで実現できます。しかし残念ながら、ポインタ(実際には、この特定のケースではアンマネージヒープを指しています)が存在するため、既存のコンストラクタオーバーロードのいずれかを使用することはできません。

pnativeptr<Vector<'a>>と再解釈するのはどうですか? Vector<_>as a generic data typeは、ない - nativeptrの型パラメータがunmanaged制約を満たすために持っているので、

let inline cast<'T, 'U when 'U : unmanaged and 'T: unmanaged> (ptr: nativeptr<'T>) = 
    ptr |> NativePtr.toNativeInt |> NativePtr.ofNativeInt<'U> 

let v = p |> NativePtr.cast<'a, Vector<'a>> |> NativePtr.read 

悲しいことに、これは、worktしません。

unmanagedの制約がC#コンパイラによって強制されないため、この問題を回避する1つの方法は、NativeInteropと組み合わせてC#を使用することです。しかし、私は本当にF#に泊まりたいです。

F#から効率的に働くことができますか?または、Vector<_>がポインターからのロードをサポートするコンストラクターで拡張されるのを待つ唯一のオプションはありますか?

+1

C#コンパイラを使用しても、生のメモリを特定の種類のオブジェクトとして「再解釈」することはできません。これはC言語ではありません。 CLR型は単にコンパイル時の構造だけでなく、ランタイム表現も持っています。その「管理されていない」制約が理由のためにあります。 –

+2

[もちろん私は](https://gist.github.com/FrankNiemeyer/38f376e78fdc53cf3246deb7822b959a)(これまで説明したように、C#コンパイラは "let me"を実行しますが、F#compは実行しません)。 'Vector 'はblittable( 'T'はプリミティブ型だけがサポートされているので常に管理されていません)であり、メモリ上で128(SSE)または256ビット(AVX)のSIMDストライドにまで下がります。それ。 – Frank

+1

あなたのプログラムはあなたが思うとは思わないと思います。その 'IntPtr.Read'拡張メソッドは、メモリを' Vector 'として「再解釈」しません。実際には、いくつかのバイトを新しい場所にコピーします。 –

答えて

-1

F#自体にこの問題を回避する方法はありません。我々は内からnativeptr<'T>Vector<'T>としてデリファレンス今、

using NativeInteropEx; 
using System.Numerics; 
using System.Runtime.CompilerServices; 

public struct VectorView<T> where T: struct 
{ 
    [MethodImpl(MethodImplOptions.AggressiveInlining)] 
    public static Vector<T> Get(IntPtr p, int idx) { 
     return p.Get<Vector<T>>(idx); 
    } 

    [MethodImpl(MethodImplOptions.AggressiveInlining)] 
    public static Vector<T> Get(IntPtr p, long idx) { 
     return p.Get<Vector<T>>(idx); 
    } 

    [MethodImpl(MethodImplOptions.AggressiveInlining)] 
    public static void Set(IntPtr p, int idx, Vector<T> value) { 
     p.Set(idx, value); 
    } 

    [MethodImpl(MethodImplOptions.AggressiveInlining)] 
    public static void Set(IntPtr p, long idx, Vector<T> value) { 
     p.Set(idx, value); 
    } 

    [MethodImpl(MethodImplOptions.AggressiveInlining)] 
    public static Vector<T> Read(IntPtr p) { 
     return p.Read<Vector<T>>(); 
    } 

    [MethodImpl(MethodImplOptions.AggressiveInlining)] 
    public static void Write(IntPtr p, Vector<T> value) { 
     p.Write(value); 
    } 
} 

この中間層を使用することができます。したがって、私は基本的に単純に転送NativeInteropEx.NativePtrに、しかし、C#コンパイラに未知のunmanaged制約を取り除きC#で少しラッパーを実装しましたF#:

# baseAddress: nativeptr<'T> when 'T: unmanaged 
# idx: int64 
let v = VectorView<'T>.Get(baseAddress, idx) 
# v: Vector<'T> of Vector<'T>.Count 'T values starting with baseAddress + sizeof<'T> * idx 
+0

潜在的なユーザを明確にする:これは安全ではない(プライベート)['System.Numerics.Vector 'コンストラクタ](https://github.com/dotnet/corefx/blob/master/src/System)とまったく同じことです。 .Numerics.Vectors/src/System/Numerics/Vector.cs#L619) 'void *'とオフセットから 'Vector 'を作成します。違いは、汎用のポインタ操作に 'NativeInterop'を使うことができるので、明示的な型切り替えは必要ないということだけです。 – Frank

関連する問題