0

In Visual Studio 2012, why would this code execute so quickly in the interactive mode and so slowly when run as a console application? I have a fast computer, but I can count to 4 before the function completes at run-time and not even to 1 in the interactive studio window.

The other part that irks me about this is that when I test other people's F# code for Project Euler #4, they all run fine. So it leaves me to believe that there is something about this code that is not optimal. (and it was so neat and clean too >:P)

let reverse(digits:string) = 
     digits.ToCharArray() |> Array.rev |> System.String.Concat    

let isPalindrome(number:int) =        
    let text = number.ToString()
    if text.Length % 2 = 0 then                        
        text = reverse(text)        
    else
        false

let palindromes(floor:int, ceiling:int) =     
    seq {
        for i1 = floor to ceiling do
            for i2 = floor to ceiling do
                let result = i1 * i2
                if isPalindrome result then
                    yield result 
    }

let run = 
    palindromes(100, 999)    
    |> Seq.max

SUMMARY

For the sake of posterity, I'll list the most effective performance changes.

  • Removing Concat and creating a new string instead.
  • Retaining only the largest palindrome instead of collecting of all of them
  • Replacing the string version of reverse with a computational reverse

It still doesn't explain my original issue. But it's is so negligible now, it's hard to convince myself to spend any more time on it. I appreciate everyone's input. Thanks!

TBone
  • 431
  • 4
  • 11
  • 2
    This probably doesn't help, but odd length Strings can be palindromes, i.e. 111. – wsanville Jan 10 '13 at 04:30
  • I believe that by default, interactive will use `-optimize` but compiled won't. Try either adding `-optimize` to the compiled options or removing it from interactive – John Palmer Jan 10 '13 at 04:54
  • @wsanville - Very true. Thanks. I was probably under the influence of having seen the answer too early in my process. – TBone Jan 10 '13 at 12:19

2 Answers2

6

If you compile the project on Release mode (Optimize code option turned on), you can hardly see the difference between running the program and executing in F# Interactive.

If you have read other people's versions, you can see that checking isPalindrome can be done directly on numbers. However, a quick fix still using String:

let reverse(digits:string) = 
     System.String (digits.ToCharArray() |> Array.rev)

Note that string concatenation is slow compared to a String constructor call.

As @wsanville said, odd-length numbers could be palindrome too:

let isPalindrome(number:int) =        
    let text = number.ToString()
    text = reverse(text)  

In palindromes function, execution time can be cut down by half by iterating i2 starting from i1:

let palindromes(floor:int, ceiling:int) =     
    seq {
        for i1 = floor to ceiling do
            for i2 = i1 to ceiling do
                let result = i1 * i2
                if isPalindrome result then
                    yield result 
    }

let run = 
    palindromes(100, 999)    
    |> Seq.max

With these simple optimizations, your code run 5x faster on my machine.

pad
  • 41,040
  • 7
  • 92
  • 166
  • Apparently, I'm a noob again. Learning new languages is the new fountain of youth. I made these changes one by one, and the biggest difference by far is from using String() instead of Concat(). All of them are good and necessary changes though. However, I still see a difference in execution time between the two modes. Building in release mode doesn't seem to change this very much in my case. It's just unsettling to see this code take any time at all, but It's much better now. Thanks! – TBone Jan 10 '13 at 12:10
  • Have you tried to measure both scenarios? Maybe your feeling comes from slow start-up time of standalone application (which doesn't count). If you run the program inside VS, make sure to **uncheck** *Tools -> Options -> Debugging -> General -> Suppress JIT optimization on module load* for low overheads. – pad Jan 10 '13 at 16:47
  • Good point. But I have the previous 3 euler methods run before this one. Between the end of #3 and the end of #4 there was a substantial pause. After I made the above changes I clocked (via System.Diagnostics.Stopwatch) the execution time of each euler problem. They were all fast. It almost feels like the pause comes from the stdio or something. I don't know. All I know is that, in it's worst state, my code executed instantly in interactve mode and at about 4-5 seconds at runtime. I need to roll my changes back and clock the original version. – TBone Jan 11 '13 at 15:46
  • Talking about measuring execution time, this thread http://stackoverflow.com/questions/12036502/euler-23-in-c-0-2-seconds-in-f-30-5-seconds-why might be helpful for you. – pad Jan 11 '13 at 15:51
  • Yeah, had my "clock" method setup exactly like his "time" method. So I'm convince the execution time is ok (now, at least). – TBone Jan 11 '13 at 16:04
0

The Question is quite old, and I have the same problem before

Console apps in F#(or any F# apps), loads the "Fsharp.Core.dll" file during execution, So in order to have a faster exe file do the following.

1.)Build using "Release" mode, (say Hello.exe)

2.)Merge the file "Fsharp.Core.dll" to your exe file Using ILMERGE(http://www.microsoft.com/en-us/download/details.aspx?id=17630)

-Open Command Prompt and CD to "Release Folder"

ILMERGE Hello.exe Fsharp.Core.dll /out:Hello2.exe

Try to run Hello2.exe, and Check if it loads faster.

Rommel
  • 85
  • 2