2011-06-12 3 views
8

Я реализую некоторые алгоритмы, которые работают с большими данными (~ 250 МБ - 1 ГБ). Для этого мне понадобился цикл для проведения бенчмаркинга. Однако в процессе я узнаю, что F # делает некоторые неприятные вещи, которые, я надеюсь, некоторые из вас могут прояснить.F # компилятор сохраняет мертвые объекты живыми

Вот мой код (описание проблемы ниже):

open System 

for i = 1 to 10 do 
    Array2D.zeroCreate 10000 10000 |> ignore  
    printfn "%d" (GC.GetTotalMemory(true)) 

Array2D.zeroCreate 10000 10000 |> ignore 
// should force a garbage collection, and GC.Collect() doesn't help either 
printfn "%d" (GC.GetTotalMemory(true)) 
Array2D.zeroCreate 10000 10000 |> ignore  
printfn "%d" (GC.GetTotalMemory(true)) 
Array2D.zeroCreate 10000 10000 |> ignore  
printfn "%d" (GC.GetTotalMemory(true)) 
Array2D.zeroCreate 10000 10000 |> ignore  
printfn "%d" (GC.GetTotalMemory(true)) 

Console.ReadLine() |> ignore 

Здесь выход будет как:

54000 
54000 
54000 
54000 
54000 
54000 
54000 
54000 
54000 
54000 
400000000 
800000000 
1200000000 

Out of memory exception 

Таким образом, в цикле F # отбрасывает результат, но когда Я не в цикле. F # будет ссылаться на «мертвые данные» (я посмотрел в IL, и, по-видимому, класс Program получает поля для этих данных). Зачем? И могу ли я исправить это?

Этот код запускается вне Visual Studio и в режиме деблокирования.

ответ

17

Причина такого поведения заключается в том, что компилятор F # ведет себя по-разному в глобальной области действия, чем в локальной области. Переменная, объявленная в глобальной области, превращается в статическое поле. Объявление модуля представляет собой статический класс с объявлениями let, скомпилированными как поля/свойства/методы.

Самый простой способ решить эту проблему, чтобы написать свой код в функции:

let main() =  
    Array2D.zeroCreate 10000 10000 |> ignore  
    printfn "%d" (GC.GetTotalMemory(true)) 
    Array2D.zeroCreate 10000 10000 |> ignore  
    printfn "%d" (GC.GetTotalMemory(true)) 
    // (...) 
    Console.ReadLine() |> ignore 

main() 

... но почему компилятор объявить поля, когда вы не используете значение и просто ignore это? Это довольно интересно - функция ignore - очень простая функция, которая встроена, когда вы ее используете. Объявление - let inline ignore _ =(). При встраивании функции компилятор объявляет некоторые переменные (для хранения аргументов функции).

Таким образом, еще один способ исправить это опустить ignore и написать:

Array2D.zeroCreate 10000 10000 
printfn "%d" (GC.GetTotalMemory(true)) 
Array2D.zeroCreate 10000 10000 
printfn "%d" (GC.GetTotalMemory(true)) 
// (...) 

Вы получите некоторые предупреждения компилятора, потому что результат выражения не unit, но он будет работать. Однако использование некоторой функции и написание кода в локальной области, вероятно, более надежны.

+0

+1 Спасибо, интересно :) Первое решение работает, но игнорировать игнорирование здесь не помогло. Меня все еще интересует, почему он делает то, что он делает. –

+1

Интересно ... Это помогло, когда я попробовал это с опцией '-O' (чтобы включить оптимизацию). –

+0

Странно. Он работал и здесь, когда я запускал его за пределами Visual Studio. Однако теперь цикл вызвал исключение из памяти, но ТОЛЬКО, если я сначала запустил «non-loop» версию:/Думаю, я просто буду придерживаться локальной области. –

Смежные вопросы