2016-11-10 17 views
1

リモートdBに接続して複数のクエリを実行するコンソールアプリケーションがあります。メモリ使用量を向上させるためのストリームラインクエリ:OutOfMemory例外

私は64ビットのF#を使ってアプリケーションを構築しています。私はこのパスとしてこれを確信しています。

C:\Program Files (x86)\Microsoft SDKs\F#\4.0\Framework\v4.0\fsc.exe 

は、ビルドプロセス中にF#フレームワークパスとして使用されます。また、上記のファイル・パスは、私のPATH環境変数

である各クエリは、これらのクエリ結果をアプリに下流で使用されているよう

query{expression} 
|> Seq.toArray 
|> Array.map (fun q -> {a = q.a; 
         b = q.b; 
         etc...} 
|> writeToJson ("filePath") 

として、私はJSONファイルに書き込む構成されています。

私は App.configファイルに

<runtime> 
<gcAllowVeryLargeObjects enabled="true" /> 
</runtime> 

が含まれているように私は、クエリでArray Sを使用

This pageこの

を含むことを示唆している...合計サイズが2ギガバイト(GB)よりも大きいアレイが可能となります。

残念ながら、いくつかの2番目のクエリでは、OutOfMemoryの例外が発生します。

クエリ

query { 
     for header in db.CustomerDetails do 
     leftOuterJoin row in db.MDR_0916 
      on (header.PID = row.PID) into result 
     for row in result do 
     select ([row.APSci; 
        row.Charter; 
        row.VirtualSchool; 
        row.AffIndicator], header) 
} 
|> Seq.toArray 
|> Array.map (fun (row, header) -> {CustId = Option.ofNullable header.CBSCUSTOMERID; 
            IDType = Option.ofObj header.IDTYPE; 
            Name = Option.ofObj header.CUSTOMERNAME; 
            State = Option.ofObj header.STATE; 
            AcctStatus = Option.ofObj header.ACCOUNTSTATUS; 
            Contract = Option.ofObj header.CONTRACT; 
            ContractDesc = Option.ofObj header.CONTRACTDESCRIPTION; 
            AcctMgr = Option.ofObj header.ACCOUNTMANAGER; 
            InstType = Option.ofObj header.CUSTOMERNAME; 
            InstDesc = Option.ofObj header.INSTITUTIONTYPEDECRIPTION; 
            MDR = Some {HasApSci = (row |> List.item 0); 
               IsCharter = (row |> List.item 1); 
               IsVirtualSchool = (row |> List.item 2); 
               AffRating = (row |> List.item 3)}}) 
|> writeToJson (Path.Combine(recEngTresorFolder, "rawCustomers.json")) 

戻っ〜250万件の結果。クエリの結果をパイプしているsizeof<RawCustomer>8です。

どのようにOOM例外が発生するのを防ぐことができるか知っていますか? gcAllowVeryLargeObjectsの間にF#64ビットを使用すると、それが処理されると思いました。

+1

私はアレイの使用を避けることを試みます。セティは怠惰です。 –

+0

'Seq'としてすべてを保持しても、クエリやOOM例外の動作は変わりません。 – Steven

+0

btw、私は64ビットがどれくらいのスペースに対処できるのか正確にはわかりませんが、TBの領域です。プロセスが32ビットの場合は、3GB程度の問題が発生する可能性があります。多分あなたは記憶を漏らしていますか? – s952163

答えて

1

基本的には、アプリケーションをデバッグする必要があります。データベースと環境にアクセスすることなく、アプリケーションを実行する必要があります。

fscを呼び出しているだけで、64ビットであるとは限りません(通常はそうですが)。コンパイラのさまざまなフラグは、通常はVisual Studioを介して.fsprojファイルに設定されますが、もちろん手動で編集することもできます。

<Prefer32Bit>true</Prefer32Bit> か:: <PlatformTarget>x64</PlatformTarget> <DocumentationFile>bin\Release\ConsoleApplication8.XML</DocumentationFile> <Prefer32Bit>false</Prefer32Bit>

あなたは64ビットであるFSIAnyCpu.exe、中にテストを開始することができ、私は32ビットのプロファイルを作成することができますたとえば

enter image description here

FSIのバージョン(VSCodeのデフォルトで、VS2015で設定する必要があります)。

  1. 次に、SQLで直接サーバー上でクエリを実行できることを確認する必要があります。

  2. 次のステップとして、データベースにアクセスするためにタイププロバイダを使用していると仮定しています。接続がクリーンアップされていることを確認してください。たとえば、クエリごとに別のdatacontextを使用し、useとバインドして、それを廃棄してGCをクリーンアップします。特定のデータベースでは、オブジェクトの追跡を無効にすることができます.DBから読み込んでいるので、これは問題ではありません。コメントで述べた

  3. として、あなたはJSONに変換するとき、それは窒息しないように最後に、StreamWriterのいくつかの並べ替えを使用しようとSeq

  4. を使用して、結果を明示する遅らせるようにしてみてください。

しかし、最終的に、あなたは慎重にテストすることにより、メモリ例外のうちの原因となるコードの一部を分離する必要があります(..いくつかの睡眠とプリント文を追加読み取り)ともちろんタスクマネージャでメモリを見てパフォーマンスプロファイラはVS2015またはRedgateの.NETプロファイラのようなものを使用する方がよいでしょう。

初心者として、クエリ(またはクエリの1つ)またはJsonエクスポータで例外の原因を見つける必要があります。

は1を追加します。

この小さなスニペットはあなたに何の問題を8GBの配列を与えないだろう。だからあなたはそれを使ってテストすることができます。ディメンションあたり約20億の要素で、配列の長さにはまだ制限があります。

open System 

[<EntryPoint>] 
let main argv = 

    let x = Int32.MaxValue /2 
    printfn "%A" x 
    let big = Array.init x (fun _ -> "aa") 
    Console.ReadLine() |> ignore 
    printfn "%A" big.Length 

    0 
+1

最終的には、私は最も簡単な解決策から始めました。まず、writeToJson関数を、すべてを一度に書くのではなく、ストリームに書き込むように変更しました。すべてのクエリを実行するプロセスは1時間以上かかりますが、それでも問題ありません。これは最大で1回/月に実行されるものです。 – Steven

関連する問題