All Versions
37
Latest Version
Avg Release Cycle
20 days
Latest Release
760 days ago

Changelog History
Page 4

  • v3.2.2-beta Changes

    June 23, 2019

    HashMap<K, V> and HashMap<EqK, K, V> are now about 4 times faster. The underlying implemenation has now switched from being a Map<int, Lst<(K, V)> to a Compressed Hash Array Map Trie (CHAMP).

    ๐ŸŽ This still has some more performance to squeeze out of it as it's a relatively naive implementation at the moment, so I won't post up the figures just yet.

  • v3.2.0 Changes

    June 20, 2019

    ๐Ÿš€ Included in this release are performance improvements for Option<A>, OptionUnsafe<A>, and Seq<A>. Seq<A> has now also become a struct to stop the usage of null but also to facilitate the new performance improvements. ISeq<A> will eventually be deprecated and so there are now [Obsolete] warnings - but I won't kill it for quite a long time, I just want to discourage its usage.

    ๐Ÿš€ I'm doing this as a beta release because the changes to Seq<A> are enormous - well, a complete re-write. It is now entirely lock free and internally has three possible states: Lazy, Strict, and Empty - which allows for improved performance for the happy paths.

    ๐ŸŽ I will keep language-ext in beta for a while as I go through the library optimising what I believe are the most used features. I am doing this now because I feel the library is settling down now - as in there's less churn of features and interfaces - and so now is a good time to start squeezing the most performance out of it.

    ๐Ÿšง Mostly this will involve trying to reduce memory allocations where possible as well as inlining operations that make unnecessary calls (which were there to keep everything super generic under the hood). This will make long term maintenance a bit harder, which is why it needed to wait until now.

    ๐ŸŽ Performance timings

    ๐ŸŽ Below are some tests I've used to tweak the performance, the timings are all relative from the old Seq to the new, so my machine's spec shouldn't matter.

    Streaming a lazy sequence

    ๐ŸŽ This performance test wraps a lazy enumerable in a Seq and then streams them in one-by-one via the foreach loop and saves the value in a results array.

    var seq = Seq(Range(0, count));foreach(var item in seq) { results[j] = item; j++; }
    
    Old Seq<A> New Seq<A>
    138 ns / op 54 ns / op

    ๐ŸŽ So a performance improvement of 2.55 times. Evaluating an IEnumerable<T> lazy sequence is about 29 ns / op which whilst faster than Seq<A> doesn't have the memoisation that Seq has. (or any of the other cool features, for that matter)

    Streaming a strict sequence

    A lot of the time I find I'm working with strict sequences - i.e. non-lazy. This is the happy path for sequences and a lot of optimisations can happen if the code knows the sequence isn't lazy

    var seq = Seq(Range(0, count));seq = seq.Strict();foreach (var item in seq) { results[j] = item; j++; }
    
    Old Seq<A> New Seq<A>
    16.1 ns / op 10.9 ns / op

    ๐ŸŽ A performance improvement of ~32.3%. This isn't quite as significant as the lazy stream improvements, but interestingly this is now faster than List<T> from the BCL, which measures around 12.5 ns / op.

    โž• Adding an item to the end of a Seq

    Essentially calling this, many times:

    seq = seq.Add(value);
    
    Old Seq<A> New Seq<A>
    49.5 ns / op 33.2 ns / op

    Around a 33% improvement. This isn't quite as fast as the 10.9 ns / op of the BCL's List<T>, I will work on this some more. But this is an immutable, thread-safe, data structure - which the BCL List<T> definitely isn't.

    โž• Adding an item to the beginning of a Seq

    Essentially calling this, many times:

    seq = value.Cons(seq);
    
    Old Seq<A> New Seq<A>
    40 ns / op 20 ns / op

    ๐ŸŽ Twice as fast. This isn't far off List<T>.Add - and so that's why I think I can make Seq<A>.Add gain a bit more speed. Note: the BCL's List<T>.Insert(0, x) which is the equivalent to Seq<A>.Cons has terrible performance at 10310 ns / op. So that's one to look out for!

    Other Seq<A> functions

    Most Seq<A> functions will either evaluate a lazy stream, a strict stream, or Add or Cons items. And so nearly all Seq<A> related functionality will gain due to these changes.

    Option<A> and OptionUnsafe<A>

    ๐ŸŽ I haven't done any performance timings for these types, as most improvements are to reduce unnecessary allocations. I have removed the support for lazy options - if anyone misses them then I'll probably create an OptionLazy<A> type. I'd rather not, but I felt that because Option<A> is probably the most used type in lang-ext then it should always be on the happy path. This has reduced a lot of unnecessary branching and has allowed the internals of Option<A> and OptionUsafe<A> to be simplified.

    Feedback please

    โšก๏ธ Because these are some big changes, if you have an app with lots of unit tests it'd be great if you could verify that they all still run with these updates. I have obviously made sure all of the existing unit tests pass and have built a number of test harnesses to check that everything still works and to verify the thread-safeness of Seq, but changes like these have a habit of kicking you when you least expect it. So, any help will be gratefully received.

  • v3.2.0-beta Changes

    June 20, 2019

    ๐Ÿš€ Included in this release are performance improvements for Option<A>, OptionUnsafe<A>, and Seq<A>. Seq<A> has now also become a struct to stop the usage of null but also to facilitate the new performance improvements. ISeq<A> will eventually be deprecated and so there are now [Obsolete] warnings - but I won't kill it for quite a long time, I just want to discourage its usage.

    ๐Ÿš€ I'm doing this as a beta release because the changes to Seq<A> are enormous - well, a complete re-write. It is now entirely lock free and internally has three possible states: Lazy, Strict, and Empty - which allows for improved performance for the happy paths.

    ๐ŸŽ I will keep language-ext in beta for a while as I go through the library optimising what I believe are the most used features. I am doing this now because I feel the library is settling down now - as in there's less churn of features and interfaces - and so now is a good time to start squeezing the most performance out of it.

    ๐Ÿšง Mostly this will involve trying to reduce memory allocations where possible as well as inlining operations that make unnecessary calls (which were there to keep everything super generic under the hood). This will make long term maintenance a bit harder, which is why it needed to wait until now.

    ๐ŸŽ Performance timings

    ๐ŸŽ Below are some tests I've used to tweak the performance, the timings are all relative from the old Seq to the new, so my machine's spec shouldn't matter.

    Streaming a lazy sequence

    ๐ŸŽ This performance test wraps a lazy enumerable in a Seq and then streams them in one-by-one via the foreach loop and saves the value in a results array.

    var seq = Seq(Range(0, count));foreach(var item in seq) { results[j] = item; j++; }
    
    Old Seq<A> New Seq<A>
    138 ns / op 54 ns / op

    ๐ŸŽ So a performance improvement of 2.55 times. Evaluating an IEnumerable<T> lazy sequence is about 29 ns / op which whilst faster than Seq<A> doesn't have the memoisation that Seq has. (or any of the other cool features, for that matter)

    Streaming a strict sequence

    A lot of the time I find I'm working with strict sequences - i.e. non-lazy. This is the happy path for sequences and a lot of optimisations can happen if the code knows the sequence isn't lazy

    var seq = Seq(Range(0, count));seq = seq.Strict();foreach (var item in seq) { results[j] = item; j++; }
    
    Old Seq<A> New Seq<A>
    16.1 ns / op 10.9 ns / op

    ๐ŸŽ A performance improvement of ~32.3%. This isn't quite as significant as the lazy stream improvements, but interestingly this is now faster than List<T> from the BCL, which measures around 12.5 ns / op.

    โž• Adding an item to the end of a Seq

    Essentially calling this, many times:

    seq = seq.Add(value);
    
    Old Seq<A> New Seq<A>
    49.5 ns / op 33.2 ns / op

    Around a 33% improvement. This isn't quite as fast as the 10.9 ns / op of the BCL's List<T>, I will work on this some more. But this is an immutable, thread-safe, data structure - which the BCL List<T> definitely isn't.

    โž• Adding an item to the beginning of a Seq

    Essentially calling this, many times:

    seq = value.Cons(seq);
    
    Old Seq<A> New Seq<A>
    40 ns / op 20 ns / op

    ๐ŸŽ Twice as fast. This isn't far off List<T>.Add - and so that's why I think I can make Seq<A>.Add gain a bit more speed. Note: the BCL's List<T>.Insert(0, x) which is the equivalent to Seq<A>.Cons has terrible performance at 10310 ns / op. So that's one to look out for!

    Other Seq<A> functions

    Most Seq<A> functions will either evaluate a lazy stream, a strict stream, or Add or Cons items. And so nearly all Seq<A> related functionality will gain due to these changes.

    Option<A> and OptionUnsafe<A>

    ๐ŸŽ I haven't done any performance timings for these types, as most improvements are to reduce unnecessary allocations. I have removed the support for lazy options - if anyone misses them then I'll probably create an OptionLazy<A> type. I'd rather not, but I felt that because Option<A> is probably the most used type in lang-ext then it should always be on the happy path. This has reduced a lot of unnecessary branching and has allowed the internals of Option<A> and OptionUsafe<A> to be simplified.

    Feedback please

    โšก๏ธ Because these are some big changes, if you have an app with lots of unit tests it'd be great if you could verify that they all still run with these updates. I have obviously made sure all of the existing unit tests pass and have built a number of test harnesses to check that everything still works and to verify the thread-safeness of Seq, but changes like these have a habit of kicking you when you least expect it. So, any help will be gratefully received.

  • v3.1.25 Changes

    June 09, 2019

    โšก๏ธ The LanguageExt.CodeGen tool has been updated to spot PascalCase field names that will become C# identifiers when made into camelCase for lenses (and to prepend an @ to the name). So, names like Class, Default, Event, etc. don't cause compilation problems.

    ๐Ÿš€ NOTE: The other LanguageExt.* nu-get packages haven't been deployed. I'm currently working on some optimisations that will need some more testing before release.

  • v3.1.23 Changes

    May 08, 2019

    The [With] and [WithLens] code-generator has been further improved:

    • ๐Ÿ‘Œ Supports generic types
    • ๐Ÿ‘Œ Supports types with constraints
    • Can ascertain the type for With parameters and can therefore ascertain whether it's a value-type or reference-type. This allows for the parameters to use Nullable<A> for value-types, therefore massively helping with implicit type-conversion.
    • โœ‚ Removed WithOpt which was playing the roll of Nullable before the type resolution that's now been added.

    โœ… > NOTE: Pre-C# 8 - you must put a constraint on your generic arguments (where A : struct or whera A : class) to allow for the null-coalescing to work correctly. The null-coalesce operator has been improved in the latest C# and so this requirement isn't needed.

  • v3.1.22 Changes

    May 07, 2019

    ๐Ÿš€ A stupidity bug unfortunately slipped through the net, which has now been fixed with the WithOpt<A> type which was built to provide sensible defaults for the [With] and [WithLens] code-gen. So, if you're using the code-gen features, you're gonna want this release.

  • v3.1.21 Changes

    May 01, 2019

    ๐Ÿ›  The Do operation for running side-effects on monadic computations was running the computations for lazy monads (Try, TryOption, TryAsync, TryOptionAsync, Reader, Writer, State, RWS) twice. This has now been fixed.

    Strict hadn't been implemented for all lazy monadic types. This has now also been rectified.

    โšก๏ธ The LanguageExt.CodeGen library has been updated to v3.1.20 - this will now deal with Option types for fields correctly. Previously, None wasn't handled as a value, but as an absence of a value. So if you use [With] or [WithLens] and you have fields in your record type that use Option<...> then you'll need this update.