Как можно написать устройство Даффа на F #? - PullRequest
3 голосов
/ 19 ноября 2011

Я пишу очень интенсивную по производительности программу и использую C, но кто-то сказал мне, насколько круто функциональное программирование, поэтому я решил переписать его на F #.

В любом случае, особая функция, которую мне сложно воспроизвести в F #, - это устройство Даффа . Вместо типичной итерации он разматывает цикл, поэтому он может копировать 8 байтов за итерацию вместо одного.

void copy_memory( char* to, char* from, size_t count ) {
    size_t n = (count+7)/8;
    switch( count%8 ) {
    case 0: do{ *to++ = *from++;
    case 7:     *to++ = *from++;
    case 6:     *to++ = *from++;
    case 5:     *to++ = *from++;
    case 4:     *to++ = *from++;
    case 3:     *to++ = *from++;
    case 2:     *to++ = *from++;
    case 1:     *to++ = *from++;
            }while(--n>0);
    }
}

Здесь используются преимущества падения регистра и возможность перехода к середине цикла в C, что, насколько я могу судить, к сожалению, является особенностями, которые, по-видимому, отсутствуют в F #.

Я прочитал кое-что на MSDN и подумал, что функция F # match будет наиболее близкой к C switch. Итак, я начал писать этот бит кода

open System.Reflection
let copyMemory (pTo : Pointer) (pFrom : Pointer) length =
    let n = (length + 7) / 8
    match n % 8 with
    | 0 ->

и тогда я не мог понять, что делать. Это не позволило бы мне начать цикл здесь и завершить его в другом случае.

Есть ли что-то в F #, что я могу использовать, чтобы сделать кейс падением и перейти в середину цикла? Если вы можете сделать это для меня, я думаю, что смогу выяснить все остальное.

Ответы [ 2 ]

7 голосов
/ 19 ноября 2011

Вот идиоматический способ возиться с памятью в F #: -)

#nowarn "9" //stop telling me I'm going to break something
open Microsoft.FSharp.NativeInterop

let inline (~+) ptr = NativePtr.add ptr 1

let rec copyMemory src dest = function
  | 0 -> ()
  | n -> 
    NativePtr.read src |> NativePtr.write dest
    copyMemory +src +dest (n - 1)

Но это, вероятно, больше в духе Даффа

let inline (+>) s d = 
  NativePtr.read !s |> NativePtr.write !d
  s:= NativePtr.add !s 1
  d:= NativePtr.add !d 1

let copyMemory src dst count =
  let n = ref ((count + 7) / 8)
  let s, d = ref src, ref dst
  let 
    rec case_0() = s +> d; case_7()
    and case_7() = s +> d; case_6()
    and case_6() = s +> d; case_5()
    and case_5() = s +> d; case_4()
    and case_4() = s +> d; case_3()
    and case_3() = s +> d; case_2()
    and case_2() = s +> d; case_1()
    and case_1() = s +> d; decr n; if !n > 0 then case_0()
  match count % 8 with
  | 7 -> case_7() | 6 -> case_6()
  | 5 -> case_5() | 4 -> case_4()
  | 3 -> case_3() | 2 -> case_2()
  | 1 -> case_1() | _ -> case_0()

А если серьезно

System.Buffer.BlockCopy(src, 0, dest, 0, count)
2 голосов
/ 19 ноября 2011

В F # нет меток. Однако вы можете развернуть петлю другим способом.

let copy (dst : nativeptr<byte>) (src : nativeptr<byte>) count =
    let mutable s = src
    let mutable d = dst

    for n in 1 .. count / 8 do
        NativePtr.read s |> NativePtr.write d
        s <- NativePtr.ofNativeInt(NativePtr.toNativeInt s + nativeint 1)
        d <- NativePtr.ofNativeInt(NativePtr.toNativeInt d + nativeint 1)
        NativePtr.read s |> NativePtr.write d
        s <- NativePtr.ofNativeInt(NativePtr.toNativeInt s + nativeint 1)
        d <- NativePtr.ofNativeInt(NativePtr.toNativeInt d + nativeint 1)
        NativePtr.read s |> NativePtr.write d
        s <- NativePtr.ofNativeInt(NativePtr.toNativeInt s + nativeint 1)
        d <- NativePtr.ofNativeInt(NativePtr.toNativeInt d + nativeint 1)
        NativePtr.read s |> NativePtr.write d
        s <- NativePtr.ofNativeInt(NativePtr.toNativeInt s + nativeint 1)
        d <- NativePtr.ofNativeInt(NativePtr.toNativeInt d + nativeint 1)
        NativePtr.read s |> NativePtr.write d
        s <- NativePtr.ofNativeInt(NativePtr.toNativeInt s + nativeint 1)
        d <- NativePtr.ofNativeInt(NativePtr.toNativeInt d + nativeint 1)
        NativePtr.read s |> NativePtr.write d
        s <- NativePtr.ofNativeInt(NativePtr.toNativeInt s + nativeint 1)
        d <- NativePtr.ofNativeInt(NativePtr.toNativeInt d + nativeint 1)
        NativePtr.read s |> NativePtr.write d
        s <- NativePtr.ofNativeInt(NativePtr.toNativeInt s + nativeint 1)
        d <- NativePtr.ofNativeInt(NativePtr.toNativeInt d + nativeint 1)
        NativePtr.read s |> NativePtr.write d
        s <- NativePtr.ofNativeInt(NativePtr.toNativeInt s + nativeint 1)
        d <- NativePtr.ofNativeInt(NativePtr.toNativeInt d + nativeint 1)

    for n in 1 .. count % 8 do
        NativePtr.read s |> NativePtr.write d
        s <- NativePtr.ofNativeInt(NativePtr.toNativeInt s + nativeint 1)
        d <- NativePtr.ofNativeInt(NativePtr.toNativeInt d + nativeint 1)

В связанной заметке NativePtr.add вызовет sizeof для nativeptr, поэтому я произвел приведение выше к nativeint.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...