An interesting sanity check for F# code generation. Certainly there is no reason why this will run slower in principle - there may be a glitch or two in IL code gen or inlining but that will be easy to fix - we would expect to generate essentially the same IL as C# in this case. Can you send the perf results you've observed? Also, have you inspected the IL produced by F# (with -O3) and with C#? And have you used any profiling tools on the code?

BTW let's move this to a public facing folder (optionScalper - can you do that for us? Would there be a way for me to do that is fsAdmin role?)

Thanks!

Don

By on 4/1/2006 10:04 AM ()

Don-

Thanks for the feedback! I'm really looking for better ways of accomplishing the PB algorithm in F#. Are there native F# functions I should use instead of the pow_mod(), inv_mod(), and mul_mod() functions? I'm making frequent use of mutable due to the fact I'm copying C# code, how should some of this be done in F#? What I've looked around for (and been unable to find) is a reference that discusses algorithm implementation in F# vs. C#. Can you make any suggestions? I guess I'm looking for F# best practices (which may lead to improved performance) rather than just improving performance.

I should also mention that Fabrice Bellard's <!--StartFragment -->algorithm is discussed here.

Thanks for any additional feedback!

-Chad

By on 4/2/2006 7:29 AM ()

Another note. I converted a majority of the F# module to do integer math. This is more in line with the C# code. This increases the performance a bit (291 ms. vs. 484 ms.). The C# class can still run 100 digits in 62 ms. whereas the F# code take 291 ms.

By on 4/2/2006 4:28 PM ()

Hi Chad,

Here's my quick translation of the first two functions - you will see that the use of recursive functions makes for a very different style.

1
2
3
4
5
6
7
8
9
10
11
open System
// note is_prime is buggy on 2!
let is_prime n = 
  (n%2 > 0) && 
  let r = Float.to_int(Math.Sqrt(float(n))) in
  let rec check i = (i > r) or (n % i > 0) && check (i+2) in 
  check 3

let rec next_prime a =
  let n = a+1 in 
  if is_prime n then n else next_prime(n);;

The key things here are (a) recursive functions (b) and, or and if-then-else expressions to simplify control-flow and (c) local functions - check is a function that utilizes the value 'r' .

Re performance - we will be taking a good look at the generated IL tomorrow. We've actually been doing a bit of work on the optimizer recently in conjunction with optimiing the F# BigInteger implementation that will make a lot of difference to the code you first showed. I would also expect the code above to run about as fast as the C# equivalent (if not, it would be considered a bug in F#). Both languages have strongly typed input, so full optimizations down to IL are possible. Make sure you're using -O3, of course.

By on 4/5/2006 7:12 PM ()

As another point of reference, here is the C# class I copied.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
 

public class Plouffe_Bellard
    {
        public Plouffe_Bellard() {}

        private static int mul_mod(int a, int b, int m) 
        {
            return (int) (((long) a * (long) b) % m);
        }

        /* return the inverse of x mod y */
        private static int inv_mod(int x, int y) 
        {
            int q,u,v,a,c,t;

            u=x;
            v=y;
            c=1;
            a=0;

            do 
            {
                q=v/u;
    
                t=c;
                c=a-q*c;
                a=t;
    
                t=u;
                u=v-q*u;
                v=t;
            } while (u!=0);

            a=a%y;

            if (a<0) 
            {
                a=y+a;
            }

            return a;
        }

        /* return (a^b) mod m */
        private static int pow_mod(int a, int b, int m)
        {
            int r, aa;
   
            r=1;
            aa=a;

            while (true) 
            {
                if ((b & 1) != 0)
                {
                    r = mul_mod(r, aa, m);
                }

                b = b >> 1;

                if (b == 0)
                {
                    break;
                }

                aa = mul_mod(aa, aa, m);
            }

            return r;
        }

        /* return true if n is prime */
        private static bool is_prime(int n)
        {
            if ((n % 2) == 0) 
            {
                return false;
            }

            int r = (int) Math.Sqrt(n);

            for (int i = 3; i <= r; i += 2)
            {
                if ((n % i) == 0) 
                {
                    return false;
                }
            }

            return true;
        }

        /* return the prime number immediatly after n */
        private static int next_prime(int n)
        {
            do 
            {
                n++;
            } while (!is_prime(n));

            return n;
        }

        public String CalculatePiDigits(int n)
        {
            int av, vmax, num, den, s, t;
  
            int N = (int) ((n + 20) * Math.Log(10) / Math.Log(2));
            //System.Diagnostics.Trace.WriteLine("N=" + N.ToString());
            double sum = 0;

            for (int a = 3; a <= (2 * N); a = next_prime(a)) 
            {
                //System.Diagnostics.Trace.WriteLine("a=" + a.ToString());
                vmax = (int) (Math.Log(2 * N) / Math.Log(a));
                //System.Diagnostics.Trace.WriteLine("vmax=" + vmax.ToString());

                av = 1;

                for (int i = 0; i < vmax; i++) 
                {
                    av = av * a;
                }
                //System.Diagnostics.Trace.WriteLine("av=" + av.ToString());

                s = 0;
                num = 1;
                den = 1;
                int v = 0;
                int kq = 1;
                int kq2 = 1;

                for (int k = 1; k <= N; k++) 
                {

                    t = k;

                    if (kq >= a) 
                    {
                        do 
                        {
                            t = t / a;
                            v--;
                        } while ((t % a) == 0);

                        kq = 0;
                    }
                    kq++;
                    //System.Diagnostics.Trace.WriteLine("num in=" + num.ToString());
                    //System.Diagnostics.Trace.WriteLine("t=" + t.ToString());
                    //System.Diagnostics.Trace.WriteLine("av=" + av.ToString());
                    num = mul_mod(num, t, av);
                    //System.Diagnostics.Trace.WriteLine("num out=" + num.ToString());

                    t = 2 * k - 1;

                    if (kq2 >= a) 
                    {
                        if (kq2 == a) 
                        {
                            do 
                            {
                                t = t / a;
                                v++;
                            } while ((t % a) == 0);
                        }
                        kq2 -= a;
                    }
                    den = mul_mod(den, t, av);
                    kq2 += 2;
      
                    if (v > 0) 
                    {
                        //System.Diagnostics.Trace.WriteLine("den=" + den.ToString() + " av=" + av.ToString());
                        t = inv_mod(den, av);
                        //System.Diagnostics.Trace.WriteLine("t=" + t.ToString());
                        t = mul_mod(t, num, av);
                        t = mul_mod(t, k, av);

                        for (int i = v; i < vmax; i++)
                        {
                            t = mul_mod(t, a, av);
                        }

                        s += t;

                        if (s >= av)
                        {
                            s -= av;
                        }
                    }
      
                }

                //System.Diagnostics.Trace.WriteLine("av=" + av.ToString());
                t = pow_mod(10, n - 1, av);
                //System.Diagnostics.Trace.WriteLine("t=" + t.ToString());
                //System.Diagnostics.Trace.WriteLine("s in=" + s.ToString());
                s = mul_mod(s, t, av);
                //System.Diagnostics.Trace.WriteLine("s out=" + s.ToString());
                sum = (sum + (double) s / (double) av) % 1.0;
                //System.Diagnostics.Trace.WriteLine("sum=" + sum.ToString());
            }
   
            int Result = (int) (sum * 1e9);

            String StringResult = String.Format("{0:D9}", Result);

            return StringResult;
        }

        public int DigitsReturned()
        {
            return 9;
        }
    }
By on 4/2/2006 4:22 PM ()

Don,

I advised Chad to put this in THCF as he's publishing an article on this topic. The main idea here was to decide what was useful in the code and then to "pretty it up" into an article. At the time it didn't make sense to post these details as they should lead into a polished article (and I'm doing the same on my random article).

You have moderator rights. So on any thread that you want to moderate, just hit the "Moderate" button and select "Move". You will be presented with a list of forums that can be the target of the post. Of course, I can move this as well for you. Let me know what you prefer, i.e. leave it here, try moving it yourself, or I can move it. :-)

---O

By on 4/1/2006 7:08 PM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper