在 fsi 4.0.30319.1 和 2.0.0.0 上运行 f# 相同代码的巨大性能差异
我正在使用两个版本的 fsi.exe 运行相同的 F# 代码,我可以在 FSharp-2.0.0.0 安装下找到该代码:
C:\Program Files\FSharp-2.0.0.0\bin\fsi.exe - Microsoft (R) F# 2.0 Interactive build 2.0.0
C:\Program Files\FSharp-2.0.0.0\v4.0\bin\fsi.exe - Microsoft (R) F# 2.0 Interactive build 4.0.30319.1
我发现相同的代码在 2.0.0.0 版本上的运行速度大约是原来的三倍。这有什么意义吗?我的环境或代码是否有问题?
顺便说一句,我尝试使用 v4.0 版本的原因是能够使用 TPL 并比较我的代码的顺序和并行实现。当我的并行实现比顺序实现慢得多时,经过一番绞尽脑汁后,我意识到并行版本在不同的 fsi.exe 下运行,就在那时我意识到代码的相同(顺序)版本要慢得多在4.0版本下。
预先感谢您的任何帮助
是
代码:
module Options
//Gaussian module is from http://fssnip.net/3g, by Tony Lee
open Gaussian
//The European Option type
type EuropeanOption =
{StockCode: string
StockPrice: float
ExercisePrice: float
NoRiskReturn: float
Volatility: float
Time: float
}
//Read one row from the file and return a European Option
//File format is:
//StockCode<TAB>StockPrice,ExercisePrice,NoRiskReturn,Volatility,Time
let convertDataRow(line:string) =
let option = List.ofSeq(line.Split('\t'))
match option with
| code::data::_ ->
let dataValues = (data.Split(','))
let euopt = {StockCode = code;
StockPrice = float (dataValues.[0]);
ExercisePrice = float (dataValues.[1]);
NoRiskReturn = float (dataValues.[2]);
Volatility = float (dataValues.[3]);
Time = float (dataValues.[4])
}
euopt
| _ -> failwith "Incorrect Data Format"
//Returns the future value of an option.
//0 if excercise price is greater than the sum of the stock price and the calculated asset price at expiration.
let futureValue sp ep nrr vol t =
//TODO: Is there no better way to get the value from a one-element sequence?
let assetPriceAtExpiration = sp+sp*nrr*t+sp*sqrt(t)*vol*(Gaussian.whiteNoise |> Seq.take 1 |> List.ofSeq |> List.max)
[0.0;assetPriceAtExpiration - ep] |> List.max
//Sequence to hold the values generated by the MonteCarlo iterations
//50,000 iterations is the minimum for a good aprox to the Black-Scholes equation
let priceValues count sp ep nrr vol t =
seq { for i in 1..count
-> futureValue sp ep nrr vol t
}
//Discount a future to a present value given the risk free rate and the time in years
let discount value noriskreturn time =
value * exp(-1.0*noriskreturn*time)
//Get the price for a European Option and a given number of Monte Carlo iterations (use numIters >= 50000)
let priceOption europeanOption numIters =
let futureValuesSeq = priceValues numIters europeanOption.StockPrice europeanOption.ExercisePrice europeanOption.NoRiskReturn europeanOption.Volatility europeanOption.Time
//The simulated future value is just the average of all the MonteCarlo runs
let presentValue = discount (futureValuesSeq |> List.ofSeq |> List.average) europeanOption.NoRiskReturn europeanOption.Time
//Return a list of tuples with the stock code and the calculated present value
europeanOption.StockCode + "_to_" + string europeanOption.Time + "_years \t" + string presentValue
module Program =
open Options
open System
open System.Diagnostics
open System.IO
//Write to a file
let writeFile path contentsArray =
File.WriteAllLines(path, contentsArray |> Array.ofList)
//TODO: This whole "method" is sooooo procedural.... is there a more functional way?
//Unique code for each run
//TODO: Something shorter, please
let runcode = string DateTime.Now.Month + "_" + string DateTime.Now.Day + "_" + string DateTime.Now.Hour + "_" + string DateTime.Now.Minute + "_" + string DateTime.Now.Second
let outputFile = @"C:\TMP\optionpricer_results_" + runcode + ".txt"
let statsfile = @"C:\TMP\optionpricer_stats_" + runcode + ".txt"
printf "Starting"
let mutable stats = ["Starting at: [" + string DateTime.Now + "]" ]
let stopWatch = Stopwatch.StartNew()
//Read the file
let lines = List.ofSeq(File.ReadAllLines(@"C:\tmp\9000.txt"))
ignore(stats <- "Read input file done at: [" + string stopWatch.Elapsed.TotalMilliseconds + "]"::stats)
printfn "%f" stopWatch.Elapsed.TotalMilliseconds
//Build the list of European Options
let options = lines |> List.map convertDataRow
ignore(stats <- ("Created Options done at: [" + string stopWatch.Elapsed.TotalMilliseconds + "]")::stats)
printfn "%f" stopWatch.Elapsed.TotalMilliseconds
//Calculate the option prices
let results = List.map (fun o -> priceOption o 50000) options
ignore(stats <- "Option prices calculated at: [" + string stopWatch.Elapsed.TotalMilliseconds + "]"::stats)
printfn "%f" stopWatch.Elapsed.TotalMilliseconds
//Write results and statistics
writeFile outputFile results
ignore(stats <- "Output file written at: [" + string stopWatch.Elapsed.TotalMilliseconds + "]"::stats)
ignore(stats <- "Total Ellapsed Time (minus stats file write): [" + string (stopWatch.Elapsed.TotalMilliseconds / 60000.0) + "] minutes"::stats)
printfn "%f" stopWatch.Elapsed.TotalMilliseconds
writeFile statsfile (stats |> List.rev)
stopWatch.Stop()
ignore(Console.ReadLine())
I am running the same F# code with the two versions of fsi.exe which I can find under my FSharp-2.0.0.0 install:
C:\Program Files\FSharp-2.0.0.0\bin\fsi.exe - Microsoft (R) F# 2.0 Interactive build 2.0.0
C:\Program Files\FSharp-2.0.0.0\v4.0\bin\fsi.exe - Microsoft (R) F# 2.0 Interactive build 4.0.30319.1
What I find is that the same code runs about three times faster on the 2.0.0.0 build. Does this make any sense? Is there something messed up with my environment or possibly code??
Incidentally, the reason I am trying to use the v4.0 build is to be able to use the TPL and compare sequential and parallel implementations of my code. When my parallel implementation was much slower than the sequential one, after much head-scratching I realized that the parallel version was running under a different fsi.exe, and that's when I realized that the same (sequential) version of the code is much slower under version 4.0.
Thanks in advance for any help
IS
The code:
module Options
//Gaussian module is from http://fssnip.net/3g, by Tony Lee
open Gaussian
//The European Option type
type EuropeanOption =
{StockCode: string
StockPrice: float
ExercisePrice: float
NoRiskReturn: float
Volatility: float
Time: float
}
//Read one row from the file and return a European Option
//File format is:
//StockCode<TAB>StockPrice,ExercisePrice,NoRiskReturn,Volatility,Time
let convertDataRow(line:string) =
let option = List.ofSeq(line.Split('\t'))
match option with
| code::data::_ ->
let dataValues = (data.Split(','))
let euopt = {StockCode = code;
StockPrice = float (dataValues.[0]);
ExercisePrice = float (dataValues.[1]);
NoRiskReturn = float (dataValues.[2]);
Volatility = float (dataValues.[3]);
Time = float (dataValues.[4])
}
euopt
| _ -> failwith "Incorrect Data Format"
//Returns the future value of an option.
//0 if excercise price is greater than the sum of the stock price and the calculated asset price at expiration.
let futureValue sp ep nrr vol t =
//TODO: Is there no better way to get the value from a one-element sequence?
let assetPriceAtExpiration = sp+sp*nrr*t+sp*sqrt(t)*vol*(Gaussian.whiteNoise |> Seq.take 1 |> List.ofSeq |> List.max)
[0.0;assetPriceAtExpiration - ep] |> List.max
//Sequence to hold the values generated by the MonteCarlo iterations
//50,000 iterations is the minimum for a good aprox to the Black-Scholes equation
let priceValues count sp ep nrr vol t =
seq { for i in 1..count
-> futureValue sp ep nrr vol t
}
//Discount a future to a present value given the risk free rate and the time in years
let discount value noriskreturn time =
value * exp(-1.0*noriskreturn*time)
//Get the price for a European Option and a given number of Monte Carlo iterations (use numIters >= 50000)
let priceOption europeanOption numIters =
let futureValuesSeq = priceValues numIters europeanOption.StockPrice europeanOption.ExercisePrice europeanOption.NoRiskReturn europeanOption.Volatility europeanOption.Time
//The simulated future value is just the average of all the MonteCarlo runs
let presentValue = discount (futureValuesSeq |> List.ofSeq |> List.average) europeanOption.NoRiskReturn europeanOption.Time
//Return a list of tuples with the stock code and the calculated present value
europeanOption.StockCode + "_to_" + string europeanOption.Time + "_years \t" + string presentValue
module Program =
open Options
open System
open System.Diagnostics
open System.IO
//Write to a file
let writeFile path contentsArray =
File.WriteAllLines(path, contentsArray |> Array.ofList)
//TODO: This whole "method" is sooooo procedural.... is there a more functional way?
//Unique code for each run
//TODO: Something shorter, please
let runcode = string DateTime.Now.Month + "_" + string DateTime.Now.Day + "_" + string DateTime.Now.Hour + "_" + string DateTime.Now.Minute + "_" + string DateTime.Now.Second
let outputFile = @"C:\TMP\optionpricer_results_" + runcode + ".txt"
let statsfile = @"C:\TMP\optionpricer_stats_" + runcode + ".txt"
printf "Starting"
let mutable stats = ["Starting at: [" + string DateTime.Now + "]" ]
let stopWatch = Stopwatch.StartNew()
//Read the file
let lines = List.ofSeq(File.ReadAllLines(@"C:\tmp\9000.txt"))
ignore(stats <- "Read input file done at: [" + string stopWatch.Elapsed.TotalMilliseconds + "]"::stats)
printfn "%f" stopWatch.Elapsed.TotalMilliseconds
//Build the list of European Options
let options = lines |> List.map convertDataRow
ignore(stats <- ("Created Options done at: [" + string stopWatch.Elapsed.TotalMilliseconds + "]")::stats)
printfn "%f" stopWatch.Elapsed.TotalMilliseconds
//Calculate the option prices
let results = List.map (fun o -> priceOption o 50000) options
ignore(stats <- "Option prices calculated at: [" + string stopWatch.Elapsed.TotalMilliseconds + "]"::stats)
printfn "%f" stopWatch.Elapsed.TotalMilliseconds
//Write results and statistics
writeFile outputFile results
ignore(stats <- "Output file written at: [" + string stopWatch.Elapsed.TotalMilliseconds + "]"::stats)
ignore(stats <- "Total Ellapsed Time (minus stats file write): [" + string (stopWatch.Elapsed.TotalMilliseconds / 60000.0) + "] minutes"::stats)
printfn "%f" stopWatch.Elapsed.TotalMilliseconds
writeFile statsfile (stats |> List.rev)
stopWatch.Stop()
ignore(Console.ReadLine())
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我还没有运行你的代码,但看起来你正在创建很多链接列表。这是非常低效的,但列表的表示近年来发生了变化,并且新的表示速度较慢。
I haven't run your code but it looks like you're creating lots of linked lists. That is very inefficient but the representation of lists was changed in recent years and the new representation is slower.