# How to speed up recursive compiled functions?

I'm trying to speed up the recursive function that calculates Rotation numbers:

ClearAll[θ]
θ[n_, Ω_, k_] := θ[n, Ω, k] = θ[n - 1, Ω, k] + Ω -
k/(2 π) Sin[2 π θ[n - 1, Ω, k]]
θ[0, Ω_, k_] := 0


Notice that I'm using memoization. Here's the timing of the uncompiled function:

AbsoluteTiming[Table[θ[n, .2, 1]/n, {n, 1, 1000}];]

{0.016223, Null}


Now I tried compiling the recursive function:

ClearAll[compθ]
compθ =
Compile[{{n, _Integer}, {Ω, _Real}, {k, _Real}},
If[n == 0, 0,
compθ[n - 1, Ω, k] + Ω - k/(2 π)Sin[2 π compθ[n - 1, Ω, k]]],
{{compθ[_, _, _], _Real}},
CompilationOptions -> {"InlineCompiledFunctions" -> True,
"ExpressionOptimization" -> True}, RuntimeOptions -> {"Speed"}]


The timing of the compiled function is worse:

AbsoluteTiming[Table[compθ[n, .2, 1]/n, {n, 1, 1000}];]

{0.342844, Null}


Of course the timing could be improved if I could somehow use memoization with the compiled function, but I couldn't find any example that shows how to do it. Is it possible? I also noticed with CompilePrint that the compiled function calls MainEvaluate, which is probably slowing the computation, but this comment suggests that this may be unavoidable. Is this true?

If you're looking for a faster way to calculate rotation numbers rather than optimizing your original function using memorization, try this:

AbsoluteTiming[ans1 = Table[θ[n, .2, 1]/n, {n, 1, 100000}];]

θ2 = Compile[{n, Ω, k}, Rest@NestList[# + Ω - k/(2 π) Sin[2 π #] &, 0, n]/Range@n];
ans2 = θ2[100000, 0.2, 1]; // AbsoluteTiming

And @@ (ans2 - ans1 // Chop // PossibleZeroQ)

{0.414000, Null}
{0.043000, Null}
True


Notice that different computing method has caused slightly different result but I think it doesn't hurt.

As to your original method, AFAIK, there's no way to compile the structure f[n_] := f[n] = …. The only thing I can think of to optimize it is to compile the compilable part only:

f = Compile[{p, Ω, k}, p + Ω - k/(2 π) Sin[2 π p]];
θ3[n_, Ω_, k_] := θ3[n, Ω, k] = f[θ3[n - 1, Ω, k], Ω, k]
θ3[0, Ω_, k_] := 0


but this only speeds up the calculation for a specific set of parameters only once.

Faster to use cycle in compiled functions

compθ2=Compile[{{n,_Integer},{Ω,_Real},{k,_Real}},
Module[{i=0,next=0.},
For[i=1,i<=n,i++,
next=next+Ω-k/(2*Pi)*Sin[2*Pi*next];
];
next
],
CompilationOptions->{"InlineCompiledFunctions"->True,"ExpressionOptimization"->True},RuntimeOptions->{"Speed"}]


But its not faster than using memorization

AbsoluteTiming[Table[compθ2[n, .2, 1]/n, {n, 1, 1000}];]
(*{0.075004,Null}*)
AbsoluteTiming[Table[θ[n,.2,1]/n,{n,1,1000}];]
(*{0.027002,Null}*)

• Interesting, on your computer the compiled code is about 2.8x slower than the uncompiled code, on my machine it's 7.6x slower.
– shrx
Jul 18, 2014 at 12:19
{"ft_lang_label":"__label__en","ft_lang_prob":0.7605632,"math_prob":0.9738336,"size":1173,"snap":"2023-40-2023-50","text_gpt3_token_len":383,"char_repetition_ratio":0.1308811,"word_repetition_ratio":0.053475935,"special_character_ratio":0.35038364,"punctuation_ratio":0.23282443,"nsfw_num_words":0,"has_unicode_error":false,"math_prob_llama3":0.99132925,"pos_list":[0],"im_url_duplicate_count":[null],"WARC_HEADER":"{\"WARC-Type\":\"response\",\"WARC-Date\":\"2023-12-09T22:18:22Z\",\"WARC-Record-ID\":\"<urn:uuid:bc4ffe16-5383-44f1-aa2e-62069ee4945c>\",\"Content-Length\":\"175162\",\"Content-Type\":\"application/http; msgtype=response\",\"WARC-Warcinfo-ID\":\"<urn:uuid:6f1223ff-09ba-4e2f-8e6c-56efe60abc02>\",\"WARC-Concurrent-To\":\"<urn:uuid:1b0324ee-568f-4f54-b828-e38863060507>\",\"WARC-IP-Address\":\"104.18.43.226\",\"WARC-Target-URI\":\"https://mathematica.stackexchange.com/questions/55206/how-to-speed-up-recursive-compiled-functions\",\"WARC-Payload-Digest\":\"sha1:SBEGEIRP7OBWZALU2MTXKTR4Z2WC3MHE\",\"WARC-Block-Digest\":\"sha1:W6FOR6F5S3EVMVCWDQZFBKWPJ6VS5H3R\",\"WARC-Identified-Payload-Type\":\"text/html\",\"warc_filename\":\"/cc_download/warc_2023/CC-MAIN-2023-50/CC-MAIN-2023-50_segments_1700679100972.58_warc_CC-MAIN-20231209202131-20231209232131-00553.warc.gz\"}"}