last day (15 days later) » 

07:05
xzczd, 你好,我知道你对Mathematica的性能提升很有研究,所以想问问你关于性能的问题
5
Q: How to boost the performance of my own BSplineFunction

Shutao TANGIn my CAGD package, I implemented a CAGDBSplineFunction[] like built-in BSplineFunction[]. Here is a performance comparison: pts = Table[{ Cos[2 Pi u/6] Cos[v], Sin[2 Pi u/6] Cos[v], v}, {u, 6}, {v, -1, 1, 1/2}]; f = BSplineFunction[pts] g = CAGDBSplineFunction[pts] ParametricPlot3D[f[u, v], {u...

你觉得这个函数还有提升的空间吗?
07:31
乙烷啊,这代码我都运行不了...
row.Transpose[validnets, {1, 3, 2}].col在这里出现Dot::dotsh: Tensors .... have incompatible shapes. >>
随便改了一下row.Transpose[validnets, {1, 3, 2}].Transpose[col],然后
@happyfish 抱歉,lst // Transpose // Last // Rest
07:49
天啊,又是我代码复制错了
@happyfish 哈哈,没有,是我刚刚改动的:)
@happyfish 根据你的经验,你觉得还有提升的空间吗?
@xzczd
我想想
我以前觉得mathematica的函数式编程比较快(Fold[],Nest[]), 但是在开发NURBS包的时候我才发现他们很慢,还没有过程式的Do[]
以前的版本我是用的Fold[],运行一次是6s多,修改为Do[]Compile[]后提速到2s
08:11
@ShutaoTANG ……确切地说,在不考虑手动用Compile的情况下Fold和Nest之类的是比较快
因为它们可以自动编译
但是如果手动调Compile了,它们和Do将失去区别。(你可以看一下CompilePrint的结果。他们全变goto了。)
甚至有的时候Nest族函数还会迷之出现CopyTensor。(当然也有帖子指出CopyTensor不一定会降低运行速度就是了。)
@xzczd 谢谢,我知道你是compiled方面的行家,最近几次我开始使用了compile,效率提高了不少
所以我想咨询你,你觉得我的那个函数还有提速的空间吗?
不……我只是踩在之前众多研究Compile的真·专家们的尸体,不对,肩膀上而已
我以为是学的你的经验:)
optimizedBSplineSurf的定义你是不是忘了给了,虽然我大概能猜到
特别是Hold[]/.DownValue[]
08:23
@ShutaoTANG 这个是从Leonid Shifrin那里学的
一个就是将compiledNonzeroBasis改为optimizedNonzeroBasis
目测把compiledBSplineSurf给编译了之后还能快一点
不会吧,compiledBSplineSurf还能再编译?我是没有朝那个方向想
……应该说它根本就没编译吧?
只是里面放了几个编译过的函数而已
频繁“进出”编译函数也可能大辐降低性能的
对对,因为我发现compiledNonzeroBasis[]这个是核心函数
其他的操作,row.Transpose[validnets, {1, 3, 2}].col这个应该很快了
08:31
唉,改了半天没有什么实质性提升
不过发现了一个无聊的事情
`pts = Table[{Cos[2 Pi u/6] Cos[v], Sin[2 Pi u/6] Cos[v], v}, {u,
6}, {v, -1, 1, 1/2}] // N;`
这个还是你给的建议,我原本是这样做的`With[{row = AllBasis[m - 1, u], col = AllBasis[n - 1, v]},
row.pts[[All, All, #]].col & /@ {1, 2, 3}]`
可以省很多时间
说来听听
懂了,你的意思是pts一开始的精度不是Real
是的
我这么改完就从0.68s左右变成0.28s
而且那步操作时间可以忽略不计
@ShutaoTANG 我隐约记得我在那帖里也说了想要更快就得花心思把其它部分也编译掉
08:38
@xzczd 可能我当时没太注意你的建议
我感觉到慢,还是前天的时候,以前也没觉得太慢
还有一处就是你Compile里面算了一遍searchSpan,但是调用它的函数compiledBSplineSurf里已经算过了,要不要把里面的删掉直接把结果p‌​ass过去
row = compiledNonzeroBasis[deg1, knots1, u, i];
col = compiledNonzeroBasis[deg2, knots2, v, j];
这倒是一个好建议@happyfish 谢谢
虽然说这个没有任何效果
恩恩,这样说来,差不多就是这个效率了,因为系统的BSplineFunction是用C写的,所以很快
你要不要考虑用Library Link
08:42
要是想写一个更快的版本,估计得写好C程序,再用Library Link链接到Mathematica
去年想学LibraryLink没学会,那个说明比较难懂,而且OS X上出现编译器兼容问题完全无法解决
我也一直想学,但是还是蛮繁琐的,就一直拖着
呵呵,
呵呵呵呵
呵呵呵呵呵呵呵呵
等一下,我马上答你的问题
估计有突破了:)
已答。
还是只有系统函数的一半快,不过这也差不多了吧
凭空猜的话,我会猜剩下的那零点零几秒是被Position吃掉了,不过这部分改起来没那么容易,就不改了。
跑步去也。
09:07
非常感谢
09:21
我还有一点改进
With[{get = Compile`GetElement},
basisCoeff[deg_, U_, i_, u_] :=
If[U[[i + deg + 1]] !=
U[[i + 1]], (u - get[U, i + 1])/(get[U, i + deg + 1] -
get[U, i + 1]), 0.];
optimizedNonzeroBasis2 =
ReleaseHold[
Hold@Compile[{{deg, _Integer}, {knots, _Real,
1}, {u0, _Real}, {i, _Integer}},
Module[{lst = Table[0., {deg + 2}, {deg + 1}], c = i - 2},
lst[[2, 1]] = 1.0;
Do[
With[{mn = m + n},
lst[[mn - c, n + 1]] =
basisCoeff[n, knots, m, u0] get[lst, mn - c - 1,
n] + (1 - basisCoeff[n, knots, m + 1, u0]) get[lst,
fullycompiledBSplineSurf2 =
Hold@Compile[{{ctrlnets, _Real,
3}, {deg1, _Integer}, {deg2, _Integer}, {knots1, _Real,
1}, {knots2, _Real, 1}, {u, _Real}, {v, _Real}},
Module[{i, j, validnets, row, col},
i = With[{un = #2[[-(#1 + 1)]]},
If[#3 == un,
Position[#2, un][[1, 1]] -
2, (Position[#, Min[#]] &@UnitStep[#3 - #2])[[1, 1]] -
2]] &[deg1, knots1, u];
j =
With[{un = #2[[-(#1 + 1)]]},
If[#3 == un,
Position[#2, un][[1, 1]] -
2, (Position[#, Min[#]] &@UnitStep[#3 - #2])[[1, 1]] -
2]] &[deg2, knots2, v];
这个还能快一点
简而言之就是Ordering不能编译,重写以下searchSpan让他可以编译
 
2 hours later…
11:19
@ShutaoTANG
11:39
@happyfish 其实可以编译的,你看CompilePrint的结果……
12:04
@xzczd 看来被编译成OrderingInteger了,然而Compile`CompilerFunctions[]里面没有Ordering函数,为什么会这样啊
12:35
@happyfish 是有几个例外的(不过Ordering我也是看了这个例子才发现)……还有Total也是。此外还有这里:mathematica.stackexchange.com/a/61651/1871
这个没什么理由可言,只能说Compile`CompilerFunctions[]的结果基本是对的,但严格来说既不充分也不必要
12:59
@xzczd 好像是因为这里还有一堆DownValues[Internal`CompileValues]
 
1 hour later…
14:12
@happyfish 那个表里好多不能编译的,比如LinearSolve,Oleksandr R.还专门在他那个答案的Edit 2里说了这个问题
@xzczd LinearSolve[]本来就是调用外部 Fortran库,像BLAS, LAPACK
总结提速的原因,我觉得如下:
(1) NonzeroBasis[]的计算非常过程化,类似于C语言
(2) NonzeroBasis[]被编译为C后,调用次数非常多,像ParametricPlot3D[]出图,在两个方向上采样估计有 100×100次
14:40
这样有一个比较
@happyfish @xzczd
15:08
话说如果不是因为Ordering不能编译,难道是因为改成了Position和Min所以有那么一点点提升?
@happyfish 我写进去的版本还是xzczd的编译版本,不是你的版本
我的那个还是还有处理权重,所以会有一点时间
其实这样已经快了好多
谢谢你们俩@happyfish@xzczd
对了,你平时科研或者学习用什么语言?
15:33
@ShutaoTANG 没,我是说之前我那个比xzczd快一点点的原因
@happyfish 说实话这种细微的提升常常属于讲不清楚的范畴……我开始还猜是Position慢,结果用If,Throw,Catch重写了一下(把那几个选项调了之后这应该是最快的)‌​结果还是没提升……
@ShutaoTANG "RuntimeOptions" -> {"EvaluateSymbolically" -> False}在这里对提速有重大意义不知道你注意到没有
我那个代码的有效改的地方其实只有那一个
理论上讲Position+Min是O(n),Ordering 是O(nlogn),但是这里数据这么小应该没有任何区别

  last day (15 days later) »