吧务
level 15
xzcyr
楼主
(类似的讨论以往有过多次,不过当前老帖几乎全被百度隐藏,发出来权当补充吧。)
因为新电脑里预装了正版的Office,就稍微折腾了下VBA,四处翻找相关文章的时候,知乎的某主题引起了我的兴趣:
《为什么 C++ 只比 VBA 快 4倍?》(链接就不贴了,否则八成又要被吞。直接搜索这个标题就可以找到。)
那么Mathematica(在不改变算法实质的情况下)的速度怎么样?——基本可以肯定比C++慢(即便编译,Mathematica在大规模低级运算上也往往不及精心编写过的C/C++/Fortran),但和VBA比呢?(当年学VB时我曾不止一次听到过“VB速度慢”的说法,但我从没正经研究过。)
总之先试试C++和VBA的速度。C++快得意料之中,51毫秒(编译器是TDM-GCC 5.1.0-2 64位,编译选项-Ofast):

VBA约279毫秒,和文中所述速度基本一致:

然后是Mathematica(测试版本9.0.1):
(自认为)写的比较好的非编译版本:
(*
可以借助
Simplify`PWToUnitStep@PiecewiseExpand@If[x < 1, 1, 0]
计算出所需的UnitStep组合
*)
With[{a = RandomReal[1, {25 10^5}], b = RandomReal[1, {25 10^5}]},
1/(2.5 10^6) ((Total[1. - UnitStep[-1 + (a^2 + b^2)]] +
Total[1. - UnitStep[-1 + ((1 - a)^2 + (1 - b)^2)]])) 2] // AbsoluteTiming
(* {0.295614, 3.14008} *)
和VBA纯循环版本的速度差不多。
然后是编译版:
Compile[{},
With[{a = RandomReal[1, {25 10^5}], b = RandomReal[1, {25 10^5}]},
1/(2.5 10^6) ((Total[1. - UnitStep[-1 + (a^2 + b^2)]] +
Total[1. - UnitStep[-1 + ((1 - a)^2 + (1 - b)^2)]])) 2]][] // AbsoluteTiming
(* {0.126434, 3.14154} *)
比VBA快了,不过还是只有C++的一半。
让我有些惊讶的是,高维列表会导致代码略微变慢:
Compile[{},
With[{n = 25 10^5},
With[{lst = RandomReal[1, {2, n}]},
1./n ((Total[1. - UnitStep[-1. + Total[lst^2]]] +
Total[1. - UnitStep[-1. + Total[(1. - lst)^2]]])) 2]]][] // AbsoluteTiming
(* {0.152793, 3.14172} *)
编译为C的话又能榨出一点速度来,当然了,调用C编译器挺慢的……:
cf = Compile[{},
Module[{c = 0., n = 25 10^5},
Do[With[{a = RandomReal[], b = RandomReal[]}, If[a^2 + b^2 < 1., c += 1.];
If[(1. - a)^2 + (1. - b)^2 < 1., c += 1.];], {n}]; c 2/(n)],
CompilationTarget -> C, RuntimeOptions -> "Speed"];//AbsoluteTiming
(* {0.296880, Null} *)
cf[] // AbsoluteTiming
(* {0.110118, 3.14154} *)
上面的代码还有优化余地吗?
2019年10月05日 15点10分
1
因为新电脑里预装了正版的Office,就稍微折腾了下VBA,四处翻找相关文章的时候,知乎的某主题引起了我的兴趣:
《为什么 C++ 只比 VBA 快 4倍?》(链接就不贴了,否则八成又要被吞。直接搜索这个标题就可以找到。)
那么Mathematica(在不改变算法实质的情况下)的速度怎么样?——基本可以肯定比C++慢(即便编译,Mathematica在大规模低级运算上也往往不及精心编写过的C/C++/Fortran),但和VBA比呢?(当年学VB时我曾不止一次听到过“VB速度慢”的说法,但我从没正经研究过。)
总之先试试C++和VBA的速度。C++快得意料之中,51毫秒(编译器是TDM-GCC 5.1.0-2 64位,编译选项-Ofast):

VBA约279毫秒,和文中所述速度基本一致:
然后是Mathematica(测试版本9.0.1):(自认为)写的比较好的非编译版本:
(*
可以借助
Simplify`PWToUnitStep@PiecewiseExpand@If[x < 1, 1, 0]
计算出所需的UnitStep组合
*)
With[{a = RandomReal[1, {25 10^5}], b = RandomReal[1, {25 10^5}]},
1/(2.5 10^6) ((Total[1. - UnitStep[-1 + (a^2 + b^2)]] +
Total[1. - UnitStep[-1 + ((1 - a)^2 + (1 - b)^2)]])) 2] // AbsoluteTiming
(* {0.295614, 3.14008} *)
和VBA纯循环版本的速度差不多。
然后是编译版:
Compile[{},
With[{a = RandomReal[1, {25 10^5}], b = RandomReal[1, {25 10^5}]},
1/(2.5 10^6) ((Total[1. - UnitStep[-1 + (a^2 + b^2)]] +
Total[1. - UnitStep[-1 + ((1 - a)^2 + (1 - b)^2)]])) 2]][] // AbsoluteTiming
(* {0.126434, 3.14154} *)
比VBA快了,不过还是只有C++的一半。
让我有些惊讶的是,高维列表会导致代码略微变慢:
Compile[{},
With[{n = 25 10^5},
With[{lst = RandomReal[1, {2, n}]},
1./n ((Total[1. - UnitStep[-1. + Total[lst^2]]] +
Total[1. - UnitStep[-1. + Total[(1. - lst)^2]]])) 2]]][] // AbsoluteTiming
(* {0.152793, 3.14172} *)
编译为C的话又能榨出一点速度来,当然了,调用C编译器挺慢的……:
cf = Compile[{},
Module[{c = 0., n = 25 10^5},
Do[With[{a = RandomReal[], b = RandomReal[]}, If[a^2 + b^2 < 1., c += 1.];
If[(1. - a)^2 + (1. - b)^2 < 1., c += 1.];], {n}]; c 2/(n)],
CompilationTarget -> C, RuntimeOptions -> "Speed"];//AbsoluteTiming
(* {0.296880, Null} *)
cf[] // AbsoluteTiming
(* {0.110118, 3.14154} *)
上面的代码还有优化余地吗?

