{"id":13677342,"url":"https://github.com/NetFabric/NetFabric.Hyperlinq","last_synced_at":"2025-04-29T11:30:40.573Z","repository":{"id":45708205,"uuid":"164350517","full_name":"NetFabric/NetFabric.Hyperlinq","owner":"NetFabric","description":" High performance LINQ implementation with minimal heap allocations. Supports enumerables, async enumerables, arrays and Span\u003cT\u003e.","archived":false,"fork":false,"pushed_at":"2024-02-14T09:36:43.000Z","size":8658,"stargazers_count":875,"open_issues_count":10,"forks_count":35,"subscribers_count":19,"default_branch":"main","last_synced_at":"2024-11-05T07:34:40.480Z","etag":null,"topics":["array","async-enumerable","buffer-pools","csharp","csharp-library","dotnet","dotnet-core","dotnet-standard","enumeration","heap-allocations","linq","nuget-package","performance","reduced-heap-allocations","simd","span"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NetFabric.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-01-06T22:19:47.000Z","updated_at":"2024-10-18T12:27:27.000Z","dependencies_parsed_at":"2024-01-06T09:58:44.204Z","dependency_job_id":"8ac22c4a-6bd3-4b0c-bb14-8d250a7f1df5","html_url":"https://github.com/NetFabric/NetFabric.Hyperlinq","commit_stats":null,"previous_names":[],"tags_count":45,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetFabric%2FNetFabric.Hyperlinq","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetFabric%2FNetFabric.Hyperlinq/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetFabric%2FNetFabric.Hyperlinq/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetFabric%2FNetFabric.Hyperlinq/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NetFabric","download_url":"https://codeload.github.com/NetFabric/NetFabric.Hyperlinq/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224163538,"owners_count":17266521,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["array","async-enumerable","buffer-pools","csharp","csharp-library","dotnet","dotnet-core","dotnet-standard","enumeration","heap-allocations","linq","nuget-package","performance","reduced-heap-allocations","simd","span"],"created_at":"2024-08-02T13:00:40.695Z","updated_at":"2024-11-11T19:31:32.256Z","avatar_url":"https://github.com/NetFabric.png","language":"C#","readme":"![GitHub last commit (main)](https://img.shields.io/github/last-commit/NetFabric/NetFabric.Hyperlinq/main.svg?style=flat-square\u0026logo=github)\n[![Build and test](https://github.com/NetFabric/NetFabric.Hyperlinq/workflows/Build%20and%20test/badge.svg?style=flat-square)](https://github.com/NetFabric/NetFabric.Hyperlinq/actions)\n[![Coverage](https://img.shields.io/coveralls/github/NetFabric/NetFabric.Hyperlinq/main?style=flat-square\u0026logo=coveralls)](https://coveralls.io/github/NetFabric/NetFabric.Hyperlinq)\n[![NuGet Version](https://img.shields.io/nuget/v/NetFabric.Hyperlinq.svg?style=flat-square\u0026logo=nuget)](https://www.nuget.org/packages/NetFabric.Hyperlinq/)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/NetFabric.Hyperlinq.svg?style=flat-square\u0026logo=nuget)](https://www.nuget.org/packages/NetFabric.Hyperlinq/) \n[![Gitter](https://img.shields.io/gitter/room/netfabric/netfabric.hyperlinq?style=flat-square\u0026logo=gitter)](https://gitter.im/NetFabric/NetFabric.Hyperlinq)\n\n\n# NetFabric.Hyperlinq\n\n`NetFabric.Hyperlinq` contains alternative implementations of many operations found in the `System.Linq` namespace:\n\n- Uses value-types to improve performance by making method calls non-virtual and reducing GC collections by not allocating on the heap. \n- Adds support for `Span\u003c\u003e`, `ReadOnlySpan\u003c\u003e`, `Memory\u003c\u003e` and `ReadOnlyMemory\u003c\u003e`.\n- [Nullable reference type](https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references) annotations.\n- One single NuGet package support for both sync and async enumerables.\n\nThis implementation **favors performance in detriment of assembly binary size** (lots of overloads).\n\n## Contents\n\n- [Fast enumeration](#fast-enumeration)\n- [Reduced heap allocations](#reduced-heap-allocations)\n- [Benchmarks](#benchmarks)\n- [Usage](#usage)\n  - [Value delegates](#value-delegates)\n  - [Generation operations](#generation-operations)\n  - [Method return types](#method-return-types)\n  - [Composition](#composition)\n  - [Option](#option)\n  - [Buffer pools](#buffer-pools)\n  - [SIMD](#simd)\n- [Documentation](#documentation)\n- [Supported operations](#supported-operations)\n- [References](#references)\n- [Credits](#credits)\n- [License](#license)\n\n## Fast enumeration\n\n`NetFabric.Hyperlinq` can enumerate faster the results of a query than `System.Linq` by performing all of the following:\n\n- Merges multiple enumerators into a single one for several more scenarios.\n- It does not box value-type enumerators so, calls to the `Current` property and the `MoveNext()` method are non-virtual.\n- All the enumerables returned by operations define a value-type enumerator.\n- Whenever possible, the enumerator returned by the public `GetEnumerator()` or `GetAsyncEnumerator()` does not implement `IDisposable`. This allows the `foreach` that enumerates the result to be inlinable. \n- Operations enumerate the source using the indexer when the source is an array, `ArraySegment\u003c\u003e`, `Span\u003c\u003e`, `ReadOnlySpan\u003c\u003e`, `Memory\u003c\u003e`, `ReadOnlyMemory\u003c\u003e`, or implements `IReadOnlyList\u003c\u003e`. \n- `Range()` and `Repeat()` return enumerables that implement `IReadOnlyCollection\u003c\u003e` and `ICollection\u003c\u003e`. `Return()` and `Select()` return enumerables that implement `IReadOnlyList\u003c\u003e` and `IList\u003c\u003e`.\n- Use of buffer pools in operations like `Distinct()`, `ToArray()` and `ToList()`.\n- Use of SIMD in `Sum()` and `SelectVector()`.\n- Elimination of conditional branchs in `Where().Count()`.\n- Allows the JIT compiler to perform optimizations on array enumeration whenever possible.\n- Takes advantage of `EqualityComparer\u003c\u003e.Default` devirtualization whenever possible.\n\nThe performance is equivalent when the enumerator is a reference-type. This happens when the enumerable is generated using `yield` or when it's cast to one of the BCL enumerable interfaces (`IEnumerable`, `IEnumerable\u003c\u003e`, `IReadOnlyCollection\u003c\u003e`, `ICollection\u003c\u003e`, `IReadOnlyList\u003c\u003e`, `IList\u003c\u003e`, or `IAsyncEnumerable\u003c\u003e`). In the case of operation composition, this only affects the first operation. The subsequent operations will have value-type enumerators.\n\n## Reduced heap allocations\n\n`NetFabric.Hyperlinq` allocates as much as possible on the stack. Enumerables and enumerators are defined as value-types. Generics constraints are used for the operation parameters so that the value-types are not boxed.\n\nIt only allocates on the heap for the following cases:\n\n- Operations that use `ICollection\u003c\u003e.CopyTo()`, `ICollection\u003c\u003e.Contains()`, or `IList\u003c\u003e.IndexOf()` will box enumerables that are value-types.   \n- `ToArray()` and `ToList()` allocate their results on the heap. You can use the `ToArray()` overload that take an buffer pool as parameter so that its result is not managed by the garbage collector.\n\n## Benchmarks\n\nThe results of the benchmarks comparing multiple LINQ libraries can be found in the [LinqBenchmarks](https://github.com/NetFabric/LinqBenchmarks) repository. \n\nThe results of the benchmarks included in this repository can be found in the [Benchmarks](https://github.com/NetFabric/NetFabric.Hyperlinq/tree/main/Benchmarks) folder. \n\nThe names of the benchmarks are structured as follow:\n\n- The library used:\n  - _Linq_ - the `System.Linq` namespace (includes [System.Linq.Async](https://www.nuget.org/packages/System.Linq.Async/), [System.Interactive](https://www.nuget.org/packages/System.Interactive/), and [System.Interactive.Async](https://www.nuget.org/packages/System.Interactive.Async/))\n  - _StructLinq_ - [StructLinq](https://www.nuget.org/packages/StructLinq/)\n  - _Hyperlinq_ - [NetFabric.Hyperlinq](https://www.nuget.org/packages/NetFabric.Hyperlinq/)\n- The type of collection used as source:\n  - _Array_ - an array\n  - _Span_ - a `Span\u003c\u003e`\n  - _Memory_ - a `Memory\u003c\u003e`\n  - _Enumerable_ - implements `IEnumerable\u003c\u003e`\n  - _Collection_ - implements `IReadOnlyCollection\u003c\u003e` and `ICollection\u003c\u003e`\n  - _List_ - implements `IReadOnlyList\u003c\u003e` and `IList\u003c\u003e` but is not an array\n  - _AsyncEnumerable_ - implements `IAsyncEnumerable\u003c\u003e`\n- The type of enumerator provided by the source:\n  - _Value_ - the enumerator is a value type\n  - _Reference_ - the enumerator is a reference type\n- How the result of the operation is iterated:\n  - _For_ - a `for` loop is used to call the indexer\n  - _Foreach_ - a `foreach` loop is used to call the enumerator\n- Has a variant:\n  - _SIMD_ - using SIMD\n\n\n## Usage\n\n- Add the [`NetFabric.Hyperlinq` NuGet package](https://www.nuget.org/packages/NetFabric.Hyperlinq/) to your project.\n- Optionally, also add the [`NetFabric.Hyperlinq.Analyzer` NuGet package](https://www.nuget.org/packages/NetFabric.Hyperlinq.Analyzer/) to your project. It's a Roslyn analyzer that suggests performance improvements on your enumeration source code. No dependencies are added to your assemblies.\n- Add an `using NetFabric.Hyperlinq` directive to all source code files where you want to use `NetFabric.Hyperlinq`. It can coexist with `System.Linq` and `System.Linq.Async` directives:\n\n``` csharp\nusing System;\nusing System.Linq;\nusing NetFabric.Hyperlinq; // add this directive\n```\n\n- Use the methods `AsValueEnumerable()` to make any collection usable with `NetFabric.Hyperlinq`. This includes arrays, `Memory\u003c\u003e`, `ReadOnlyMemory\u003c\u003e`, `Span\u003c\u003e`, `ReadOnlySpan\u003c\u003e`, BCL collections, and any other implementation of `IEnumerable\u003c\u003e`. Use `AsAsyncValueEnumerable()` for any implementation of `IAsyncEnumerable\u003c\u003e`.\n\n``` csharp\npublic static void Example(IReadOnlyList\u003cint\u003e list)\n{\n  var result = list\n    .AsValueEnumerable()\n    .Where(item =\u003e item \u003e 2)\n    .Select(item =\u003e item * 2);\n\n  foreach(var value in result)\n    Console.WriteLine(value);\n}\n```\n\n- `Netfabric.Hyperlinq` contains special versions of `AsValueEnumerable()` for better performance with all collections in the `System.Collections.Immutable` namespace. Projects targetting .NET Framework, `netcoreapp2.1` or `netstandard2.0`, require the addition of the [`NetFabric.Hyperlinq.Immutable` NuGet package](https://www.nuget.org/packages/NetFabric.Hyperlinq.Immutable/) dependency.\n\n- Most enumerables returned by `NetFabric.Hyperlinq` are compatible with `System.Linq`. The exception is enumerables for `Span\u003c\u003e` or `ReadOnlySpan\u003c\u003e`.\n\nThis allows the use of `System.Linq` operators on `NetFabric.Hyperlinq` enumerables. `OrderByDescending()` is not yet available in `Netfabric.Hyperlinq` but can still be used without requiring any conversion:\n\n``` csharp\npublic static void Example(IReadOnlyList\u003cint\u003e list)\n{\n  var result = list\n    .AsValueEnumerable()\n    .Where(item =\u003e item \u003e 2)\n    .OrderByDescending(item =\u003e item) // is not yet available in Netfabric.Hyperlinq\n    .AsValueEnumerable()\n    .Select(item =\u003e item * 2);\n\n  foreach(var value in result)\n    Console.WriteLine(value);\n}\n```\n\nTo add `NetFabric.Hyperlinq` operations after a `System.Linq` operation, simply add one more `AsValueEnumerable()` or `AsAsyncValueEnumerable()`.\n\n### Value delegates\n\nCalling a lambda expression for each item of the collection is very expensive. `NetFabric.Hyperlinq` supports an alternative that is not as practical but that has much better performance.\n\n- Declare a `struct` that implements `IFunction\u003c\u003e`. Here's two examples of how to implement:\n\n``` csharp\nreadonly struct MultiplyBy2\n    : IFunction\u003cint, int\u003e\n{\n    public int Invoke(int element)\n        =\u003e element * 2;\n}\n\nreadonly struct LessThan\n    : IFunction\u003cint, bool\u003e\n{\n\treadonly int value;\n\t\n    public LessThan(int value)\n\t\t=\u003e this.value = value;\n    \n    public bool Invoke(int element)\n        =\u003e element \u003c value;\n}\n```\n\n- Pass an instance as a parameter or just add the type to the generics arguments list (uses the default constructor):\n\n``` csharp\npublic static void Example(IReadOnlyList\u003cint\u003e list)\n{\n  var result = list\n    .AsValueEnumerable()\n    .Where(new LessThan(10))\n    .Select\u003cint, MultiplyBy2\u003e();\n\n  foreach(var value in result)\n    Console.WriteLine(value);\n}\n```\n\nThe instances are allocated on the stack and the methods calls are non-virtual. \n\n### Generation operations\n\nIn `NetFabric.Hyperlinq`, the generation operations like `Empty()`, `Range()`, `Repeat()` and `Return()` are static methods implemented in the static class `ValueEnumerable`. To use them, instead of the `System.Linq` equivalents, simply use `ValueEnumerable` instead of `Enumerable`.\n\n``` csharp\npublic static void Example(int count)\n{\n  var source = ValueEnumerable\n    .Range(0, count)\n    .Select(item =\u003e item * 2);\n\n  foreach(var value in source)\n    Console.WriteLine(value);\n}\n```\n\n### Composition\n\n`NetFabric.Hyperlinq` operations can be composed just like with `System.Linq`. The difference is on how each one optimizes the internals to reduce the number of enumerators required to iterate the values.\n\nBoth `System.Linq` and `NetFabric.Hyperlinq` optimize the code in the following example so that only one enumerator is used to perform both the `Where()` and the `Select()`:\n\n``` csharp\nvar result = source.AsValueEnumerable()\n    .Where(item =\u003e item \u003e 2)\n    .Select(item =\u003e item * 2);\n```\n\n`NetFabric.Hyperlinq` includes many more composition optimizations. In the following code, only one enumerator is used:\n\n``` csharp\nvar result = array.AsValueEnumerable()\n    .Skip(1)\n    .Take(10)\n    .Where(item =\u003e item \u003e 2)\n    .Select(item =\u003e item * 2)\n    .First();\n```\n\n### Option\n\nIn `System.Linq`, the aggregation operations like `First()`, `Single()` and `ElementAt()`, throw an exception when the source has no items. Often, empty collections are a valid scenario and exception handling is very slow. `System.Linq` has alternative methods like `FirstOrDefault()`, `SingleOrDefault()` and `ElementAtOrDefault()`, that return the `default` value instead of throwing. This is still an issue when the items are of a value-type, where there's no way to distinguish between an empty collection and a valid item.\n\nIn `NetFabric.Hyperlinq`, aggregation operations return an `Option\u003c\u003e` type. This is similar in behavior to the [`Nullable\u003c\u003e`](https://docs.microsoft.com/en-us/dotnet/api/system.nullable-1) but it can contain reference types. \n\nHere's a small example using `First()`:\n\n``` csharp\nvar result = source.AsValueEnumerable().First();\nif (result.IsSome)\n  Console.WriteLine(result.Value);\n```\n\nIt also provides a deconstructor so, you can convert it to a tuple:\n\n``` csharp\nvar (isSome, value) = source.AsValueEnumerable().First();\nif (isSome)\n  Console.WriteLine(value);\n```\n\nIf you prefer a more functional approach, you can use `Match()` to specify the value returned when the collection has values and when it's empty. Here's how to use it to define the previous behavior of `First()` and `FirstOrDefault()`:\n\n``` csharp\nvar first = source.AsValueEnumerable()\n  .First()\n  .Match(\n    item =\u003e item,\n    () =\u003e throw new InvalidOperationException(\"Sequence contains no elements\"));\n\nvar firstOrDefault = source.AsValueEnumerable()\n  .First()\n  .Match(\n    item =\u003e item,\n    () =\u003e default);\n\nConsole.WriteLine(first);\nConsole.WriteLine(firstOrDefault);\n```\n\n`Match()` can also be used to define actions:\n\n``` csharp\nsource.AsValueEnumerable()\n  .First()\n  .Match(\n    item =\u003e Console.WriteLine(item),\n    () =\u003e { });\n```\n\nThe `NetFabric.Hyperlinq` operations can be applied to `Option\u003c\u003e`, including `Where()`, `Select()` and `SelectMany()`. These return another `Option\u003c\u003e` with the predicate/selector applied to the value, if it exists.\n\n```csharp\nsource.AsValueEnumerable()\n  .First()\n  .Where(item =\u003e item \u003e 2)\n  .Match(\n    item =\u003e Console.WriteLine(item),\n    () =\u003e { });\n```\n\n### Buffer pools\n\n[Buffer pools](https://adamsitnik.com/Array-Pool/) allow the use of heap memory without adding pressure to the garbage collector. It pre-allocates a chunk of memory and \"rents\" it as required. The garbage collector will add this memory to the Large Object Heap (LOH).\n\n`ToArray()` is frequently used to cache values for a brief period and the use of buffer pools may be useful.\n\n`Netfabric.Hyperlinq` adds an overload that takes a `ArrayPool\u003c\u003e` as a parameter:\n\n``` csharp\nvoid Method()\n{\n  using var buffer = source.AsValueEnumerable()\n      .ToArray(ArrayPool\u003cint\u003e.Shared);\n  var memory = buffer.Memory;\n  // use memory here\n}\n```\n\nIt returns an instance of a [`IMemoryOwner\u003c\u003e`](https://docs.microsoft.com/en-us/dotnet/api/system.buffers.imemoryowner-1). The `using` statement guarantees that it is disposed and the buffer automatically returned to the pool. \n\n### SIMD\n\n`NetFabric.Hyperlinq` uses SIMD implicitly to improve performance of some operations. Many times this can only be done explicitly because it can only be used on a limited number of types and operations on them.\n\nIn `NetFabric.Hyperlinq`, alternative methods that use SIMD, have the word `Vector` at the end of its name. You'll also find that the operations that require an expression, now require two.\n\n``` csharp\nvar result = list\n  .AsValueEnumerable()\n  .SelectVector(item = item * 2, item = item * 2)\n  .ToArray();\n```\n\nSIMD improves performance by performing the same operations simultaneously on multiple items. The operation can only be performed if this number is met. This means that the remaining number of items has to be processed without SIMD. The first expression is applied on a [`System.Numerics.Vector\u003c\u003e`](https://docs.microsoft.com/en-us/dotnet/api/system.numerics.vector-1), while the second one is applied on an item.\n\nThese methods also support the use of [value delegates](#value-delegates). In this case, the `struct` containing the expressions must implement two `IFunction\u003c,\u003e`: \n\n``` csharp\nreadonly struct MultiplyBy2\n    : IFunction\u003cVector\u003cint\u003e, Vector\u003cint\u003e\u003e\n    , IFunction\u003cint, int\u003e\n{\n    public Vector\u003cint\u003e Invoke(Vector\u003cint\u003e element)\n        =\u003e element * 2;\n\n    public int Invoke(int element)\n        =\u003e element * 2;\n}\n\npublic static void Example(List\u003cint\u003e list)\n{\n  var result = list\n    .AsValueEnumerable()\n    .SelectVector\u003cint, MultiplyBy2\u003e()\n    .ToArray();\n\n  foreach(var value in result)\n    Console.WriteLine(value);\n}\n```\n\nPlease note that the operation `SelectVector()` does not return an enumerable. It returns the context required for subsequent operations like `ToArray()`, `ToList()`, and `Sum()`. \n\nUp until now I haven't found a way to improve the performance of `Select()` by using SIMD. The returned context allows the use of composition, exposing only the operations that can gain with the use of SIMD.  \n\n## Documentation\n\nArticles explaining implementation:\n\n- [Optimizing LINQ](https://medium.com/@antao.almada/netfabric-hyperlinq-optimizing-linq-348e02566cef)\n- [Generation Operations](https://medium.com/@antao.almada/netfabric-hyperlinq-generation-operations-6530826a70ca)\n- [Select Operation](https://medium.com/@antao.almada/netfabric-hyperlinq-select-operation-e4ac2bbfb187)\n- [Zero Allocation](https://medium.com/@antao.almada/netfabric-hyperlinq-zero-allocation-fe5d0dd6b1a6)\n\n## Supported operations\n\n- Aggregation\n  - `Count()`\n  - `Sum()`\n- Conversion\n  - `AsEnumerable()`\n  - `AsValueEnumerable()`\n  - `ToArray()`\n  - `ToList()`\n  - `ToDictionary(Func\u003cTSource, TKey\u003e)`\n  - `ToDictionary(Func\u003cTSource, TKey\u003e, IEqualityComparer\u003cTKey\u003e)`\n  - `ToDictionary(Func\u003cTSource, TKey\u003e, Func\u003cTSource, TElement\u003e)`\n  - `ToDictionary(Func\u003cTSource, TKey\u003e, Func\u003cTSource, TElement\u003e, IEqualityComparer\u003cTKey\u003e)`\n- Element\n  - `ElementAt()`\n  - `First()`\n  - `Single()`\n- Filtering\n  - `Where(Func\u003cTSource, bool\u003e)`\n  - `Where(Func\u003cTSource, int, bool\u003e)`\n- Generation\n  - `Create(Func\u003cTEnumerator\u003e)`\n  - `Empty()`\n  - `Range(int, int)`\n  - `Repeat(TSource, int)`\n  - `Return(TSource)`\n- Projection\n  - `Select(Func\u003cTSource, TResult\u003e)`\n  - `Select(Func\u003cTSource, int, TResult\u003e)`\n  - `SelectMany(IValueEnumerable\u003cTSource\u003e)`\n- Partitioning\n  - `Take(int)`\n  - `Skip(int)`\n- Quantifier\n  - `All(Func\u003cTSource, bool\u003e)`\n  - `All(Func\u003cTSource, int, bool\u003e)`\n  - `Any()`\n  - `Any(Func\u003cTSource, bool\u003e)`\n  - `Any(Func\u003cTSource, int, bool\u003e)`\n  - `Contains(TSource)`\n  - `Contains(TSource, IEqualityComparer\u003cTSource\u003e)`\n- Set\n  - `Distinct(TSource)`\n  - `Distinct(TSource, IEqualityComparer\u003cTSource\u003e)`\n\n## References\n\n- [Enumeration in .NET](https://blog.usejournal.com/enumeration-in-net-d5674921512e) by Antão Almada\n- [Performance of value-type vs reference-type enumerators](https://medium.com/@antao.almada/performance-of-value-type-vs-reference-type-enumerators-820ab1acc291) by Antão Almada\n- [Performance Tuning for .NET Core](https://reubenbond.github.io/posts/dotnet-perf-tuning) by Reuben Bond\n- [ValueLinqBenchmarks](https://gist.github.com/benaadams/294cbd41ec1179638cb4b5495a15accf) by Ben Adams\n- [C# - How method calling works](http://www.levibotelho.com/development/how-method-calling-works/) by Levi Botelho\n- [Improving .NET Disruptor performance — Part 2](https://medium.com/@ocoanet/improving-net-disruptor-performance-part-2-5bf456cd595f) by Olivier Coanet\n- [Optimizing string.Count all the way from LINQ to hardware accelerated vectorized instructions](https://medium.com/@SergioPedri/optimizing-string-count-all-the-way-from-linq-to-hardware-accelerated-vectorized-instructions-186816010ad9) by Sergio Pedri \n- [Simulating Return Type Inference in C#](https://tyrrrz.me/blog/return-type-inference) by Alexey Golub\n- [Pooling large arrays with ArrayPool](https://adamsitnik.com/Array-Pool/) by Adam Sitnik\n\n## Credits\n\nThe following open-source projects are used to build and test this project:\n\n- [.NET](https://github.com/dotnet)\n- [BenchmarkDotNet](https://benchmarkdotnet.org/)\n- [coveralls](https://coveralls.io)\n- [coverlet](https://github.com/tonerdo/coverlet)\n- [ILRepack](https://github.com/gluck/il-repack)\n- [ILRepack.MSBuild.Task](https://github.com/peters/ILRepack.MSBuild.Task)\n- [NetFabric.Assertive](https://github.com/NetFabric/NetFabric.Assertive)\n- [NetFabric.Hyperlinq.Analyzer](https://github.com/NetFabric/NetFabric.Hyperlinq.Analyzer)\n- [Nullable](https://github.com/manuelroemer/Nullable)\n- [Source Link](https://github.com/dotnet/sourcelink)\n- [xUnit.net](https://xunit.net/)\n\n## License\n\nThis project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info.\n","funding_links":[],"categories":["C#","Projects using custom Source Generators \"internally\""],"sub_categories":["Other"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNetFabric%2FNetFabric.Hyperlinq","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FNetFabric%2FNetFabric.Hyperlinq","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNetFabric%2FNetFabric.Hyperlinq/lists"}