{"id":20153687,"url":"https://github.com/anupsarkar-dev/csharp-loop-benchmark","last_synced_at":"2026-03-06T00:30:54.399Z","repository":{"id":135783954,"uuid":"597011945","full_name":"anupsarkar-dev/CSharp-Loop-Benchmark","owner":"anupsarkar-dev","description":null,"archived":false,"fork":false,"pushed_at":"2023-02-03T14:03:31.000Z","size":21,"stargazers_count":1,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-06T11:50:31.971Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/anupsarkar-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2023-02-03T12:35:58.000Z","updated_at":"2023-04-09T14:30:16.000Z","dependencies_parsed_at":null,"dependency_job_id":"d4d4688e-59e4-4535-8baa-21255e82d749","html_url":"https://github.com/anupsarkar-dev/CSharp-Loop-Benchmark","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/anupsarkar-dev/CSharp-Loop-Benchmark","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anupsarkar-dev%2FCSharp-Loop-Benchmark","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anupsarkar-dev%2FCSharp-Loop-Benchmark/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anupsarkar-dev%2FCSharp-Loop-Benchmark/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anupsarkar-dev%2FCSharp-Loop-Benchmark/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/anupsarkar-dev","download_url":"https://codeload.github.com/anupsarkar-dev/CSharp-Loop-Benchmark/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anupsarkar-dev%2FCSharp-Loop-Benchmark/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30156285,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T22:39:40.138Z","status":"ssl_error","status_checked_at":"2026-03-05T22:39:24.771Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2024-11-13T23:20:31.799Z","updated_at":"2026-03-06T00:30:54.301Z","avatar_url":"https://github.com/anupsarkar-dev.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"High Performance Loop in C#\n===========================\n\nLoop is one of the most important feature in every programming language. C# provides various of ways to iterate a collection. Most common ways to loop through a list or a array is  \"For, While, ForEach\" loop. There are several others way to iterate a collection using Linq, Parallel ForEach \u0026 Span. Lets do some benchmarking and see the actual performance.\n\nTo test the performance , I am going to use a list of integers. Size of this interger list will be 100,10000,100000,1000000 elements. We will benchmark each sizes using BenchmarkDotNet.\n\n```\n\\[Params(100,10000,100000,1000000)\\]  \npublic int size { get; set; }  \n  \nprivate List\u003cint\u003e items = new();  \n  \n\\[GlobalSetup\\]  \npublic void InitList()  \n{  \n    items = Enumerable.Range(1, size).Select(x =\u003e random.Next()).ToList();  \n}\n```\n\nHere I have defined individual methods for each looping.\n\nFor\n\n```\n\\[Benchmark\\]  \npublic void For()  \n{  \n    for (int i = 0; i \u003c items.Count; i++)  \n    {  \n        var item = items\\[i\\];  \n    }  \n}\n```\n\nWhile\n\n```\n\\[Benchmark\\]  \npublic void While()  \n{  \n    var i = 0;  \n    while (i \u003c items.Count)  \n    {  \n        var item = items\\[i\\];  \n        i++;  \n    }  \n}\n```\n\nForEach\n\n```\n  \n\\[Benchmark\\]  \npublic void ForEach()  \n{  \n    foreach (var item in items)  \n    {  \n    }  \n}\n```\n\nForEach Linq\n\n```\n\\[Benchmark\\]  \npublic void Foreach\\_Linq()  \n{  \n    items.ForEach(item =\u003e  \n    {  \n  \n    });  \n}\n```\n\nParallel ForEach\n\n```\n \\[Benchmark\\]  \n    public void Parallel\\_ForEach()  \n    {  \n        Parallel.ForEach(items, item =\u003e  \n        {  \n  \n        });  \n    }\n```\n\nParallel Linq\n\n```\n \\[Benchmark\\]  \n    public void Parallel\\_Linq()  \n    {  \n        items.AsParallel().ForAll(item =\u003e  \n        {  \n  \n        });  \n    }\n```\n\nFor Span\n\n```\n\\[Benchmark\\]  \npublic void For\\_Span()  \n{  \n    var asSpanList = CollectionsMarshal.AsSpan(items);  \n  \n    for (var i=0;i\u003c asSpanList.Length;i++)  \n    {  \n        var item = asSpanList\\[i\\];  \n    }  \n}\n```\n\nForEach Span\n\n```\n\\[Benchmark\\]  \npublic void Foreach\\_Span()  \n{  \n    foreach (var item in CollectionsMarshal.AsSpan(items))  \n    {  \n  \n    }  \n}\n```\n\n**Benchmark Results :**\n=======================\n\nRunning the result in following hardware :\n``` ini\n\nBenchmarkDotNet=v0.13.4, OS=Windows 11 (10.0.22000.1455/21H2)\nAMD Ryzen 7 5800H with Radeon Graphics, 1 CPU, 16 logical and 8 physical cores\n.NET SDK=7.0.102\n  [Host]     : .NET 6.0.13 (6.0.1322.58009), X64 RyuJIT AVX2 [AttachedDebugger]\n  DefaultJob : .NET 6.0.13 (6.0.1322.58009), X64 RyuJIT AVX2\n\n\n```\n|           Method |    size |            Mean |         Error |        StdDev | Allocated |\n|----------------- |-------- |----------------:|--------------:|--------------:|----------:|\n|              **For** |     **100** |        **46.67 ns** |      **1.165 ns** |      **3.417 ns** |         **-** |\n|            While |     100 |        47.51 ns |      1.217 ns |      3.588 ns |         - |\n|          ForEach |     100 |        77.41 ns |      1.394 ns |      1.236 ns |         - |\n|     Foreach_Linq |     100 |       173.88 ns |      1.106 ns |      1.034 ns |         - |\n| Parallel_ForEach |     100 |     5,731.73 ns |     86.697 ns |     72.396 ns |    2586 B |\n|    Parallel_Linq |     100 |    32,550.63 ns |    637.052 ns |    933.783 ns |    7544 B |\n|         For_Span |     100 |        28.48 ns |      0.579 ns |      0.752 ns |         - |\n|     Foreach_Span |     100 |        28.44 ns |      0.497 ns |      0.440 ns |         - |\n|              **For** |   **10000** |     **3,624.66 ns** |     **71.397 ns** |     **76.394 ns** |         **-** |\n|            While |   10000 |     3,629.78 ns |     51.335 ns |     42.867 ns |         - |\n|          ForEach |   10000 |     7,193.93 ns |     72.673 ns |     67.978 ns |         - |\n|     Foreach_Linq |   10000 |    17,036.08 ns |    301.575 ns |    267.338 ns |         - |\n| Parallel_ForEach |   10000 |    38,179.41 ns |    199.711 ns |    177.039 ns |    4374 B |\n|    Parallel_Linq |   10000 |    44,337.14 ns |    671.524 ns |    628.144 ns |    7544 B |\n|         For_Span |   10000 |     2,347.31 ns |     12.294 ns |     11.500 ns |         - |\n|     Foreach_Span |   10000 |     2,338.86 ns |      4.766 ns |      3.980 ns |         - |\n|              **For** |  **100000** |    **35,951.16 ns** |    **235.119 ns** |    **208.427 ns** |         **-** |\n|            While |  100000 |    35,876.83 ns |    336.323 ns |    314.597 ns |         - |\n|          ForEach |  100000 |    71,685.42 ns |    932.394 ns |    872.162 ns |         - |\n|     Foreach_Linq |  100000 |   168,568.65 ns |  1,120.527 ns |  1,048.142 ns |         - |\n| Parallel_ForEach |  100000 |   150,327.02 ns |  2,552.659 ns |  2,387.759 ns |    5651 B |\n|    Parallel_Linq |  100000 |   161,650.69 ns |  1,610.477 ns |  1,344.822 ns |    7568 B |\n|         For_Span |  100000 |    23,767.82 ns |    463.648 ns |    496.099 ns |         - |\n|     Foreach_Span |  100000 |    23,353.36 ns |    114.071 ns |     95.255 ns |         - |\n|              **For** | **1000000** |   **358,105.68 ns** |  **1,191.449 ns** |  **1,056.188 ns** |         **-** |\n|            While | 1000000 |   357,501.54 ns |  1,257.940 ns |    982.117 ns |         - |\n|          ForEach | 1000000 |   713,361.81 ns |  1,544.057 ns |  1,289.358 ns |       1 B |\n|     Foreach_Linq | 1000000 | 1,682,066.91 ns |  2,430.385 ns |  1,897.485 ns |       1 B |\n| Parallel_ForEach | 1000000 |   875,492.44 ns | 16,617.031 ns | 26,356.371 ns |    5737 B |\n|    Parallel_Linq | 1000000 | 1,128,220.79 ns | 20,107.288 ns | 17,824.587 ns |    7581 B |\n|         For_Span | 1000000 |   233,805.18 ns |  1,804.816 ns |  1,599.923 ns |         - |\n|     Foreach_Span | 1000000 |   236,692.77 ns |  4,434.790 ns |  4,148.305 ns |         - |\n\n\nLets see the result which is very interesting.\n\n**List Size : 100 Elements**\n============================\n\nLets analyze the results. We can see from the result the lowest time taken to iterate 100 elements is 28.44 ns by **For Span**. After then comes for loop which took almost 46.67. Also very interesting to see **Parallel Foreach** takes so much longer 5,731.73 ns (5.7 seconds) . Also in case of **Parallel Linq** took almost 32 seconds. Furthermore they allocated some memory also.\n\n**Winner : For Span (**28.44 ns)\n\n**List Size : 10000 Elements**\n==============================\n\nThis time the winner is ForEach Span.\n\n**Winner : ForEach Span (**2,338.86 ns)\n\nList Size : 100000 Elements\n===========================\n\nOne things is noticible here is that the performance of For/ForEach span over the collection size quite predictable and dependable.\n\n**Again the Winner : ForEach Span (**23,353.36 ns)\n\nList Size : 1000000 Elements\n============================\n\n**Again the Winner : For Span (**233,805.18 ns)\n\nIn every aspect For Span is the winner.\n\nWhy is so faster than any other method? Lets deep drive.\n\nUnderstanding Span\u003cT\u003e in C#\n===========================\n\nA `Span\u003c\u003e` is an allocation-free representation of contiguous regions of arbitrary memory. `Span\u003c\u003e` is implemented as a `ref struct` object that contains a `ref` to an object `T` and a length. This means that a Span in C# will always be allocated to stack memory, not heap memory. LetΓÇÖs consider this simplified implementation of `Span\u003c\u003e`:\n\n```\npublic readonly ref struct Span\u003cT\u003e  \n{  \n    private readonly ref T \\_pointer;  \n    private readonly int \\_length;  \n}\n```\n\nUsing `Span\u003c\u003e` leads to performance increases because they are always allocated on the stack. Since garbage collection does not have to suspend execution to clean up objects with no references on the heap as often the application runs faster. Pausing an application to collect garbage is always an expensive operation and should be avoided if possible. `Span\u003c\u003e` operations can be as efficient as operations on arrays. Indexing into a span does not require computation to determine the memory address to index to.\n\nAnother implementation of a Span in C# is `ReadOnlySpan\u003c\u003e`. It is a struct exactly like `Span\u003c\u003e` other than that its indexer returns a `readonly ref T,` not a `ref T`. This allows us to use `ReadOnlySpan\u003c\u003e` to represent immutable data types such as `String`.\n\nSpans can use other value types such as `int`, `byte`, `ref structs`, `bool`, and `enum`. Spans can not use types like `object`, `dynamic`, or `interfaces`.\n\nSpan Limitations\n================\n\nSpan\u003cT\u003e implementation limits its use in code, but conversely, it provides span useful properties.\n\nThe compiler allocates reference type objects on the heap which means **we cannot use spans as fields in reference types.** More specifically `ref struct` objects cannot be boxed like other value-type objects. For the same reason, lambda statements can not make use of spans either. Spans can not be used in asynchronous programming across `await` and `yield` boundaries.\n\nSpans are not appropriate in all situations. **Because we are allocating memory on the stack using spans, we must remember that there is less stack memory than heap memory.** We must consider this when choosing to use spans over strings.\n\nIf we want to use a span-like class in asynchronous programming we could take advantage of `Memory\u003c\u003e` and `ReadOnlyMemory\u003c\u003e`. We can create a `Memory\u003c\u003e` object from an array and slice it as we will see, we can do with a span. Once we can synchronously run code, we can get a span from a `Memory\u003c\u003e` object.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanupsarkar-dev%2Fcsharp-loop-benchmark","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanupsarkar-dev%2Fcsharp-loop-benchmark","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanupsarkar-dev%2Fcsharp-loop-benchmark/lists"}