Sep 27

High performance numerics with F#

We stumbled upon an interesting result recently whilst toying with numerical benchmarks ported from OCaml. Typically, OCaml programs are very easy to port but run more slowly under F# because the performance characteristics of the languages are different. However, we recently observed that some benchmarks optimized for OCaml actually run substantially faster in F# without having to make even a single change.

Specifically, the OCaml implementations of the numerically-intensive spectral-norm and n-body benchmarks from the Computer Language Shootout run more quickly in F#. We believe the reason is essentially that Microsoft’s .NET compiler is much better at optimizing simple numerical algorithms, particularly loops over floating point arrays. Indeed, the results are all the more compelling because F# on vanilla 32-bit Windows XP Pro outperforms a longer, hand-optimized OCaml implementation running on OCaml’s best-case platform for numerical programs, 64-bit Linux. In particular, bounds checking incurs a significant overhead in the spectral-norm benchmark and, we believe, the bounds checks are automatically hoisted from the inner loop in F# but this must be done manually in OCaml.

  OCaml F#
Spectral-Norm 14.353 9.374
N-Body 9.469 6.933

This clearly shows that F# can achieve excellent performance on common machines for an important class of numerical algorithms.

Jun 26

Libraries

The source code for both fslib.dll and mllib.dll is included as sample code in the release. All F# programs implicitly reference both mllib.dll and fslib.dll as well as the following .NET assemblies, where available:

  • mscorlib.dll
  • System.dll
  • System.Xml.dll
  • System.Runtime.Remoting.dll
  • System.Data.dll
  • System.Drawing.dll
  • System.Web.dll
  • System.Web.Services.dll
  • System.Windows.Forms.dll

Some of the namespaces, modules and types in the F# library are:

This namespaces Microsoft.FSharp.Core, Microsoft.FSharp.Collections, Microsoft.FSharp.Control and Microsoft.FSharp.Text are automatically opened for every source code file, as is the module Microsoft.FSharp.Core.Operators. This means modules such as Microsoft.FSharp.Collections.List can typically be opened just by using open List.

Some modules are primarily of interest for those cross-compiling OCaml code, e.g. those under the Microsoft.FSharp.Compatibility.OCaml namespace.

 
Jun 26

The F# Programming Language Informal Specification

1. Introduction

F# is a scalable, script-like, type-safe, efficiently executing functional/imperative/object-oriented programming language. It aims to be the premier type-safe symbolic programming language for the .NET platform.

This manual describes the F# language through a mixture of informal and semi-formal techniques.

F# is similar to the Caml programming language family, and was partially inspired by it. See F# and OCaml for more details.

2. Lexical Specification

Comments. Comments are delimited by (* and *) and may be nested. In addition, C#/C++-style // single-line comments may be used and extend to the end of a line.

Strings embedded within comments are parsed without looking for closing *) marks, e.g., (* Here’s a code snippet: let s = “*)” *) is a valid comment.

Conditional Compilation. #if ident/#endif directives delimit conditional compilation sections. Text in a #if section is included in the compilation if the given ident is defined in the compilation environment, typically via the command line option –define. F# also allows code to be cross-compiled as both F# and OCaml code. Sections marked

         (*IF-FSHARP  ... ENDIF-FSHARP*)
     or  (*F#         ... F#*)

are included when compiling with the F# compiler, and text surrounded by

         (*IF-CAML*)  ...  (*ENDIF-CAML*)
      or (*IF-OCAML*)  ... (*ENDIF-OCAML*)

is excluded when compiling with the F# compiler. Of course the converse holds when compiling programs using an OCaml compiler.

Keywords. The keywords of the F# language are shown below.

abstract and as assert begin class default delegate do done downcast downto else end
enum exception extern false finally for fun function if in inherit interface land lazy let
match member  module mutable namespace new null of open or override rec sig static struct then to true try
type val when inline upcast while with void

The following identifiers are also keywords because they are keywords in OCaml. However their use is not recommended in F# code, since symbolic alternatives are available.

asr land lor lsl lsr lxor mod

The symbols act as keywords:

| -> ->> ] [| |] { } #

The following identifiers are used in OCaml and/or C# and are reserved for future use by F#. The –ml-compatibility option permits OCaml identifiers to be used.

async atomic break checked component const constraint
constructor continue decimal eager event
external fixed functor include method mixin object
process property protected public pure readonly return sealed
virtual volatile {}

Operators and Symbolic Keywords. Operator names are sequences of characters as shown below, except where a combination of characters is used as a symbol elsewhere in the language. Precedence is specified below.

    first-op-char := !$%&*+-./?@^|~
    op-char       := first-op-char | :
    operator      := first-op-char op-char*

For example, &&& and ||| are valid operators. These have default definitions as overloaded bitwise manipulations on integer types.

Certain Unicode characters may also be included in the operator set. The final choice of such characters is as yet unspecified, though it may be specified in the change notices with each compiler release.

In addition, the following operators correspond to syntactic forms for expressions.

    .[]       (expression form "a.[i]")
    .[]
                    

The .[] forms have default definitions as overloaded lookup and assignment on arrays, dictionarys, maps and any value supporting an Item property. The .() forms have default definitions as array lookup. In theory the operators can be redefined, e.g., the example below shows how to do this for byte arrays, but this is rarely used in practice:

    let (.[])   s n   = Bytearray.get s n
    let (.[]
                    

For compatibility with OCaml the following identifiers are parsed as infix operators, corresponding to logical bitwise manipulations:

    land lor lsl lsr lxor mod

However the corresponding overloaded symbolic operators (&&& etc.) are often preferred.

The following operators are assigned a meaning via lifted term quotation:

    quotation-operator-left :=
| op-char*
|