- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
更新:
我的编译命令是ghc -O2 Montecarlo.hs
。我的随机版本是random-1.1
,ghc版本是8.6.4
,我的系统是macOS Big Sur 11.1(Intel芯片)。我用来测试速度的命令是time ./Montecarlo 10000000
,它返回的结果是real 0m17.463s, user 0m17.176s, sys 0m0.162s
。
以下是使用蒙特卡洛计算pi
的Haskell程序。但是,当输入为1000万时,程序运行了20秒。同样逻辑编写的C程序只用了0.206秒。为什么会这样,我怎样才能加快速度?谢谢大家。
这是 Haskell 版本:
import System.Random
import Data.List
import System.Environment
montecarloCircle :: Int -> Double
montecarloCircle x
= 4*fromIntegral
(foldl' (\x y -> if y <= 1 then x+1 else x) 0
$ zipWith (\x y -> (x**2 + y**2))
(take x $ randomRs (-1.0,1) (mkStdGen 1) :: [Double])
(take x $ randomRs (-1.0,1) (mkStdGen 2) :: [Double]) )
/ fromIntegral x
main = do
num <- getArgs
let n = read (num !! 0)
print $ montecarloCircle n
这是 C 版本:
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <stdlib.h>
#define N 10000000
#define int_t long // the type of N and M
// Rand from 0.0 to 1.0
double rand01()
{
return rand()*1.0/RAND_MAX;
}
int main()
{
srand((unsigned)time(NULL));
double x,y;
int_t M = 0;
for (int_t i = 0;i < N;i++)
{
x = rand01();
y = rand01();
if (x*x+y*y<1) M++;
}
double pi = (double)4*M/N;
printf("%lf\n", pi);
}
最佳答案
在某种程度上,在数值应用程序中,Haskell 代码往往比用经典命令式语言(如 C/C++/Fortran)编写的等效代码慢 2 到 5 倍。然而,Haskell 代码慢了 100 倍是非常意外的!
使用带有 Intel Celeron N2840 64 位 cpu、运行 Linux kernel 5.3、GLIBC 2.28、GCC 8.3、GHC 8.2.2、GHC random-1.1 的有点旧的笔记本,我们得到了计时:
C code, gcc -O3: 950 msecHaskell code, ghc -O3: 104 sec
So indeed, with these configurations, the Haskell code runs about 100 times slower than the C code.
A first remark is that the π computing arithmetics atop random number generation looks quite simple, hence the runtimes are probably dominated by the generation of 2*10 millions pseudo-random numbers. Furthermore, we have every reason to expect that Haskell and C are using completely unrelated random number generation algorithms.
So, rather than comparing Haskell with C, we could be comparing the random number generation algorithms these 2 languages happen to be using, as well as their respective implementations.
In C, the language standard does not specify which algorithm function srand()
is supposed to use, but that tends to be old, simple and fast algorithms.
On the other hand, in Haskell, we have traditionally seen a lot of complaints about the poor efficiency of StdGen
, like here back in 2006:
https://gitlab.haskell.org/ghc/ghc/-/issues/427
where one of the leading Haskell luminaries mentions that StdGen
could possibly be made 30 times faster.
Fortunately, help is already on the way. This recent blog post explains how the new Haskell random-1.2 package is solving the problem of StdGen
lack of speed, using a completely different algorithm known as splitmix.
The field of pseudo-random number generation (PRNG) is a rather active one. Algorithms routinely get obsoleted by newer and better ones. For the sake of perspective, here is a relatively recent (2018) review paper on the subject.
Using another, slightly more powerful machine with an Intel Core i5-4440 3GHz 64 bits cpu, running Linux kernel 5.10, GLIBC 2.32, GCC 10.2, and critically Haskell package random-1.2:
C code, gcc -O3: 164 msecHaskell code, ghc -O3: 985 msec
So Haskell is now “just” 6 times slower instead of 100 times.
And we still have to address the injustice of Haskell having to use x**2+y**2
versus C getting x*x+y*y
, a detail which did not really matter before with random-1.1. This gives us 379 msec ! So we are back into the usual 2X-5X ballpark for Haskell to C speed comparisons.
Note that if we ask the Haskell executable to run with statistics on, we get the following output:
$ time q66441802.x +RTS -s -RTS 100000003.1415616 925,771,512 bytes allocated in the heap ... Alloc rate 2,488,684,937 bytes per MUT second Productivity 99.3% of total user, 99.3% of total elapsedreal 0m0,379suser 0m0,373ssys 0m0,004s
so Haskell is found to allocate close to one gigabyte of memory along the way, which helps to understand the speed difference.
We note that the C code uses a single random serie while the Haskell code is using two, with two calls to mkStdGen
with numbers 1 and 2 as seeds. This is not only unfair but also incorrect. Applied mathematicians who design PRNG algorithms take great care to ensure any single serie has the right statistical properties, but give essentially no guarantees about possible correlations between different series. It is not unheard of to even use the seed as an offset into a single global sequence.
This is fixed in the following code, which does not alter performance significantly:
computeNorms :: [Double] -> [Double]
computeNorms [] = []
computeNorms [x] = []
computeNorms (x:y:xs2) = (x*x + y*y) : (computeNorms xs2)
monteCarloCircle :: Int -> Double
monteCarloCircle nn =
let
randomSeed = 1
gen0 = mkStdGen randomSeed
-- use a single random serie:
uxys = (randomRs (-1.0, 1.0) gen0) :: [Double]
norms = take nn (computeNorms uxys)
insiderCount = length $ filter (<= 1.0) norms
in
(4.0::Double) * ((fromIntegral insiderCount) / (fromIntegral nn))
这个 recent SO question 中提到了新的 Haskell random-1.2 包,尽管是在新的 monadic 接口(interface)的上下文中。
假设练习的目标是衡量 C 和 Haskell 语言的相对运行速度,基本上有两种可能性:
一个是完全避免使用 PRNG,因为使用的算法不同。
另一种是通过手动为两种语言提供一种相同的算法来控制随机数的生成。例如,可以使用公开可用的 MRG32k3a由 Pierre L'Ecuyer 设计的算法。 Candidate Haskell MRG32k3a implementation here (截至 2021 年 3 月)。留作读者练习。
关于performance - 为什么这个 Monte Carlo Haskell 程序这么慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66441802/
自己试试看: import pandas as pd s=pd.Series(xrange(5000000)) %timeit s.loc[[0]] # You need pandas 0.15.1
我最近开始使用 Delphi 中的 DataSnap 来生成 RESTful Web 服务。在遵循 Marco Cantu 本人和互联网上其他几个人的指导后,我成功地使整个“链条”正常工作。 但是有一
我一直在为操作系统类(class)编写以下代码,但结果有些奇怪。该代码创建x线程并同时运行它们,以便将两个平方矩阵相乘。每个线程将输入矩阵的Number_of_rows/Number_of_threa
我正在尝试确定何时使用 parallel包以加快运行某些分析所需的时间。我需要做的一件事是创建矩阵,比较具有不同行数的两个数据框中的变量。我在 StackOverflow 上问了一个关于有效方法的问题
我最近对我的代码进行了一些清理,并在此过程中更改了此内容(不完全是真实的代码): read = act readSTRef test1 term i var = do t v^!terms.
我正在计时查询和同一个查询的执行时间,分页。 foreach (var x in productSource.OrderBy(p => p.AdminDisplayName) .Wher
我正在开发一个项目 (WPF),我有一个 Datagrid 从数据库加载超过 5000 条记录,所以我使用 BackgroundWorker 来通知用户数据正在加载,但它太慢了,我需要等待将近 2分钟
我在查询中添加 ORDER BY 时遇到问题。没有 ORDER BY 查询大约需要 26ms,一旦我添加 ORDER BY,它大约需要 20s。 我尝试了几种不同的方法,但似乎可以减少时间。 尝试 F
我是 Android 开发新手,遇到了性能问题。当我的 GridView 有太多项目时,它会变得有点慢。有什么方法可以让它运行得更快一些吗? 这是我使用的代码: 适配器: public class C
这里的要点是: 1.设置query_cache_type = 0;重置查询缓存; 2.在 heidisql(或任何其他客户端 UI)中运行任何查询 --> 执行,例如 45 毫秒 3.使用以下代码运行
想象下表: CREATE TABLE drops( id BIGSERIAL PRIMARY KEY, loc VARCHAR(5) NOT NULL, tag INT NOT
我的表 test_table 中的示例数据: date symbol value created_time 2010-01-09 symbol1
首先,如果已经有人问过这个问题,我深表歉意,至少我找不到任何东西。 无论如何,我将每 5 分钟运行一次 cron 任务。该脚本加载 79 个外部页面,而每个页面包含大约 200 个我需要在数据库中检查
我有下面的 SQL 代码,它来自 MySQL 数据库。现在它给了我期望的结果,但是查询很慢,我想我应该在进一步之前加快这个查询的速度。 表agentstatusinformation有: PKEY(主
我需要获取一个对象在 Core Data 中数千个其他对象之间的排名。现在,这是我的代码: - (void)rankMethod { //Fetch all objects NSFet
我正在编写一个应用程序,我需要在其中读取用户的地址簿并显示他所有联系人的列表。我正在测试的 iPhone 有大约 100 个联系人,加载联系人确实需要很多时间。 ABAddressBookRef ad
我正在使用 javascript 将 160 行添加到包含 10 列的表格中。如果我这样做: var cellText = document.createTextNode(value); cell.a
我是 Swift 的新手,我已经设置了一个 tableView,它从 JSON 提要中提取数据并将其加载到表中。 表格加载正常,但是当表格中有超过 10 个单元格时,它会变得缓慢且有些滞后,特别是它到
我在 InitializeCulture 和 Page_PreInit 事件之间的 asp.net 页面中遇到性能问题。当我重写 DeterminePostBackMode() 时,我发现问题出在 b
我在 Hetzner 上有一个带有 256GB RAM 6 个 CPU(12 个线程) 的专用服务器,它位于德国。我有 CENTOS 7.5。 EA4。 我的问题是 SSL。每天大约 2 小时,我们在
我是一名优秀的程序员,十分优秀!