Greg suggested in https://enduracode.kilnhg.com/Review/K185700
using Skip(length-n) instead of two reverses. The results were really
interesting.
Here’s our implementations
/// <summary> /// Returns the last <paramref name="n"/> elements. /// </summary> public static IEnumerable<T> TakeLast<T>( this IEnumerable<T> c, int n ) { return c.Reverse().Take( n ).Reverse(); } /// <summary> /// Returns the last <paramref name="n"/> elements. /// </summary> public static IEnumerable<T> TakeLast2<T>( this IEnumerable<T> c, int n ) { var count = c.Count(); return c.Skip( count-n ); }
Here’s our performance test.
[ TestFixture ]
internal class TakeLast {
[ Test ]
public void Test() {
var enumerable = Enumerable.Range( 0, 1000000 );
Console.WriteLine("Takelast");
takelast( enumerable );
takelast( enumerable );
takelast( enumerable );
Console.WriteLine("Takelast2");
takelast2( enumerable );
takelast2( enumerable );
takelast2( enumerable );
Assert.True( true );
}
private static void takelast( IEnumerable<int> enumerable ) {
var stop = Stopwatch.StartNew();
var last3 = enumerable.TakeLast( 10 );
Console.WriteLine( "Took: " + stop.ElapsedTicks );
Console.WriteLine( "Last: " + last3.GetCommaDelimitedStringFromCollection() );
}
private static void takelast2( IEnumerable<int> enumerable ) {
var stop = Stopwatch.StartNew();
var last3 = enumerable.TakeLast2( 10 );
Console.WriteLine( "Took: " + stop.ElapsedTicks );
Console.WriteLine( "Last: " + last3.GetCommaDelimitedStringFromCollection() );
}
}
Wait for it. Here’s the results of executing the test twice.
Takelast
Took: 3364
Last: 999990, 999991, 999992, 999993, 999994, 999995, 999996, 999997, 999998, 999999
Took: 1
Last: 999990, 999991, 999992, 999993, 999994, 999995, 999996, 999997, 999998, 999999
Took: 2
Last: 999990, 999991, 999992, 999993, 999994, 999995, 999996, 999997, 999998, 999999
Takelast2
Took: 17152
Last: 999990, 999991, 999992, 999993, 999994, 999995, 999996, 999997, 999998, 999999
Took: 14391
Last: 999990, 999991, 999992, 999993, 999994, 999995, 999996, 999997, 999998, 999999
Took: 14500
Last: 999990, 999991, 999992, 999993, 999994, 999995, 999996, 999997, 999998, 999999
Try #2
Takelast
Took: 3491
Last: 999990, 999991, 999992, 999993, 999994, 999995, 999996, 999997, 999998, 999999
Took: 1
Last: 999990, 999991, 999992, 999993, 999994, 999995, 999996, 999997, 999998, 999999
Took: 1
Last: 999990, 999991, 999992, 999993, 999994, 999995, 999996, 999997, 999998, 999999
Takelast2
Took: 16823
Last: 999990, 999991, 999992, 999993, 999994, 999995, 999996, 999997, 999998, 999999
Took: 14640
Last: 999990, 999991, 999992, 999993, 999994, 999995, 999996, 999997, 999998, 999999
Took: 14721
Last: 999990, 999991, 999992, 999993, 999994, 999995, 999996, 999997, 999998, 999999
Okay. Skip(length-n) is definitely not faster. What’s way more interesting to
me: why are the next two executions of the first test literally O(1)?