Book Image

F# High Performance

By : Eriawan Kusumawardhono
Book Image

F# High Performance

By: Eriawan Kusumawardhono

Overview of this book

F# is a functional programming language and is used in enterprise applications that demand high performance. It has its own unique trait: it is a functional programming language and has OOP support at the same time. This book will help you make F# applications run faster with examples you can easily break down and take into your own work. You will be able to assess the performance of the program and identify bottlenecks. Beginning with a gentle overview of concurrency features in F#, you will get to know the advanced topics of concurrency optimizations in F#, such as F# message passing agent of MailboxProcessor and further interoperation with .NET TPL. Based on this knowledge, you will be able to enhance the performance optimizations when implementing and using other F# language features. The book also covers optimization techniques by using F# best practices and F# libraries. You will learn how the concepts of concurrency and parallel programming will help in improving the performance. With this, you would be able to take advantage of multi-core processors and track memory leaks, root causes, and CPU issues. Finally, you will be able to test their applications to achieve scalability.
Table of Contents (15 chapters)
F# High Performance
Credits
About the Author
About the Reviewer
www.PacktPub.com
Customer Feedback
Preface

Understanding the nature of F# code


Understanding the nature of F# code is very crucial and is a definitive prerequisite before we begin to measure how long it runs and its effectiveness. We can measure a running F# code by running time, but to fully understand why it may run slow or fast, there are some basic concepts we have to consider first.

Before we dive more into this, we must meet the basic requirements and setup.

After the requirements have been set, we need to put in place the environment setting of Visual Studio 2015. We have to set this because we need to maintain the consistency of the default setting of Visual Studio. The setting should be set to General.

These are the steps:

  1. Select the Tools menu from Visual Studio's main menu.

  2. Select Import and Export Settings... and the Import and Export Settings Wizard screen is displayed:

  3. Select Reset all Settings and then Next to proceed.

  4. Select No, just reset my settings overwriting my current setting and then Next to proceed

  5. Select  General and then click on Finish:

After setting it up, we will have a consistent layout to be used throughout this book, including the menu locations and the look and feel of Visual Studio.

Now, we are going to scratch the surface of F# runtime with an introductory overview of common F# runtime, which will give us some insights into F# performance.

F# runtime characteristics

The release of Visual Studio 2015 occurred at the same time as the release of .NET 4.6 and the rest of the tools, including the F# compiler. The compiler version of F# in Visual Studio 2015 is F# 4.0.

F# 4.0 has no large differences or notable new features compared to the previous version, F# 3.0 in Visual Studio 2013.

Its runtime characteristic is essentially the same as F# 4.0, although there are some subtle performance improvements and bug fixes.

For more information on what's new in F# 4.0 (described as release notes) visit: 

https://github.com/Microsoft/visualfsharp/blob/fsharp4/CHANGELOG.md

Note

At the time of writing this book, the online and offline MSDN Library of F# in Visual Studio does not have F# 4.0 release notes documentation, but you can always go to the GitHub repository of F# to check the latest update.

These are the common characteristics of F# as part of managed programming language:

  • F# must conform to .NET CLR. This includes the compatibilities, the IL emitted after compilation, and support for .NET BCL (the basic class library). Therefore, F# functions and libraries can be used by other CLR-compliant languages such as C#, VB, and managed C++.

  • The debug symbols (PDB) have the same format and semantics as the other CLR-compliant languages. This is important because F# code must be able to be debugged from other CLR-compliant languages as well.

From the managed languages perspective, measuring the performance of F# is similar when measured by tools such as the CLR profiler. But from an F# unique perspective, the following are the unique characteristics of F#:

  • By default, all types in F# are immutable. Therefore, it's safe to assume it is intrinsically thread safe.

  • F# has a distinctive collection library, and it is immutable by default. It is also safe to assume it is intrinsically thread safe.

  • F# has a strong type inference model, and when a generic type is inferred without any concrete type, it automatically performs generalizations.

  • Default functions in F# are implemented internally by creating an internal class derived from F#'s FSharpFunc. This FSharpFunc is essentially a delegate that is used by F# to apply functional language constructs such as currying and partial application.

  • With tail call recursive optimization in the IL, the F# compiler may emit .tail IL, and then the CLR will recognize this and perform optimization at runtime. More on this in Chapter 7, Language Features and Constructs Optimization.

  • F# has inline functions as options. More on this in Chapter 7, Language Features and Constructs Optimization.

  • F# has a computation workflow that is used to compose functions. This will be described in more detail in Chapter 8, Optimizing Computation Expressions.

  • F# async computation doesn't need Task<T> to implement it.

Note

Although F# async doesn't need the Task<T> object, it can operate well with the async-await model in C# and VB. The async-await model in C# and VB is inspired by F# async, but behaves semantically differently based on more things than just the usage of Task<T>. More on this in Chapter 4, Introduction to Concurrency in F#.

All of those characteristics are not only unique, but they can also have performance implications when used to interoperate with C# and VB.

Relation between F# code and its generated assembly

The F# assembly (commonly known as DLL or executable EXE in .NET running on Windows) is the same as the C#/VB assembly upon compilation. The end product of the compiler is a .NET assembly.

An assembly may contain multiple namespaces, and each namespace may contain multiple files of modules, classes, or a mix of both.

The following table describes the F# relation of code and compiled code (assembly):

F# code

Description

Compiled code

Project

An organization of an F# project. It may contain F# script (FSX) and F# source files (FS).

In the conceptual layout, a project may contain multiple namespaces that spawn across multiple files of FSX and F# script.

An assembly of either executable EXE or DLL class library

Namespace

A logical organization of modules and classes to help organizing within an organization, company, or functionality.

For example: the System.Web namespace that contains many classes related to enable browser/server communication, including HTTP and HTTPS.

A namespace may spawn across different assemblies instead of a namespace for only one assembly

Module

A module in F# is equal to a C# static class or module in VB. An F# FS file may contain multiple modules, although it is not recommended to have this practice.

Part of a generated assembly

Classes and interfaces

A file can contain multiple classes and interfaces under different namespaces. It is recommended to have not more than one namespace for each file as this also minimizes compilation time when it tries to resolve references.

Part of a generated assembly

Immutability versus mutability

F# implementation of types and collection types are immutable. Immutable in this sense means it is read-only, and we can only initialize the object with an initial value and we can't change it afterwards.

Mutability means once we initialize an object, it can be changed afterwards. This is why it is sometimes called a mutating object value instead of a changing object value.

For example consider the following:

let anynumber = 0 

By default, anynumber is immutable and the value of it will always be 0.

To mark a variable as mutable, F# has the mutable keyword and we can use mutable in the let declaration, as in this example:

let mutable anymutablenumber = 0 

However, changing the value requires the use of the <- symbol in F#, for example:

anymutablenumber <- anymutablenumber + 1 

Since the nature of F# is functional, a symbol can be both a data and a function. The content of the symbol is read-only, so does a function in it.

Immutability also has another advantage: it scales well across multiple threads or even in parallel, no matter whether it's a value or a function. The immutability guarantee means that it is free of side effects. It is then safe to spawn multiple symbols in parallel because the result of an execution will be guaranteed to have the same result. This is also simply called thread safe.

The fact that F# has a mixed support for functional and OOP at the same time (including having support for the inherent mutable state of OOP) may lead to bottlenecks as described next.