├── .editorconfig
├── .github
├── dependabot.yaml
└── workflows
│ ├── build-debug.yml
│ ├── build-release.yml
│ └── stale.yml
├── .gitignore
├── Directory.Build.props
├── Icon.png
├── LICENSE.md
├── README.md
├── SimdLinq.sln
├── docs
└── simdlinq_graph.xlsx
├── opensource.snk
├── sandbox
└── Benchmark
│ ├── Benchmark.csproj
│ └── Program.cs
├── src
└── SimdLinq
│ ├── SimdLinq.csproj
│ ├── SimdLinqExtensions.Average.cs
│ ├── SimdLinqExtensions.Average.tt
│ ├── SimdLinqExtensions.AverageCore.cs
│ ├── SimdLinqExtensions.Contains.cs
│ ├── SimdLinqExtensions.Contains.tt
│ ├── SimdLinqExtensions.ContainsCore.cs
│ ├── SimdLinqExtensions.LongSum.cs
│ ├── SimdLinqExtensions.LongSum.tt
│ ├── SimdLinqExtensions.Max.cs
│ ├── SimdLinqExtensions.Max.tt
│ ├── SimdLinqExtensions.Min.cs
│ ├── SimdLinqExtensions.Min.tt
│ ├── SimdLinqExtensions.MinMax.cs
│ ├── SimdLinqExtensions.MinMax.tt
│ ├── SimdLinqExtensions.MinMaxCore.cs
│ ├── SimdLinqExtensions.SequenceEqual.cs
│ ├── SimdLinqExtensions.SequenceEqual.tt
│ ├── SimdLinqExtensions.Sum.cs
│ ├── SimdLinqExtensions.Sum.tt
│ ├── SimdLinqExtensions.SumCore.cs
│ ├── SimdLinqExtensions.cs
│ └── SimdLinqExtensions.ttinclude
└── tests
└── SimdLinq.Tests
├── AverageTest.cs
├── ContainsTest.cs
├── MinMaxTest.cs
├── SequenceEqualTest.cs
├── SimdLinq.Tests.csproj
└── SumTest.cs
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | insert_final_newline = true
6 | indent_style = space
7 | trim_trailing_whitespace = true
8 |
9 | [*.{sh,yaml,yml,rules}]
10 | charset = utf-8
11 | indent_size = 2
12 | end_of_line = lf
13 |
14 | [*{_AssemblyInfo.cs,.notsupported.cs}]
15 | generated_code = true
16 |
17 | # C# files
18 | [*.cs]
19 | charset = utf-8-bom
20 | indent_size = 4
21 |
22 | csharp_style_namespace_declarations = file_scoped
23 | dotnet_style_require_accessibility_modifiers = never
24 |
25 | # VSTHRD101: Avoid async void
26 | # VSTHRD101: Avoid unsupported async delegates
27 | dotnet_diagnostic.VSTHRD100.severity = none
28 | dotnet_diagnostic.VSTHRD101.severity = none
29 |
30 | # VSTHRD003: Avoid awaiting foreign Tasks
31 | dotnet_diagnostic.VSTHRD003.severity = none
32 |
33 | # VSTHRD111: Use ConfigureAwait(bool)
34 | dotnet_diagnostic.VSTHRD111.severity = error
35 |
--------------------------------------------------------------------------------
/.github/dependabot.yaml:
--------------------------------------------------------------------------------
1 | # ref: https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot
2 | version: 2
3 | updates:
4 | - package-ecosystem: "github-actions"
5 | directory: "/"
6 | schedule:
7 | interval: "weekly" # Check for updates to GitHub Actions every week
8 | ignore:
9 | # I just want update action when major/minor version is updated. patch updates are too noisy.
10 | - dependency-name: '*'
11 | update-types:
12 | - version-update:semver-patch
13 |
--------------------------------------------------------------------------------
/.github/workflows/build-debug.yml:
--------------------------------------------------------------------------------
1 | name: Build-Debug
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches:
7 | - "main"
8 | pull_request:
9 | branches:
10 | - "main"
11 |
12 | jobs:
13 | build-dotnet:
14 | runs-on: ubuntu-latest
15 | timeout-minutes: 10
16 | steps:
17 | - uses: Cysharp/Actions/.github/actions/checkout@main
18 | - uses: Cysharp/Actions/.github/actions/setup-dotnet@main
19 | - run: dotnet build -c Debug
20 | - run: dotnet test -c Debug --no-build
21 |
--------------------------------------------------------------------------------
/.github/workflows/build-release.yml:
--------------------------------------------------------------------------------
1 | name: Build-Release
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | tag:
7 | description: "tag: git tag you want create. (sample 1.0.0)"
8 | required: true
9 | dry-run:
10 | description: "dry-run: true will never create release/nuget."
11 | required: true
12 | default: false
13 | type: boolean
14 |
15 | jobs:
16 | build-dotnet:
17 | runs-on: ubuntu-latest
18 | timeout-minutes: 10
19 | steps:
20 | - uses: Cysharp/Actions/.github/actions/checkout@main
21 | - uses: Cysharp/Actions/.github/actions/setup-dotnet@main
22 | # pack nuget
23 | - run: dotnet build -c Release -p:Version=${{ inputs.tag }}
24 | - run: dotnet test -c Release --no-build
25 | - run: dotnet pack -c Release --no-build -p:Version=${{ inputs.tag }} -p:PackageOutputPath=../../publish
26 | - uses: Cysharp/Actions/.github/actions/upload-artifact@main
27 | with:
28 | name: nuget
29 | path: ./publish
30 | retention-days: 1
31 |
32 | # release
33 | create-release:
34 | needs: [build-dotnet]
35 | uses: Cysharp/Actions/.github/workflows/create-release.yaml@main
36 | with:
37 | commit-id: ''
38 | dry-run: ${{ inputs.dry-run }}
39 | tag: ${{ inputs.tag }}
40 | nuget-push: true
41 | secrets: inherit
42 |
--------------------------------------------------------------------------------
/.github/workflows/stale.yml:
--------------------------------------------------------------------------------
1 | name: "Close stale issues"
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | - cron: "0 0 * * *"
7 |
8 | jobs:
9 | stale:
10 | uses: Cysharp/Actions/.github/workflows/stale-issue.yaml@main
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
2 | [Bb]in/
3 | [Oo]bj/
4 |
5 | # mstest test results
6 | TestResults
7 |
8 | ## Ignore Visual Studio temporary files, build results, and
9 | ## files generated by popular Visual Studio add-ons.
10 |
11 | # User-specific files
12 | *.suo
13 | *.user
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Rr]elease/
19 | *_i.c
20 | *_p.c
21 | *.ilk
22 | *.obj
23 | *.pch
24 | *.pdb
25 | *.pgc
26 | *.pgd
27 | *.rsp
28 | *.sbr
29 | *.tlb
30 | *.tli
31 | *.tlh
32 | *.tmp
33 | *.log
34 | *.vspscc
35 | *.vssscc
36 | .builds
37 |
38 | # Visual C++ cache files
39 | ipch/
40 | *.aps
41 | *.ncb
42 | *.opensdf
43 | *.sdf
44 |
45 | # Visual Studio profiler
46 | *.psess
47 | *.vsp
48 | *.vspx
49 |
50 | # Guidance Automation Toolkit
51 | *.gpState
52 |
53 | # ReSharper is a .NET coding add-in
54 | _ReSharper*
55 |
56 | # NCrunch
57 | *.ncrunch*
58 | .*crunch*.local.xml
59 |
60 | # Installshield output folder
61 | [Ee]xpress
62 |
63 | # DocProject is a documentation generator add-in
64 | DocProject/buildhelp/
65 | DocProject/Help/*.HxT
66 | DocProject/Help/*.HxC
67 | DocProject/Help/*.hhc
68 | DocProject/Help/*.hhk
69 | DocProject/Help/*.hhp
70 | DocProject/Help/Html2
71 | DocProject/Help/html
72 |
73 | # Click-Once directory
74 | publish
75 |
76 | # Publish Web Output
77 | *.Publish.xml
78 |
79 | # NuGet Packages Directory
80 | # packages # upm pacakge will use Packages
81 |
82 | # Windows Azure Build Output
83 | csx
84 | *.build.csdef
85 |
86 | # Windows Store app package directory
87 | AppPackages/
88 |
89 | # Others
90 | [Bb]in
91 | [Oo]bj
92 | sql
93 | TestResults
94 | [Tt]est[Rr]esult*
95 | *.Cache
96 | ClientBin
97 | [Ss]tyle[Cc]op.*
98 | ~$*
99 | *.dbmdl
100 | Generated_Code #added for RIA/Silverlight projects
101 |
102 | # Backup & report files from converting an old project file to a newer
103 | # Visual Studio version. Backup files are not needed, because we have git ;-)
104 | _UpgradeReport_Files/
105 | Backup*/
106 | UpgradeLog*.XML
107 | .vs/config/applicationhost.config
108 | .vs/restore.dg
109 |
110 | nuget/tools/*
111 | nuget/*.nupkg
112 | nuget/*.unitypackage
113 | .vs/
114 |
115 | # Jetbrains Rider
116 | .idea/
117 |
118 | __packages/
119 |
120 | sandbox/SandboxConsoleApp/MemoryPackLogs/
121 | /sandbox/SandboxWebApp/node_modules
122 |
123 | # Unity
124 |
125 | src/*.Unity/Library/*
126 | src/*.Unity/Temp/*
127 | src/*.Unity/Logs/*
128 |
129 | src/*.Unity/*.csproj
130 | src/*.Unity/*.sln
131 | src/*.Unity/*.*.unitypackage
132 |
133 | src/StructureOfArraysGenerator.Unity/UserSettings/Layouts/default-2021.dwlt
134 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | latest
4 | enable
5 | true
6 | $(NoWarn);CS1591
7 | true
8 | $(MSBuildThisFileDirectory)opensource.snk
9 |
10 |
11 | $(Version)
12 | Cysharp
13 | Cysharp
14 | © Cysharp, Inc.
15 | https://github.com/Cysharp/SimdLinq
16 | $(PackageProjectUrl)
17 | git
18 | MIT
19 | Icon.png
20 | $(MSBuildThisFileDirectory)opensource.snk
21 |
22 |
--------------------------------------------------------------------------------
/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cysharp/SimdLinq/f00603b250e3eaeac31c5de292b6a5cbcbff85f7/Icon.png
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Cysharp, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > [!IMPORTANT]
2 | > [Cysharp/ZLinq](https://github.com/Cysharp/ZLinq), a new zero allocation LINQ library, includes LINQ to SIMD capabilities, it now contains all SimdLinq features. Please migrate to ZLinq.
3 |
4 | # SimdLinq
5 | [](https://github.com/Cysharp/SimdLinq/actions) [](https://github.com/Cysharp/SimdLinq/releases)
6 |
7 | SimdLinq is a drop-in replacement of LINQ aggregation operations(`Sum`, `Average`, `Min`, `Max`) extremely faster with SIMD.
8 |
9 | 
10 |
11 | [.NET 7 LINQ supports SIMD](https://devblogs.microsoft.com/dotnet/performance_improvements_in_net_7/#linq) but it is very limited, due to compatibility and safety issues, it is only enabled for `int[]` `Average`, `Min`, `Max` and `long[]` `Min`, `Max`.
12 |
13 | SimdLinq accelerates many methods (`Sum`, `LongSum`, `Average`, `Min`, `Max`, `MinMax`, `Contains`, `SequenceEqual`) and types (`byte`, `sbyte`, `short`, `ushort`, ` int`, `uint`, `long`, `ulong`, `float`, `double`). It can also be used with `List`, `Span`, `ReadOnlySpan`, `Memory`, `ReadOnlyMemory` in addition to `T[]`.
14 |
15 | Using the overload resolution priority, all target methods are automatically SIMD by simply referencing the library and setting global using.
16 |
17 | Quick Start
18 | ---
19 | This library is distributed via NuGet.
20 |
21 | > PM> Install-Package [SimdLinq](https://www.nuget.org/packages/SimdLinq)
22 |
23 | Note that this library requires **.NET 7** or above because this library uses static abstract members and the new cross platform **.NET 7** SIMD Api.
24 |
25 | ```csharp
26 | using SimdLinq; // enable SimdLinq extension methods
27 |
28 | var array = Enumerable.Range(1, 100000).ToArray();
29 |
30 | var sum = array.Sum(); // uses SimdLinqExtensions.Sum
31 | ```
32 |
33 | To enable SimdLinq per file, add the `using SimdLinq;` using directive. To enable SimdLinq across the project, use global usings in the csproj file.
34 |
35 | ```xml
36 |
37 |
38 |
39 | ```
40 |
41 | `SimdLinqExtensions` has methods for concrete data types, like `int[]` or `Span`, with the same names as LINQ's methods. If a method is eligible for SIMD optimization (such as the Sum of int[]), the `SimdLinqExtensions` method will be used for improved performance.
42 |
43 | Unlike base LINQ, SimdLinq supports `Span`, allowing you to call methods such as `Sum`, `Min`, etc. on `Span` or `ReadOnlySpan` collections.
44 |
45 | ```csharp
46 | (double Min, double Max) GetMinMax(Span span)
47 | {
48 | return span.MinMax();
49 | }
50 | ```
51 |
52 | `MinMax` is an original SimdLinq extension, that returns a tuple of `Min` and `Max`.
53 |
54 | Compatibility
55 | ---
56 | One of the reasons why LINQ's SIMD support in .NET 7 is incomplete, is compatibility. SimdLinq prioritises performance over some safety features and full compatibility with LINQ. Please note the following differences.
57 |
58 | ### Sum/Average
59 |
60 | LINQ Sum is `checked` but SimdLinq is `unchecked`(SIMD operation is not supported overflow). To reduce the risk of overflow, `Sum` and `Average` only support types that are 32-bit or higher(`int`, `long`, `uint`, `ulong`, `double`, `float`).
61 |
62 | SimdLinq provides `LongSum` for `int` and `uint`, that returns `long`/`ulong` so avoid overflow.
63 |
64 | ### float/double
65 |
66 | Unlike LINQ, SimdLinq does not check for NaN in float/double Min/Max calculations. Additionally, the order of calculation for Sum is not sequential, leading to slight differences in floating-point operations compared to regular LINQ. For instance, LINQ returns 1.5710588F, while SimdLinq returns 1.5710589F. If compatibility is not a concern, this difference is not significant, but be aware of potential small tolerance differences.
67 |
68 | Supported collections
69 | ---
70 | `T[]`, `List`, `Span`, `Memory`, `ReadOnlyMemory`, `Span`, `ReadOnlySpan`.
71 |
72 | Supported methods
73 | ---
74 | * `Sum` for `int`, `uint`, `long`, `ulong`, `float`, `double`
75 | * `LongSum` for `int`, `uint`
76 | * `Average` for `int`, `uint`, `long`, `ulong`, `float`, `double`
77 | * `Min` for `byte`, `sbyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `float`, `double`
78 | * `Max` for `byte`, `sbyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `float`, `double`
79 | * `MinMax` for `byte`, `sbyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `float`, `double`
80 | * `Contains` for `byte`, `sbyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `float`, `double`
81 | * `SequenceEqual` for `byte`, `sbyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `float`, `double`
82 |
83 | SoA
84 | ---
85 | SIMD with LINQ requires primitive array(or Span). [Cysharp/StructureOfArraysGenerator](https://github.com/Cysharp/StructureOfArraysGenerator) makes easy to create SoA array to use SIMD easily.
86 |
87 | License
88 | ---
89 | This library is licensed under the MIT License.
90 |
--------------------------------------------------------------------------------
/SimdLinq.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.4.33122.133
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimdLinq", "src\SimdLinq\SimdLinq.csproj", "{AA3CCB48-DEFB-46AC-8E59-234735CB46FA}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{45E7884C-F0DE-4A7F-AD46-24D712E689D5}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sandbox", "sandbox", "{2E552713-81E9-456B-89CB-4AE514224FAA}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{8F6AE842-1F39-42D9-935A-995F00132030}"
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimdLinq.Tests", "tests\SimdLinq.Tests\SimdLinq.Tests.csproj", "{152C1467-82AE-4EB5-A5D1-24ED455FE214}"
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmark", "sandbox\Benchmark\Benchmark.csproj", "{B3A429CC-AC28-4A60-8DA0-9C542B602684}"
17 | EndProject
18 | Global
19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
20 | Debug|Any CPU = Debug|Any CPU
21 | Release|Any CPU = Release|Any CPU
22 | EndGlobalSection
23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
24 | {AA3CCB48-DEFB-46AC-8E59-234735CB46FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {AA3CCB48-DEFB-46AC-8E59-234735CB46FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {AA3CCB48-DEFB-46AC-8E59-234735CB46FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {AA3CCB48-DEFB-46AC-8E59-234735CB46FA}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {152C1467-82AE-4EB5-A5D1-24ED455FE214}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {152C1467-82AE-4EB5-A5D1-24ED455FE214}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {152C1467-82AE-4EB5-A5D1-24ED455FE214}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {152C1467-82AE-4EB5-A5D1-24ED455FE214}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {B3A429CC-AC28-4A60-8DA0-9C542B602684}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {B3A429CC-AC28-4A60-8DA0-9C542B602684}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {B3A429CC-AC28-4A60-8DA0-9C542B602684}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {B3A429CC-AC28-4A60-8DA0-9C542B602684}.Release|Any CPU.Build.0 = Release|Any CPU
36 | EndGlobalSection
37 | GlobalSection(SolutionProperties) = preSolution
38 | HideSolutionNode = FALSE
39 | EndGlobalSection
40 | GlobalSection(NestedProjects) = preSolution
41 | {AA3CCB48-DEFB-46AC-8E59-234735CB46FA} = {45E7884C-F0DE-4A7F-AD46-24D712E689D5}
42 | {152C1467-82AE-4EB5-A5D1-24ED455FE214} = {8F6AE842-1F39-42D9-935A-995F00132030}
43 | {B3A429CC-AC28-4A60-8DA0-9C542B602684} = {2E552713-81E9-456B-89CB-4AE514224FAA}
44 | EndGlobalSection
45 | GlobalSection(ExtensibilityGlobals) = postSolution
46 | SolutionGuid = {85BA7882-CB7B-40DA-9B27-2CCF09AB188A}
47 | EndGlobalSection
48 | EndGlobal
49 |
--------------------------------------------------------------------------------
/docs/simdlinq_graph.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cysharp/SimdLinq/f00603b250e3eaeac31c5de292b6a5cbcbff85f7/docs/simdlinq_graph.xlsx
--------------------------------------------------------------------------------
/opensource.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cysharp/SimdLinq/f00603b250e3eaeac31c5de292b6a5cbcbff85f7/opensource.snk
--------------------------------------------------------------------------------
/sandbox/Benchmark/Benchmark.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net7.0
6 | enable
7 | True
8 | enable
9 | true
10 | false
11 |
12 |
13 | $(WarningsAsErrors);NU1605;
14 | $(NoWarn);CS1591;CS8604;CS8032;CS8002;CS8600
15 |
16 |
17 |
18 | $(WarningsAsErrors);NU1605;
19 | $(NoWarn);CS1591;CS8604;CS8032;CS8002;CS8600
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/sandbox/Benchmark/Program.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable CA1050 // Declare types in namespaces
2 | using BenchmarkDotNet.Configs;
3 | using BenchmarkDotNet.Diagnosers;
4 | using BenchmarkDotNet.Exporters;
5 | using BenchmarkDotNet.Jobs;
6 | using BenchmarkDotNet.Running;
7 | using JM.LinqFaster.SIMD;
8 | using NetFabric.Hyperlinq;
9 | using SimdLinq;
10 | using System.ComponentModel;
11 | using System.Linq;
12 | using System.Numerics;
13 | using System.Reflection;
14 |
15 | var config = ManualConfig.CreateMinimumViable()
16 | .AddDiagnoser(MemoryDiagnoser.Default)
17 | .AddExporter(MarkdownExporter.Default)
18 | .AddJob(Job.Default.WithWarmupCount(1).WithIterationCount(1));
19 |
20 | #if !DEBUG
21 |
22 |
23 |
24 |
25 | //BenchmarkRunner.Run(config, args);
26 | //BenchmarkRunner.Run(config, args);
27 | //BenchmarkRunner.Run(config, args);
28 | //BenchmarkRunner.Run(config, args);
29 | //BenchmarkRunner.Run(config, args);
30 | BenchmarkRunner.Run(config, args);
31 |
32 | // BenchmarkSwitcher.FromAssembly(Assembly.GetEntryAssembly()!).RunAllJoined(config);
33 |
34 | #else
35 |
36 |
37 | // _= Enumerable.Max((int[])null!);
38 | //System.Linq.Enumerable.Min(Array.Empty());
39 | //_ = new IntMinBenchmark().SimdLinq();
40 |
41 | #endif
42 |
43 | public class IntSumBenchmark
44 | {
45 | readonly int[] source;
46 |
47 | public IntSumBenchmark()
48 | {
49 | source = Enumerable.Range(1, 10000).ToArray();
50 |
51 | var check = new[] { For(), LINQ(), SimdLinq(), LinqFasterSIMD(), Hyperlinq() }.Distinct().Count() == 1;
52 | if (!check)
53 | {
54 | throw new Exception("invalid result exists");
55 | }
56 | }
57 |
58 | [Benchmark]
59 | public int For()
60 | {
61 | var array = source;
62 | var sum = 0;
63 | for (int i = 0; i < array.Length; i++)
64 | {
65 | sum += array[i];
66 | }
67 | return sum;
68 | }
69 |
70 | [Benchmark]
71 | public int LINQ()
72 | {
73 | return Enumerable.Sum(source);
74 | }
75 |
76 | [Benchmark]
77 | public int SimdLinq()
78 | {
79 | return SimdLinqExtensions.Sum(source);
80 | }
81 |
82 | [Benchmark]
83 | public int LinqFasterSIMD()
84 | {
85 | return source.SumS();
86 | }
87 |
88 | [Benchmark]
89 | public int Hyperlinq()
90 | {
91 | return source.AsValueEnumerable().Sum();
92 | }
93 | }
94 |
95 | public class IntMinBenchmark
96 | {
97 | readonly int[] source;
98 |
99 | public IntMinBenchmark()
100 | {
101 | var rand = new Random(123);
102 | source = Enumerable.Range(1, 10000).Select(_ => rand.Next()).ToArray();
103 |
104 | var check = new[] { For(), LINQ(), SimdLinq(), LinqFasterSIMD(), Hyperlinq() }.Distinct().Count() == 1;
105 | if (!check)
106 | {
107 | throw new Exception("invalid result exists");
108 | }
109 | }
110 |
111 | [Benchmark]
112 | public int For()
113 | {
114 | var array = source;
115 | var min = array[0];
116 | for (int i = 1; i < array.Length; i++)
117 | {
118 | if (array[i] < min)
119 | {
120 | min = array[i];
121 | }
122 | }
123 | return min;
124 | }
125 |
126 | [Benchmark]
127 | public int LINQ()
128 | {
129 | return Enumerable.Min(source);
130 | }
131 |
132 | [Benchmark]
133 | public int SimdLinq()
134 | {
135 | return SimdLinqExtensions.Min(source);
136 | }
137 |
138 | [Benchmark]
139 | public int LinqFasterSIMD()
140 | {
141 | return source.MinS();
142 | }
143 |
144 | [Benchmark]
145 | public int Hyperlinq()
146 | {
147 | return source.AsValueEnumerable().Min();
148 | }
149 | }
150 |
151 | public class LongMinBenchmark
152 | {
153 | readonly long[] source;
154 |
155 | public LongMinBenchmark()
156 | {
157 | var rand = new Random(123);
158 | source = Enumerable.Range(1, 10000).Select(_ => rand.NextInt64()).ToArray();
159 |
160 | var check = new[] { For(), LINQ(), SimdLinq(), LinqFasterSIMD(), Hyperlinq() }.Distinct().Count() == 1;
161 | if (!check)
162 | {
163 | throw new Exception("invalid result exists");
164 | }
165 | }
166 |
167 | [Benchmark]
168 | public long For()
169 | {
170 | var array = source;
171 | var min = array[0];
172 | for (int i = 1; i < array.Length; i++)
173 | {
174 | if (array[i] < min)
175 | {
176 | min = array[i];
177 | }
178 | }
179 | return min;
180 | }
181 |
182 | [Benchmark]
183 | public long LINQ()
184 | {
185 | return Enumerable.Min(source);
186 | }
187 |
188 | [Benchmark]
189 | public long SimdLinq()
190 | {
191 | return SimdLinqExtensions.Min(source);
192 | }
193 |
194 | [Benchmark]
195 | public long LinqFasterSIMD()
196 | {
197 | return source.MinS();
198 | }
199 |
200 | [Benchmark]
201 | public long Hyperlinq()
202 | {
203 | return source.AsValueEnumerable().Min();
204 | }
205 | }
206 |
207 | public class DoubleMinBenchmark
208 | {
209 | readonly double[] source;
210 |
211 | public DoubleMinBenchmark()
212 | {
213 | var rand = new Random(123);
214 | source = Enumerable.Range(1, 10000).Select(_ => rand.NextDouble()).ToArray();
215 |
216 | var check = new[] { For(), LINQ(), SimdLinq(), LinqFasterSIMD(), Hyperlinq() }.Distinct().Count() == 1;
217 | if (!check)
218 | {
219 | throw new Exception("invalid result exists");
220 | }
221 | }
222 |
223 | [Benchmark]
224 | public double For()
225 | {
226 | var array = source;
227 | var min = array[0];
228 | for (int i = 1; i < array.Length; i++)
229 | {
230 | if (array[i] < min)
231 | {
232 | min = array[i];
233 | }
234 | }
235 | return min;
236 | }
237 |
238 | [Benchmark]
239 | public double LINQ()
240 | {
241 | return Enumerable.Min(source);
242 | }
243 |
244 | [Benchmark]
245 | public double SimdLinq()
246 | {
247 | return SimdLinqExtensions.Min(source);
248 | }
249 |
250 | [Benchmark]
251 | public double LinqFasterSIMD()
252 | {
253 | return source.MinS();
254 | }
255 |
256 | [Benchmark]
257 | public double Hyperlinq()
258 | {
259 | return source.AsValueEnumerable().Min();
260 | }
261 | }
262 |
263 | public class IntContainsBenchmark
264 | {
265 | readonly int[] source;
266 | readonly int target;
267 | public IntContainsBenchmark()
268 | {
269 | source = Enumerable.Range(1, 10000).ToArray();
270 | target = 8345;
271 | var check = new[] { For(), LINQ(), SimdLinq(), LinqFasterSIMD(), Hyperlinq() }.Distinct().Count() == 1;
272 | if (!check)
273 | {
274 | throw new Exception("invalid result exists");
275 | }
276 | }
277 |
278 | [Benchmark]
279 | public bool For()
280 | {
281 | var array = source;
282 | var t = target;
283 | for (int i = 0; i < array.Length; i++)
284 | {
285 | if (array[i] == t) return true;
286 | }
287 | return false;
288 | }
289 |
290 | [Benchmark]
291 | public bool LINQ()
292 | {
293 | return Enumerable.Contains(source, target);
294 | }
295 |
296 | [Benchmark]
297 | public bool SimdLinq()
298 | {
299 | return SimdLinqExtensions.Contains(source, target);
300 | }
301 |
302 | [Benchmark]
303 | public bool LinqFasterSIMD()
304 | {
305 | return source.ContainsS(target);
306 | }
307 |
308 | [Benchmark]
309 | public bool Hyperlinq()
310 | {
311 | return source.AsValueEnumerable().Contains(target);
312 | }
313 |
314 |
315 | }
316 |
317 |
318 | public class SumVsLongSumBenchmark
319 | {
320 | readonly int[] source;
321 |
322 | public SumVsLongSumBenchmark()
323 | {
324 | source = Enumerable.Range(1, 10000).ToArray();
325 | }
326 |
327 | [Benchmark]
328 | public int SimdSum()
329 | {
330 | return SimdLinqExtensions.Sum(source);
331 | }
332 |
333 | [Benchmark]
334 | public long SimdLongSum()
335 | {
336 | return SimdLinqExtensions.LongSum(source);
337 | }
338 |
339 | [Benchmark]
340 | public int LinqSum()
341 | {
342 | return Enumerable.Sum(source);
343 | }
344 | }
345 |
--------------------------------------------------------------------------------
/src/SimdLinq/SimdLinq.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net7.0
5 | enable
6 | enable
7 | true
8 | $(NoWarn);CS1591;CA2255
9 | linq
10 | Drop-in replacement of LINQ aggregation operations extremely faster with SIMD.
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | SimdLinqExtensions.LongSum.cs
20 | TextTemplatingFileGenerator
21 |
22 |
23 | SimdLinqExtensions.Max.cs
24 | TextTemplatingFileGenerator
25 |
26 |
27 | SimdLinqExtensions.Min.cs
28 | TextTemplatingFileGenerator
29 |
30 |
31 | SimdLinqExtensions.Sum.cs
32 | TextTemplatingFileGenerator
33 |
34 |
35 | SimdLinqExtensions.Contains.cs
36 | TextTemplatingFileGenerator
37 |
38 |
39 | SimdLinqExtensions.SequenceEqual.cs
40 | TextTemplatingFileGenerator
41 |
42 |
43 | TextTemplatingFileGenerator
44 | SimdLinqExtensions.MinMax.cs
45 |
46 |
47 | TextTemplatingFileGenerator
48 | SimdLinqExtensions.Average.cs
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | SimdLinqExtensions.LongSum.tt
59 | True
60 | True
61 |
62 |
63 | SimdLinqExtensions.Max.tt
64 | True
65 | True
66 |
67 |
68 | SimdLinqExtensions.Min.tt
69 | True
70 | True
71 |
72 |
73 | SimdLinqExtensions.Sum.tt
74 | True
75 | True
76 |
77 |
78 | SimdLinqExtensions.Contains.tt
79 | True
80 | True
81 |
82 |
83 | SimdLinqExtensions.SequenceEqual.tt
84 | True
85 | True
86 |
87 |
88 | True
89 | True
90 | SimdLinqExtensions.MinMax.tt
91 |
92 |
93 | True
94 | True
95 | SimdLinqExtensions.Average.tt
96 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/src/SimdLinq/SimdLinqExtensions.Average.cs:
--------------------------------------------------------------------------------
1 | namespace SimdLinq;
2 |
3 | using System.Runtime.InteropServices;
4 |
5 | public static partial class SimdLinqExtensions
6 | {
7 | ///
8 | /// Computes the average of a sequence of values using SIMD acceleration.
9 | ///
10 | /// A collection of values to calculate the average of.
11 | /// The average of the collection of values.
12 | /// is .
13 | /// contains no elements.
14 | public static double Average(this int[] source)
15 | {
16 | ArgumentNullException.ThrowIfNull(source);
17 | return AverageCore(new ReadOnlySpan(source));
18 | }
19 |
20 | ///
21 | /// Computes the average of a sequence of values using SIMD acceleration.
22 | ///
23 | /// A collection of values to calculate the average of.
24 | /// The average of the collection of values.
25 | /// is .
26 | /// contains no elements.
27 | public static double Average(this List source)
28 | {
29 | ArgumentNullException.ThrowIfNull(source);
30 | return AverageCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
31 | }
32 |
33 | ///
34 | /// Computes the average of a sequence of values using SIMD acceleration.
35 | ///
36 | /// A collection of values to calculate the average of.
37 | /// The average of the collection of values.
38 | /// contains no elements.
39 | public static double Average(this Memory source)
40 | {
41 | return AverageCore((ReadOnlySpan)source.Span);
42 | }
43 |
44 | ///
45 | /// Computes the average of a sequence of values using SIMD acceleration.
46 | ///
47 | /// A collection of values to calculate the average of.
48 | /// The average of the collection of values.
49 | /// contains no elements.
50 | public static double Average(this ReadOnlyMemory source)
51 | {
52 | return AverageCore(source.Span);
53 | }
54 |
55 | ///
56 | /// Computes the average of a sequence of values using SIMD acceleration.
57 | ///
58 | /// A collection of values to calculate the average of.
59 | /// The average of the collection of values.
60 | /// contains no elements.
61 | public static double Average(this Span source)
62 | {
63 | return AverageCore((ReadOnlySpan)source);
64 | }
65 |
66 | ///
67 | /// Computes the average of a sequence of values using SIMD acceleration.
68 | ///
69 | /// A collection of values to calculate the average of.
70 | /// The average of the collection of values.
71 | /// contains no elements.
72 | public static double Average(this ReadOnlySpan source)
73 | {
74 | return AverageCore(source);
75 | }
76 |
77 | ///
78 | /// Computes the average of a sequence of values using SIMD acceleration.
79 | ///
80 | /// A collection of values to calculate the average of.
81 | /// The average of the collection of values.
82 | /// is .
83 | /// contains no elements.
84 | public static double Average(this long[] source)
85 | {
86 | ArgumentNullException.ThrowIfNull(source);
87 | return AverageCore(new ReadOnlySpan(source));
88 | }
89 |
90 | ///
91 | /// Computes the average of a sequence of values using SIMD acceleration.
92 | ///
93 | /// A collection of values to calculate the average of.
94 | /// The average of the collection of values.
95 | /// is .
96 | /// contains no elements.
97 | public static double Average(this List source)
98 | {
99 | ArgumentNullException.ThrowIfNull(source);
100 | return AverageCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
101 | }
102 |
103 | ///
104 | /// Computes the average of a sequence of values using SIMD acceleration.
105 | ///
106 | /// A collection of values to calculate the average of.
107 | /// The average of the collection of values.
108 | /// contains no elements.
109 | public static double Average(this Memory source)
110 | {
111 | return AverageCore((ReadOnlySpan)source.Span);
112 | }
113 |
114 | ///
115 | /// Computes the average of a sequence of values using SIMD acceleration.
116 | ///
117 | /// A collection of values to calculate the average of.
118 | /// The average of the collection of values.
119 | /// contains no elements.
120 | public static double Average(this ReadOnlyMemory source)
121 | {
122 | return AverageCore(source.Span);
123 | }
124 |
125 | ///
126 | /// Computes the average of a sequence of values using SIMD acceleration.
127 | ///
128 | /// A collection of values to calculate the average of.
129 | /// The average of the collection of values.
130 | /// contains no elements.
131 | public static double Average(this Span source)
132 | {
133 | return AverageCore((ReadOnlySpan)source);
134 | }
135 |
136 | ///
137 | /// Computes the average of a sequence of values using SIMD acceleration.
138 | ///
139 | /// A collection of values to calculate the average of.
140 | /// The average of the collection of values.
141 | /// contains no elements.
142 | public static double Average(this ReadOnlySpan source)
143 | {
144 | return AverageCore(source);
145 | }
146 |
147 | ///
148 | /// Computes the average of a sequence of values using SIMD acceleration.
149 | ///
150 | /// Unlike Linq, the order for the sum calculation is not sequential, leading to slight differences in floating-point operations and the resulting value. While this difference is minimal you should not use this method if you require the same result as Linq.Average.
151 | /// A collection of values to calculate the average of.
152 | /// The average of the collection of values.
153 | /// is .
154 | /// contains no elements.
155 | public static double Average(this double[] source)
156 | {
157 | ArgumentNullException.ThrowIfNull(source);
158 | return AverageCore(new ReadOnlySpan(source));
159 | }
160 |
161 | ///
162 | /// Computes the average of a sequence of values using SIMD acceleration.
163 | ///
164 | /// Unlike Linq, the order for the sum calculation is not sequential, leading to slight differences in floating-point operations and the resulting value. While this difference is minimal you should not use this method if you require the same result as Linq.Average.
165 | /// A collection of values to calculate the average of.
166 | /// The average of the collection of values.
167 | /// is .
168 | /// contains no elements.
169 | public static double Average(this List source)
170 | {
171 | ArgumentNullException.ThrowIfNull(source);
172 | return AverageCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
173 | }
174 |
175 | ///
176 | /// Computes the average of a sequence of values using SIMD acceleration.
177 | ///
178 | /// Unlike Linq, the order for the sum calculation is not sequential, leading to slight differences in floating-point operations and the resulting value. While this difference is minimal you should not use this method if you require the same result as Linq.Average.
179 | /// A collection of values to calculate the average of.
180 | /// The average of the collection of values.
181 | /// contains no elements.
182 | public static double Average(this Memory source)
183 | {
184 | return AverageCore((ReadOnlySpan)source.Span);
185 | }
186 |
187 | ///
188 | /// Computes the average of a sequence of values using SIMD acceleration.
189 | ///
190 | /// Unlike Linq, the order for the sum calculation is not sequential, leading to slight differences in floating-point operations and the resulting value. While this difference is minimal you should not use this method if you require the same result as Linq.Average.
191 | /// A collection of values to calculate the average of.
192 | /// The average of the collection of values.
193 | /// contains no elements.
194 | public static double Average(this ReadOnlyMemory source)
195 | {
196 | return AverageCore(source.Span);
197 | }
198 |
199 | ///
200 | /// Computes the average of a sequence of values using SIMD acceleration.
201 | ///
202 | /// Unlike Linq, the order for the sum calculation is not sequential, leading to slight differences in floating-point operations and the resulting value. While this difference is minimal you should not use this method if you require the same result as Linq.Average.
203 | /// A collection of values to calculate the average of.
204 | /// The average of the collection of values.
205 | /// contains no elements.
206 | public static double Average(this Span source)
207 | {
208 | return AverageCore((ReadOnlySpan)source);
209 | }
210 |
211 | ///
212 | /// Computes the average of a sequence of values using SIMD acceleration.
213 | ///
214 | /// Unlike Linq, the order for the sum calculation is not sequential, leading to slight differences in floating-point operations and the resulting value. While this difference is minimal you should not use this method if you require the same result as Linq.Average.
215 | /// A collection of values to calculate the average of.
216 | /// The average of the collection of values.
217 | /// contains no elements.
218 | public static double Average(this ReadOnlySpan source)
219 | {
220 | return AverageCore(source);
221 | }
222 |
223 | ///
224 | /// Computes the average of a sequence of values using SIMD acceleration.
225 | ///
226 | /// Unlike Linq, the order for the sum calculation is not sequential, leading to slight differences in floating-point operations and the resulting value. While this difference is minimal you should not use this method if you require the same result as Linq.Average.
227 | /// A collection of values to calculate the average of.
228 | /// The average of the collection of values.
229 | /// is .
230 | /// contains no elements.
231 | public static float Average(this float[] source)
232 | {
233 | ArgumentNullException.ThrowIfNull(source);
234 | return AverageCore(new ReadOnlySpan(source));
235 | }
236 |
237 | ///
238 | /// Computes the average of a sequence of values using SIMD acceleration.
239 | ///
240 | /// Unlike Linq, the order for the sum calculation is not sequential, leading to slight differences in floating-point operations and the resulting value. While this difference is minimal you should not use this method if you require the same result as Linq.Average.
241 | /// A collection of values to calculate the average of.
242 | /// The average of the collection of values.
243 | /// is .
244 | /// contains no elements.
245 | public static float Average(this List source)
246 | {
247 | ArgumentNullException.ThrowIfNull(source);
248 | return AverageCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
249 | }
250 |
251 | ///
252 | /// Computes the average of a sequence of values using SIMD acceleration.
253 | ///
254 | /// Unlike Linq, the order for the sum calculation is not sequential, leading to slight differences in floating-point operations and the resulting value. While this difference is minimal you should not use this method if you require the same result as Linq.Average.
255 | /// A collection of values to calculate the average of.
256 | /// The average of the collection of values.
257 | /// contains no elements.
258 | public static float Average(this Memory source)
259 | {
260 | return AverageCore((ReadOnlySpan)source.Span);
261 | }
262 |
263 | ///
264 | /// Computes the average of a sequence of values using SIMD acceleration.
265 | ///
266 | /// Unlike Linq, the order for the sum calculation is not sequential, leading to slight differences in floating-point operations and the resulting value. While this difference is minimal you should not use this method if you require the same result as Linq.Average.
267 | /// A collection of values to calculate the average of.
268 | /// The average of the collection of values.
269 | /// contains no elements.
270 | public static float Average(this ReadOnlyMemory source)
271 | {
272 | return AverageCore(source.Span);
273 | }
274 |
275 | ///
276 | /// Computes the average of a sequence of values using SIMD acceleration.
277 | ///
278 | /// Unlike Linq, the order for the sum calculation is not sequential, leading to slight differences in floating-point operations and the resulting value. While this difference is minimal you should not use this method if you require the same result as Linq.Average.
279 | /// A collection of values to calculate the average of.
280 | /// The average of the collection of values.
281 | /// contains no elements.
282 | public static float Average(this Span source)
283 | {
284 | return AverageCore((ReadOnlySpan)source);
285 | }
286 |
287 | ///
288 | /// Computes the average of a sequence of values using SIMD acceleration.
289 | ///
290 | /// Unlike Linq, the order for the sum calculation is not sequential, leading to slight differences in floating-point operations and the resulting value. While this difference is minimal you should not use this method if you require the same result as Linq.Average.
291 | /// A collection of values to calculate the average of.
292 | /// The average of the collection of values.
293 | /// contains no elements.
294 | public static float Average(this ReadOnlySpan source)
295 | {
296 | return AverageCore(source);
297 | }
298 |
299 | ///
300 | /// Computes the average of a sequence of values using SIMD acceleration.
301 | ///
302 | /// A collection of values to calculate the average of.
303 | /// The average of the collection of values.
304 | /// is .
305 | /// contains no elements.
306 | public static double Average(this uint[] source)
307 | {
308 | ArgumentNullException.ThrowIfNull(source);
309 | return AverageCore(new ReadOnlySpan(source));
310 | }
311 |
312 | ///
313 | /// Computes the average of a sequence of values using SIMD acceleration.
314 | ///
315 | /// A collection of values to calculate the average of.
316 | /// The average of the collection of values.
317 | /// is .
318 | /// contains no elements.
319 | public static double Average(this List source)
320 | {
321 | ArgumentNullException.ThrowIfNull(source);
322 | return AverageCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
323 | }
324 |
325 | ///
326 | /// Computes the average of a sequence of values using SIMD acceleration.
327 | ///
328 | /// A collection of values to calculate the average of.
329 | /// The average of the collection of values.
330 | /// contains no elements.
331 | public static double Average(this Memory source)
332 | {
333 | return AverageCore((ReadOnlySpan)source.Span);
334 | }
335 |
336 | ///
337 | /// Computes the average of a sequence of values using SIMD acceleration.
338 | ///
339 | /// A collection of values to calculate the average of.
340 | /// The average of the collection of values.
341 | /// contains no elements.
342 | public static double Average(this ReadOnlyMemory source)
343 | {
344 | return AverageCore(source.Span);
345 | }
346 |
347 | ///
348 | /// Computes the average of a sequence of values using SIMD acceleration.
349 | ///
350 | /// A collection of values to calculate the average of.
351 | /// The average of the collection of values.
352 | /// contains no elements.
353 | public static double Average(this Span source)
354 | {
355 | return AverageCore((ReadOnlySpan)source);
356 | }
357 |
358 | ///
359 | /// Computes the average of a sequence of values using SIMD acceleration.
360 | ///
361 | /// A collection of values to calculate the average of.
362 | /// The average of the collection of values.
363 | /// contains no elements.
364 | public static double Average(this ReadOnlySpan source)
365 | {
366 | return AverageCore(source);
367 | }
368 |
369 | ///
370 | /// Computes the average of a sequence of values using SIMD acceleration.
371 | ///
372 | /// A collection of values to calculate the average of.
373 | /// The average of the collection of values.
374 | /// is .
375 | /// contains no elements.
376 | public static double Average(this ulong[] source)
377 | {
378 | ArgumentNullException.ThrowIfNull(source);
379 | return AverageCore(new ReadOnlySpan(source));
380 | }
381 |
382 | ///
383 | /// Computes the average of a sequence of values using SIMD acceleration.
384 | ///
385 | /// A collection of values to calculate the average of.
386 | /// The average of the collection of values.
387 | /// is .
388 | /// contains no elements.
389 | public static double Average(this List source)
390 | {
391 | ArgumentNullException.ThrowIfNull(source);
392 | return AverageCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
393 | }
394 |
395 | ///
396 | /// Computes the average of a sequence of values using SIMD acceleration.
397 | ///
398 | /// A collection of values to calculate the average of.
399 | /// The average of the collection of values.
400 | /// contains no elements.
401 | public static double Average(this Memory source)
402 | {
403 | return AverageCore((ReadOnlySpan)source.Span);
404 | }
405 |
406 | ///
407 | /// Computes the average of a sequence of values using SIMD acceleration.
408 | ///
409 | /// A collection of values to calculate the average of.
410 | /// The average of the collection of values.
411 | /// contains no elements.
412 | public static double Average(this ReadOnlyMemory source)
413 | {
414 | return AverageCore(source.Span);
415 | }
416 |
417 | ///
418 | /// Computes the average of a sequence of values using SIMD acceleration.
419 | ///
420 | /// A collection of values to calculate the average of.
421 | /// The average of the collection of values.
422 | /// contains no elements.
423 | public static double Average(this Span source)
424 | {
425 | return AverageCore((ReadOnlySpan)source);
426 | }
427 |
428 | ///
429 | /// Computes the average of a sequence of values using SIMD acceleration.
430 | ///
431 | /// A collection of values to calculate the average of.
432 | /// The average of the collection of values.
433 | /// contains no elements.
434 | public static double Average(this ReadOnlySpan source)
435 | {
436 | return AverageCore(source);
437 | }
438 |
439 | }
440 |
441 |
--------------------------------------------------------------------------------
/src/SimdLinq/SimdLinqExtensions.Average.tt:
--------------------------------------------------------------------------------
1 | <#@ template debug="false" hostspecific="false" language="C#" #>
2 | <#@ assembly name="System.Core" #>
3 | <#@ import namespace="System.Linq" #>
4 | <#@ import namespace="System.Text" #>
5 | <#@ import namespace="System.Collections.Generic" #>
6 | <#@ output extension=".cs" #>
7 | <#
8 | string summary(string x, bool nullable)
9 | {
10 | var remark = "";
11 | if(x is "double" or "float")
12 | {
13 | remark += """
14 |
15 | /// Unlike Linq, the order for the sum calculation is not sequential, leading to slight differences in floating-point operations and the resulting value. While this difference is minimal you should not use this method if you require the same result as Linq.Average.
16 | """;
17 | }
18 | var exception = "";
19 | if (nullable)
20 | {
21 | exception += """
22 |
23 | /// is .
24 | """;
25 | }
26 |
27 | return $"""
28 | ///
29 | /// Computes the average of a sequence of values using SIMD acceleration.
30 | /// {remark}
31 | /// A collection of values to calculate the average of.
32 | /// The average of the collection of values.{exception}
33 | /// contains no elements.
34 | """;
35 | }
36 |
37 | var retType = (string x) => x is "double" or "float" ? x : "double";
38 |
39 | var methodName = "Average";
40 | var types = new []
41 | {
42 | "int", "long", "double", "float", "uint", "ulong"
43 | };
44 | #>
45 | namespace SimdLinq;
46 |
47 | using System.Runtime.InteropServices;
48 |
49 | <#@ include file=".\SimdLinqExtensions.ttinclude" #>
50 |
--------------------------------------------------------------------------------
/src/SimdLinq/SimdLinqExtensions.AverageCore.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using System.Numerics;
3 |
4 | namespace SimdLinq;
5 |
6 | public static partial class SimdLinqExtensions
7 | {
8 | static T AverageCore(ReadOnlySpan source)
9 | where T : struct, INumber
10 | {
11 | if (source.IsEmpty) ThrowNoElements();
12 | return T.CreateChecked(SumCore(source)) / T.CreateChecked(source.Length);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/SimdLinq/SimdLinqExtensions.Contains.tt:
--------------------------------------------------------------------------------
1 | <#@ template debug="false" hostspecific="false" language="C#" #>
2 | <#@ assembly name="System.Core" #>
3 | <#@ import namespace="System.Linq" #>
4 | <#@ import namespace="System.Text" #>
5 | <#@ import namespace="System.Collections.Generic" #>
6 | <#@ output extension=".cs" #>
7 | <#
8 | string summary(string x, bool nullable)
9 | {
10 | var remark = "";
11 | if(x is "double" or "float")
12 | {
13 | remark += """
14 |
15 | /// This method does not check for / values and should not be used when may contain one.
16 | """;
17 | }
18 | var exception = "";
19 | if (nullable)
20 | {
21 | exception += """
22 |
23 | /// is .
24 | """;
25 | }
26 |
27 | return $"""
28 | ///
29 | /// Determines whether a sequence of contains a specified element using SIMD acceleration.
30 | /// {remark}
31 | /// A sequence of values in which to locate a value.
32 | /// The value to locate in the sequence.
33 | /// if the source sequence contains an element that has the specified value; otherwise, .{exception}
34 | /// contains no elements.
35 | """;
36 | }
37 |
38 | var types = new []
39 | {
40 | "byte", "sbyte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double"
41 | };
42 | #>
43 | namespace SimdLinq;
44 |
45 | using System.Runtime.InteropServices;
46 |
47 | public static partial class SimdLinqExtensions
48 | {
49 | <# foreach(var type in types) { #>
50 | <#= summary(type, true) #>
51 | public static bool Contains(this <#= type #>[] source, <#= type #> value)
52 | {
53 | ArgumentNullException.ThrowIfNull(source);
54 | return ContainsCore(new ReadOnlySpan<<#= type #>>(source), value);
55 | }
56 |
57 | <#= summary(type, true) #>
58 | public static bool Contains(this List<<#= type #>> source, <#= type #> value)
59 | {
60 | ArgumentNullException.ThrowIfNull(source);
61 | return ContainsCore((ReadOnlySpan<<#= type #>>)CollectionsMarshal.AsSpan(source), value);
62 | }
63 |
64 | <#= summary(type, true) #>
65 | public static bool Contains(this Memory<<#= type #>> source, <#= type #> value)
66 | {
67 | return ContainsCore((ReadOnlySpan<<#= type #>>)source.Span, value);
68 | }
69 |
70 | <#= summary(type, true) #>
71 | public static bool Contains(this ReadOnlyMemory<<#= type #>> source, <#= type #> value)
72 | {
73 | return ContainsCore(source.Span, value);
74 | }
75 |
76 | <#= summary(type, true) #>
77 | public static bool Contains(this Span<<#= type #>> source, <#= type #> value)
78 | {
79 | return ContainsCore((ReadOnlySpan<<#= type #>>)source, value);
80 | }
81 |
82 | <#= summary(type, true) #>
83 | public static bool Contains(this ReadOnlySpan<<#= type #>> source, <#= type #> value)
84 | {
85 | return ContainsCore(source, value);
86 | }
87 | <# } #>
88 | }
89 |
--------------------------------------------------------------------------------
/src/SimdLinq/SimdLinqExtensions.ContainsCore.cs:
--------------------------------------------------------------------------------
1 | using System.Numerics;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 | using System.Runtime.Intrinsics;
5 |
6 | namespace SimdLinq;
7 |
8 | public static partial class SimdLinqExtensions
9 | {
10 | static bool ContainsCore(ReadOnlySpan source, T value)
11 | where T : struct, INumber
12 | {
13 | T sum = T.Zero;
14 |
15 | if (!Vector128.IsHardwareAccelerated || source.Length < Vector128.Count)
16 | {
17 | // Not SIMD supported or small source.
18 | for (int i = 0; i < source.Length; i++)
19 | {
20 | if (source[i] == value)
21 | {
22 | return true;
23 | }
24 | }
25 | }
26 | else if (!Vector256.IsHardwareAccelerated || source.Length < Vector256.Count)
27 | {
28 | // Only 128bit SIMD supported or small source.
29 | ref var begin = ref MemoryMarshal.GetReference(source);
30 | ref var last = ref Unsafe.Add(ref begin, source.Length);
31 | ref var current = ref begin;
32 |
33 | var vectorValue = Vector128.Create(value);
34 | ref var to = ref Unsafe.Add(ref begin, source.Length - Vector128.Count);
35 | while (Unsafe.IsAddressLessThan(ref current, ref to))
36 | {
37 | if (Vector128.EqualsAny(Vector128.LoadUnsafe(ref current), vectorValue))
38 | {
39 | return true;
40 | }
41 | current = ref Unsafe.Add(ref current, Vector128.Count);
42 | }
43 | while (Unsafe.IsAddressLessThan(ref current, ref last))
44 | {
45 | if (current == value)
46 | {
47 | return true;
48 | }
49 | current = ref Unsafe.Add(ref current, 1);
50 | }
51 | }
52 | else
53 | {
54 | // 256bit SIMD supported
55 | ref var begin = ref MemoryMarshal.GetReference(source);
56 | ref var last = ref Unsafe.Add(ref begin, source.Length);
57 | ref var current = ref begin;
58 |
59 | var vectorValue = Vector256.Create(value);
60 | ref var to = ref Unsafe.Add(ref begin, source.Length - Vector256.Count);
61 | while (Unsafe.IsAddressLessThan(ref current, ref to))
62 | {
63 | if (Vector256.EqualsAny(Vector256.LoadUnsafe(ref current), vectorValue))
64 | {
65 | return true;
66 | }
67 | current = ref Unsafe.Add(ref current, Vector256.Count);
68 | }
69 | while (Unsafe.IsAddressLessThan(ref current, ref last))
70 | {
71 | if (current == value)
72 | {
73 | return true;
74 | }
75 | current = ref Unsafe.Add(ref current, 1);
76 | }
77 | }
78 |
79 | return false;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/SimdLinq/SimdLinqExtensions.LongSum.cs:
--------------------------------------------------------------------------------
1 | namespace SimdLinq;
2 |
3 | using System.Runtime.InteropServices;
4 |
5 | public static partial class SimdLinqExtensions
6 | {
7 | ///
8 | /// Computes the long sum of a sequence of values using SIMD acceleration.
9 | ///
10 | ///
11 | /// Note that unlike Linq.Sum this method is unchecked and will not throw an .
12 | ///
13 | /// A sequence of values to calculate the long sum of.
14 | /// The sum of the values in the sequence.
15 | /// is .
16 | public static long LongSum(this int[] source)
17 | {
18 | ArgumentNullException.ThrowIfNull(source);
19 | return LongSumCore(new ReadOnlySpan(source));
20 | }
21 |
22 | ///
23 | /// Computes the long sum of a sequence of values using SIMD acceleration.
24 | ///
25 | ///
26 | /// Note that unlike Linq.Sum this method is unchecked and will not throw an .
27 | ///
28 | /// A sequence of values to calculate the long sum of.
29 | /// The sum of the values in the sequence.
30 | /// is .
31 | public static long LongSum(this List source)
32 | {
33 | ArgumentNullException.ThrowIfNull(source);
34 | return LongSumCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
35 | }
36 |
37 | ///
38 | /// Computes the long sum of a sequence of values using SIMD acceleration.
39 | ///
40 | ///
41 | /// Note that unlike Linq.Sum this method is unchecked and will not throw an .
42 | ///
43 | /// A sequence of values to calculate the long sum of.
44 | /// The sum of the values in the sequence.
45 | public static long LongSum(this Memory source)
46 | {
47 | return LongSumCore((ReadOnlySpan)source.Span);
48 | }
49 |
50 | ///
51 | /// Computes the long sum of a sequence of values using SIMD acceleration.
52 | ///
53 | ///
54 | /// Note that unlike Linq.Sum this method is unchecked and will not throw an .
55 | ///
56 | /// A sequence of values to calculate the long sum of.
57 | /// The sum of the values in the sequence.
58 | public static long LongSum(this ReadOnlyMemory source)
59 | {
60 | return LongSumCore(source.Span);
61 | }
62 |
63 | ///
64 | /// Computes the long sum of a sequence of values using SIMD acceleration.
65 | ///
66 | ///
67 | /// Note that unlike Linq.Sum this method is unchecked and will not throw an .
68 | ///
69 | /// A sequence of values to calculate the long sum of.
70 | /// The sum of the values in the sequence.
71 | public static long LongSum(this Span source)
72 | {
73 | return LongSumCore((ReadOnlySpan)source);
74 | }
75 |
76 | ///
77 | /// Computes the long sum of a sequence of values using SIMD acceleration.
78 | ///
79 | ///
80 | /// Note that unlike Linq.Sum this method is unchecked and will not throw an .
81 | ///
82 | /// A sequence of values to calculate the long sum of.
83 | /// The sum of the values in the sequence.
84 | public static long LongSum(this ReadOnlySpan source)
85 | {
86 | return LongSumCore(source);
87 | }
88 |
89 | ///
90 | /// Computes the long sum of a sequence of values using SIMD acceleration.
91 | ///
92 | ///
93 | /// Note that unlike Linq.Sum this method is unchecked and will not throw an .
94 | ///
95 | /// A sequence of values to calculate the long sum of.
96 | /// The sum of the values in the sequence.
97 | /// is .
98 | public static ulong LongSum(this uint[] source)
99 | {
100 | ArgumentNullException.ThrowIfNull(source);
101 | return LongSumCore(new ReadOnlySpan(source));
102 | }
103 |
104 | ///
105 | /// Computes the long sum of a sequence of values using SIMD acceleration.
106 | ///
107 | ///
108 | /// Note that unlike Linq.Sum this method is unchecked and will not throw an .
109 | ///
110 | /// A sequence of values to calculate the long sum of.
111 | /// The sum of the values in the sequence.
112 | /// is .
113 | public static ulong LongSum(this List source)
114 | {
115 | ArgumentNullException.ThrowIfNull(source);
116 | return LongSumCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
117 | }
118 |
119 | ///
120 | /// Computes the long sum of a sequence of values using SIMD acceleration.
121 | ///
122 | ///
123 | /// Note that unlike Linq.Sum this method is unchecked and will not throw an .
124 | ///
125 | /// A sequence of values to calculate the long sum of.
126 | /// The sum of the values in the sequence.
127 | public static ulong LongSum(this Memory source)
128 | {
129 | return LongSumCore((ReadOnlySpan)source.Span);
130 | }
131 |
132 | ///
133 | /// Computes the long sum of a sequence of values using SIMD acceleration.
134 | ///
135 | ///
136 | /// Note that unlike Linq.Sum this method is unchecked and will not throw an .
137 | ///
138 | /// A sequence of values to calculate the long sum of.
139 | /// The sum of the values in the sequence.
140 | public static ulong LongSum(this ReadOnlyMemory source)
141 | {
142 | return LongSumCore(source.Span);
143 | }
144 |
145 | ///
146 | /// Computes the long sum of a sequence of values using SIMD acceleration.
147 | ///
148 | ///
149 | /// Note that unlike Linq.Sum this method is unchecked and will not throw an .
150 | ///
151 | /// A sequence of values to calculate the long sum of.
152 | /// The sum of the values in the sequence.
153 | public static ulong LongSum(this Span source)
154 | {
155 | return LongSumCore((ReadOnlySpan)source);
156 | }
157 |
158 | ///
159 | /// Computes the long sum of a sequence of values using SIMD acceleration.
160 | ///
161 | ///
162 | /// Note that unlike Linq.Sum this method is unchecked and will not throw an .
163 | ///
164 | /// A sequence of values to calculate the long sum of.
165 | /// The sum of the values in the sequence.
166 | public static ulong LongSum(this ReadOnlySpan source)
167 | {
168 | return LongSumCore(source);
169 | }
170 |
171 | }
172 |
173 |
--------------------------------------------------------------------------------
/src/SimdLinq/SimdLinqExtensions.LongSum.tt:
--------------------------------------------------------------------------------
1 | <#@ template debug="false" hostspecific="false" language="C#" #>
2 | <#@ assembly name="System.Core" #>
3 | <#@ import namespace="System.Linq" #>
4 | <#@ import namespace="System.Text" #>
5 | <#@ import namespace="System.Collections.Generic" #>
6 | <#@ output extension=".cs" #>
7 | <#
8 | string summary(string x, bool nullable)
9 | {
10 | var exception = "";
11 | if (nullable)
12 | {
13 | exception += """
14 |
15 | /// is .
16 | """;
17 | }
18 |
19 | return $"""
20 | ///
21 | /// Computes the long sum of a sequence of values using SIMD acceleration.
22 | ///
23 | ///
24 | /// Note that unlike Linq.Sum this method is unchecked and will not throw an .
25 | ///
26 | /// A sequence of values to calculate the long sum of.
27 | /// The sum of the values in the sequence.{exception}
28 | """;
29 | }
30 |
31 | var retType = (string x) => x is "int" ? "long" : "ulong";;
32 |
33 | var methodName = "LongSum";
34 | var types = new []
35 | {
36 | "int", "uint"
37 | };
38 | #>
39 | namespace SimdLinq;
40 |
41 | using System.Runtime.InteropServices;
42 |
43 | <#@ include file=".\SimdLinqExtensions.ttinclude" #>
44 |
--------------------------------------------------------------------------------
/src/SimdLinq/SimdLinqExtensions.Max.cs:
--------------------------------------------------------------------------------
1 | namespace SimdLinq;
2 |
3 | using System.Runtime.InteropServices;
4 |
5 | public static partial class SimdLinqExtensions
6 | {
7 | ///
8 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
9 | ///
10 | /// A sequence of values to determine the maximum value of.
11 | /// The maximum value in the sequence.
12 | /// is .
13 | /// contains no elements.
14 | public static byte Max(this byte[] source)
15 | {
16 | ArgumentNullException.ThrowIfNull(source);
17 | return MaxCore(new ReadOnlySpan(source));
18 | }
19 |
20 | ///
21 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
22 | ///
23 | /// A sequence of values to determine the maximum value of.
24 | /// The maximum value in the sequence.
25 | /// is .
26 | /// contains no elements.
27 | public static byte Max(this List source)
28 | {
29 | ArgumentNullException.ThrowIfNull(source);
30 | return MaxCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
31 | }
32 |
33 | ///
34 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
35 | ///
36 | /// A sequence of values to determine the maximum value of.
37 | /// The maximum value in the sequence.
38 | /// contains no elements.
39 | public static byte Max(this Memory source)
40 | {
41 | return MaxCore((ReadOnlySpan)source.Span);
42 | }
43 |
44 | ///
45 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
46 | ///
47 | /// A sequence of values to determine the maximum value of.
48 | /// The maximum value in the sequence.
49 | /// contains no elements.
50 | public static byte Max(this ReadOnlyMemory source)
51 | {
52 | return MaxCore(source.Span);
53 | }
54 |
55 | ///
56 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
57 | ///
58 | /// A sequence of values to determine the maximum value of.
59 | /// The maximum value in the sequence.
60 | /// contains no elements.
61 | public static byte Max(this Span source)
62 | {
63 | return MaxCore((ReadOnlySpan)source);
64 | }
65 |
66 | ///
67 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
68 | ///
69 | /// A sequence of values to determine the maximum value of.
70 | /// The maximum value in the sequence.
71 | /// contains no elements.
72 | public static byte Max(this ReadOnlySpan source)
73 | {
74 | return MaxCore(source);
75 | }
76 |
77 | ///
78 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
79 | ///
80 | /// A sequence of values to determine the maximum value of.
81 | /// The maximum value in the sequence.
82 | /// is .
83 | /// contains no elements.
84 | public static sbyte Max(this sbyte[] source)
85 | {
86 | ArgumentNullException.ThrowIfNull(source);
87 | return MaxCore(new ReadOnlySpan(source));
88 | }
89 |
90 | ///
91 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
92 | ///
93 | /// A sequence of values to determine the maximum value of.
94 | /// The maximum value in the sequence.
95 | /// is .
96 | /// contains no elements.
97 | public static sbyte Max(this List source)
98 | {
99 | ArgumentNullException.ThrowIfNull(source);
100 | return MaxCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
101 | }
102 |
103 | ///
104 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
105 | ///
106 | /// A sequence of values to determine the maximum value of.
107 | /// The maximum value in the sequence.
108 | /// contains no elements.
109 | public static sbyte Max(this Memory source)
110 | {
111 | return MaxCore((ReadOnlySpan)source.Span);
112 | }
113 |
114 | ///
115 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
116 | ///
117 | /// A sequence of values to determine the maximum value of.
118 | /// The maximum value in the sequence.
119 | /// contains no elements.
120 | public static sbyte Max(this ReadOnlyMemory source)
121 | {
122 | return MaxCore(source.Span);
123 | }
124 |
125 | ///
126 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
127 | ///
128 | /// A sequence of values to determine the maximum value of.
129 | /// The maximum value in the sequence.
130 | /// contains no elements.
131 | public static sbyte Max(this Span source)
132 | {
133 | return MaxCore((ReadOnlySpan)source);
134 | }
135 |
136 | ///
137 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
138 | ///
139 | /// A sequence of values to determine the maximum value of.
140 | /// The maximum value in the sequence.
141 | /// contains no elements.
142 | public static sbyte Max(this ReadOnlySpan source)
143 | {
144 | return MaxCore(source);
145 | }
146 |
147 | ///
148 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
149 | ///
150 | /// A sequence of values to determine the maximum value of.
151 | /// The maximum value in the sequence.
152 | /// is .
153 | /// contains no elements.
154 | public static short Max(this short[] source)
155 | {
156 | ArgumentNullException.ThrowIfNull(source);
157 | return MaxCore(new ReadOnlySpan(source));
158 | }
159 |
160 | ///
161 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
162 | ///
163 | /// A sequence of values to determine the maximum value of.
164 | /// The maximum value in the sequence.
165 | /// is .
166 | /// contains no elements.
167 | public static short Max(this List source)
168 | {
169 | ArgumentNullException.ThrowIfNull(source);
170 | return MaxCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
171 | }
172 |
173 | ///
174 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
175 | ///
176 | /// A sequence of values to determine the maximum value of.
177 | /// The maximum value in the sequence.
178 | /// contains no elements.
179 | public static short Max(this Memory source)
180 | {
181 | return MaxCore((ReadOnlySpan)source.Span);
182 | }
183 |
184 | ///
185 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
186 | ///
187 | /// A sequence of values to determine the maximum value of.
188 | /// The maximum value in the sequence.
189 | /// contains no elements.
190 | public static short Max(this ReadOnlyMemory source)
191 | {
192 | return MaxCore(source.Span);
193 | }
194 |
195 | ///
196 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
197 | ///
198 | /// A sequence of values to determine the maximum value of.
199 | /// The maximum value in the sequence.
200 | /// contains no elements.
201 | public static short Max(this Span source)
202 | {
203 | return MaxCore((ReadOnlySpan)source);
204 | }
205 |
206 | ///
207 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
208 | ///
209 | /// A sequence of values to determine the maximum value of.
210 | /// The maximum value in the sequence.
211 | /// contains no elements.
212 | public static short Max(this ReadOnlySpan source)
213 | {
214 | return MaxCore(source);
215 | }
216 |
217 | ///
218 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
219 | ///
220 | /// A sequence of values to determine the maximum value of.
221 | /// The maximum value in the sequence.
222 | /// is .
223 | /// contains no elements.
224 | public static ushort Max(this ushort[] source)
225 | {
226 | ArgumentNullException.ThrowIfNull(source);
227 | return MaxCore(new ReadOnlySpan(source));
228 | }
229 |
230 | ///
231 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
232 | ///
233 | /// A sequence of values to determine the maximum value of.
234 | /// The maximum value in the sequence.
235 | /// is .
236 | /// contains no elements.
237 | public static ushort Max(this List source)
238 | {
239 | ArgumentNullException.ThrowIfNull(source);
240 | return MaxCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
241 | }
242 |
243 | ///
244 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
245 | ///
246 | /// A sequence of values to determine the maximum value of.
247 | /// The maximum value in the sequence.
248 | /// contains no elements.
249 | public static ushort Max(this Memory source)
250 | {
251 | return MaxCore((ReadOnlySpan)source.Span);
252 | }
253 |
254 | ///
255 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
256 | ///
257 | /// A sequence of values to determine the maximum value of.
258 | /// The maximum value in the sequence.
259 | /// contains no elements.
260 | public static ushort Max(this ReadOnlyMemory source)
261 | {
262 | return MaxCore(source.Span);
263 | }
264 |
265 | ///
266 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
267 | ///
268 | /// A sequence of values to determine the maximum value of.
269 | /// The maximum value in the sequence.
270 | /// contains no elements.
271 | public static ushort Max(this Span source)
272 | {
273 | return MaxCore((ReadOnlySpan)source);
274 | }
275 |
276 | ///
277 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
278 | ///
279 | /// A sequence of values to determine the maximum value of.
280 | /// The maximum value in the sequence.
281 | /// contains no elements.
282 | public static ushort Max(this ReadOnlySpan source)
283 | {
284 | return MaxCore(source);
285 | }
286 |
287 | ///
288 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
289 | ///
290 | /// A sequence of values to determine the maximum value of.
291 | /// The maximum value in the sequence.
292 | /// is .
293 | /// contains no elements.
294 | public static int Max(this int[] source)
295 | {
296 | ArgumentNullException.ThrowIfNull(source);
297 | return MaxCore(new ReadOnlySpan(source));
298 | }
299 |
300 | ///
301 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
302 | ///
303 | /// A sequence of values to determine the maximum value of.
304 | /// The maximum value in the sequence.
305 | /// is .
306 | /// contains no elements.
307 | public static int Max(this List source)
308 | {
309 | ArgumentNullException.ThrowIfNull(source);
310 | return MaxCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
311 | }
312 |
313 | ///
314 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
315 | ///
316 | /// A sequence of values to determine the maximum value of.
317 | /// The maximum value in the sequence.
318 | /// contains no elements.
319 | public static int Max(this Memory source)
320 | {
321 | return MaxCore((ReadOnlySpan)source.Span);
322 | }
323 |
324 | ///
325 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
326 | ///
327 | /// A sequence of values to determine the maximum value of.
328 | /// The maximum value in the sequence.
329 | /// contains no elements.
330 | public static int Max(this ReadOnlyMemory source)
331 | {
332 | return MaxCore(source.Span);
333 | }
334 |
335 | ///
336 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
337 | ///
338 | /// A sequence of values to determine the maximum value of.
339 | /// The maximum value in the sequence.
340 | /// contains no elements.
341 | public static int Max(this Span source)
342 | {
343 | return MaxCore((ReadOnlySpan)source);
344 | }
345 |
346 | ///
347 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
348 | ///
349 | /// A sequence of values to determine the maximum value of.
350 | /// The maximum value in the sequence.
351 | /// contains no elements.
352 | public static int Max(this ReadOnlySpan source)
353 | {
354 | return MaxCore(source);
355 | }
356 |
357 | ///
358 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
359 | ///
360 | /// A sequence of values to determine the maximum value of.
361 | /// The maximum value in the sequence.
362 | /// is .
363 | /// contains no elements.
364 | public static uint Max(this uint[] source)
365 | {
366 | ArgumentNullException.ThrowIfNull(source);
367 | return MaxCore(new ReadOnlySpan(source));
368 | }
369 |
370 | ///
371 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
372 | ///
373 | /// A sequence of values to determine the maximum value of.
374 | /// The maximum value in the sequence.
375 | /// is .
376 | /// contains no elements.
377 | public static uint Max(this List source)
378 | {
379 | ArgumentNullException.ThrowIfNull(source);
380 | return MaxCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
381 | }
382 |
383 | ///
384 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
385 | ///
386 | /// A sequence of values to determine the maximum value of.
387 | /// The maximum value in the sequence.
388 | /// contains no elements.
389 | public static uint Max(this Memory source)
390 | {
391 | return MaxCore((ReadOnlySpan)source.Span);
392 | }
393 |
394 | ///
395 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
396 | ///
397 | /// A sequence of values to determine the maximum value of.
398 | /// The maximum value in the sequence.
399 | /// contains no elements.
400 | public static uint Max(this ReadOnlyMemory source)
401 | {
402 | return MaxCore(source.Span);
403 | }
404 |
405 | ///
406 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
407 | ///
408 | /// A sequence of values to determine the maximum value of.
409 | /// The maximum value in the sequence.
410 | /// contains no elements.
411 | public static uint Max(this Span source)
412 | {
413 | return MaxCore((ReadOnlySpan)source);
414 | }
415 |
416 | ///
417 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
418 | ///
419 | /// A sequence of values to determine the maximum value of.
420 | /// The maximum value in the sequence.
421 | /// contains no elements.
422 | public static uint Max(this ReadOnlySpan source)
423 | {
424 | return MaxCore(source);
425 | }
426 |
427 | ///
428 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
429 | ///
430 | /// A sequence of values to determine the maximum value of.
431 | /// The maximum value in the sequence.
432 | /// is .
433 | /// contains no elements.
434 | public static long Max(this long[] source)
435 | {
436 | ArgumentNullException.ThrowIfNull(source);
437 | return MaxCore(new ReadOnlySpan(source));
438 | }
439 |
440 | ///
441 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
442 | ///
443 | /// A sequence of values to determine the maximum value of.
444 | /// The maximum value in the sequence.
445 | /// is .
446 | /// contains no elements.
447 | public static long Max(this List source)
448 | {
449 | ArgumentNullException.ThrowIfNull(source);
450 | return MaxCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
451 | }
452 |
453 | ///
454 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
455 | ///
456 | /// A sequence of values to determine the maximum value of.
457 | /// The maximum value in the sequence.
458 | /// contains no elements.
459 | public static long Max(this Memory source)
460 | {
461 | return MaxCore((ReadOnlySpan)source.Span);
462 | }
463 |
464 | ///
465 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
466 | ///
467 | /// A sequence of values to determine the maximum value of.
468 | /// The maximum value in the sequence.
469 | /// contains no elements.
470 | public static long Max(this ReadOnlyMemory source)
471 | {
472 | return MaxCore(source.Span);
473 | }
474 |
475 | ///
476 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
477 | ///
478 | /// A sequence of values to determine the maximum value of.
479 | /// The maximum value in the sequence.
480 | /// contains no elements.
481 | public static long Max(this Span source)
482 | {
483 | return MaxCore((ReadOnlySpan)source);
484 | }
485 |
486 | ///
487 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
488 | ///
489 | /// A sequence of values to determine the maximum value of.
490 | /// The maximum value in the sequence.
491 | /// contains no elements.
492 | public static long Max(this ReadOnlySpan source)
493 | {
494 | return MaxCore(source);
495 | }
496 |
497 | ///
498 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
499 | ///
500 | /// A sequence of values to determine the maximum value of.
501 | /// The maximum value in the sequence.
502 | /// is .
503 | /// contains no elements.
504 | public static ulong Max(this ulong[] source)
505 | {
506 | ArgumentNullException.ThrowIfNull(source);
507 | return MaxCore(new ReadOnlySpan(source));
508 | }
509 |
510 | ///
511 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
512 | ///
513 | /// A sequence of values to determine the maximum value of.
514 | /// The maximum value in the sequence.
515 | /// is .
516 | /// contains no elements.
517 | public static ulong Max(this List source)
518 | {
519 | ArgumentNullException.ThrowIfNull(source);
520 | return MaxCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
521 | }
522 |
523 | ///
524 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
525 | ///
526 | /// A sequence of values to determine the maximum value of.
527 | /// The maximum value in the sequence.
528 | /// contains no elements.
529 | public static ulong Max(this Memory source)
530 | {
531 | return MaxCore((ReadOnlySpan)source.Span);
532 | }
533 |
534 | ///
535 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
536 | ///
537 | /// A sequence of values to determine the maximum value of.
538 | /// The maximum value in the sequence.
539 | /// contains no elements.
540 | public static ulong Max(this ReadOnlyMemory source)
541 | {
542 | return MaxCore(source.Span);
543 | }
544 |
545 | ///
546 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
547 | ///
548 | /// A sequence of values to determine the maximum value of.
549 | /// The maximum value in the sequence.
550 | /// contains no elements.
551 | public static ulong Max(this Span source)
552 | {
553 | return MaxCore((ReadOnlySpan)source);
554 | }
555 |
556 | ///
557 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
558 | ///
559 | /// A sequence of values to determine the maximum value of.
560 | /// The maximum value in the sequence.
561 | /// contains no elements.
562 | public static ulong Max(this ReadOnlySpan source)
563 | {
564 | return MaxCore(source);
565 | }
566 |
567 | ///
568 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
569 | ///
570 | /// This method does not check for / values and should not be used when may contain one.
571 | /// A sequence of values to determine the maximum value of.
572 | /// The maximum value in the sequence.
573 | /// is .
574 | /// contains no elements.
575 | public static float Max(this float[] source)
576 | {
577 | ArgumentNullException.ThrowIfNull(source);
578 | return MaxCore(new ReadOnlySpan(source));
579 | }
580 |
581 | ///
582 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
583 | ///
584 | /// This method does not check for / values and should not be used when may contain one.
585 | /// A sequence of values to determine the maximum value of.
586 | /// The maximum value in the sequence.
587 | /// is .
588 | /// contains no elements.
589 | public static float Max(this List source)
590 | {
591 | ArgumentNullException.ThrowIfNull(source);
592 | return MaxCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
593 | }
594 |
595 | ///
596 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
597 | ///
598 | /// This method does not check for / values and should not be used when may contain one.
599 | /// A sequence of values to determine the maximum value of.
600 | /// The maximum value in the sequence.
601 | /// contains no elements.
602 | public static float Max(this Memory source)
603 | {
604 | return MaxCore((ReadOnlySpan)source.Span);
605 | }
606 |
607 | ///
608 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
609 | ///
610 | /// This method does not check for / values and should not be used when may contain one.
611 | /// A sequence of values to determine the maximum value of.
612 | /// The maximum value in the sequence.
613 | /// contains no elements.
614 | public static float Max(this ReadOnlyMemory source)
615 | {
616 | return MaxCore(source.Span);
617 | }
618 |
619 | ///
620 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
621 | ///
622 | /// This method does not check for / values and should not be used when may contain one.
623 | /// A sequence of values to determine the maximum value of.
624 | /// The maximum value in the sequence.
625 | /// contains no elements.
626 | public static float Max(this Span source)
627 | {
628 | return MaxCore((ReadOnlySpan)source);
629 | }
630 |
631 | ///
632 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
633 | ///
634 | /// This method does not check for / values and should not be used when may contain one.
635 | /// A sequence of values to determine the maximum value of.
636 | /// The maximum value in the sequence.
637 | /// contains no elements.
638 | public static float Max(this ReadOnlySpan source)
639 | {
640 | return MaxCore(source);
641 | }
642 |
643 | ///
644 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
645 | ///
646 | /// This method does not check for / values and should not be used when may contain one.
647 | /// A sequence of values to determine the maximum value of.
648 | /// The maximum value in the sequence.
649 | /// is .
650 | /// contains no elements.
651 | public static double Max(this double[] source)
652 | {
653 | ArgumentNullException.ThrowIfNull(source);
654 | return MaxCore(new ReadOnlySpan(source));
655 | }
656 |
657 | ///
658 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
659 | ///
660 | /// This method does not check for / values and should not be used when may contain one.
661 | /// A sequence of values to determine the maximum value of.
662 | /// The maximum value in the sequence.
663 | /// is .
664 | /// contains no elements.
665 | public static double Max(this List source)
666 | {
667 | ArgumentNullException.ThrowIfNull(source);
668 | return MaxCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
669 | }
670 |
671 | ///
672 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
673 | ///
674 | /// This method does not check for / values and should not be used when may contain one.
675 | /// A sequence of values to determine the maximum value of.
676 | /// The maximum value in the sequence.
677 | /// contains no elements.
678 | public static double Max(this Memory source)
679 | {
680 | return MaxCore((ReadOnlySpan)source.Span);
681 | }
682 |
683 | ///
684 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
685 | ///
686 | /// This method does not check for / values and should not be used when may contain one.
687 | /// A sequence of values to determine the maximum value of.
688 | /// The maximum value in the sequence.
689 | /// contains no elements.
690 | public static double Max(this ReadOnlyMemory source)
691 | {
692 | return MaxCore(source.Span);
693 | }
694 |
695 | ///
696 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
697 | ///
698 | /// This method does not check for / values and should not be used when may contain one.
699 | /// A sequence of values to determine the maximum value of.
700 | /// The maximum value in the sequence.
701 | /// contains no elements.
702 | public static double Max(this Span source)
703 | {
704 | return MaxCore((ReadOnlySpan)source);
705 | }
706 |
707 | ///
708 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
709 | ///
710 | /// This method does not check for / values and should not be used when may contain one.
711 | /// A sequence of values to determine the maximum value of.
712 | /// The maximum value in the sequence.
713 | /// contains no elements.
714 | public static double Max(this ReadOnlySpan source)
715 | {
716 | return MaxCore(source);
717 | }
718 |
719 | }
720 |
721 |
--------------------------------------------------------------------------------
/src/SimdLinq/SimdLinqExtensions.Max.tt:
--------------------------------------------------------------------------------
1 | <#@ template debug="false" hostspecific="false" language="C#" #>
2 | <#@ assembly name="System.Core" #>
3 | <#@ import namespace="System.Linq" #>
4 | <#@ import namespace="System.Text" #>
5 | <#@ import namespace="System.Collections.Generic" #>
6 | <#@ output extension=".cs" #>
7 | <#
8 | string summary(string x, bool nullable)
9 | {
10 | var remark = "";
11 | if(x is "double" or "float")
12 | {
13 | remark += """
14 |
15 | /// This method does not check for / values and should not be used when may contain one.
16 | """;
17 | }
18 | var exception = "";
19 | if (nullable)
20 | {
21 | exception += """
22 |
23 | /// is .
24 | """;
25 | }
26 |
27 | return $"""
28 | ///
29 | /// Returns the maximum value in a sequence of values using SIMD acceleration.
30 | /// {remark}
31 | /// A sequence of values to determine the maximum value of.
32 | /// The maximum value in the sequence.{exception}
33 | /// contains no elements.
34 | """;
35 | }
36 |
37 | var retType = (string x) => x;
38 |
39 | var methodName = "Max";
40 | var types = new []
41 | {
42 | "byte", "sbyte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double"
43 | };
44 | #>
45 | namespace SimdLinq;
46 |
47 | using System.Runtime.InteropServices;
48 |
49 | <#@ include file=".\SimdLinqExtensions.ttinclude" #>
50 |
--------------------------------------------------------------------------------
/src/SimdLinq/SimdLinqExtensions.Min.tt:
--------------------------------------------------------------------------------
1 | <#@ template debug="false" hostspecific="false" language="C#" #>
2 | <#@ assembly name="System.Core" #>
3 | <#@ import namespace="System.Linq" #>
4 | <#@ import namespace="System.Text" #>
5 | <#@ import namespace="System.Collections.Generic" #>
6 | <#@ output extension=".cs" #>
7 | <#
8 | string summary(string x, bool nullable)
9 | {
10 | var remark = "";
11 | if(x is "double" or "float")
12 | {
13 | remark += """
14 |
15 | /// This method does not check for / values and should not be used when may contain one.
16 | """;
17 | }
18 | var exception = "";
19 | if (nullable)
20 | {
21 | exception += """
22 |
23 | /// is .
24 | """;
25 | }
26 |
27 | return $"""
28 | ///
29 | /// Returns the minimum value in a sequence of values using SIMD acceleration.
30 | /// {remark}
31 | /// A sequence of values to determine the minimum value of.
32 | /// The minimum value in the sequence.{exception}
33 | /// contains no elements.
34 | """;
35 | }
36 |
37 | var retType = (string x) => x;
38 |
39 | var methodName = "Min";
40 | var types = new []
41 | {
42 | "byte", "sbyte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double"
43 | };
44 | #>
45 | namespace SimdLinq;
46 |
47 | using System.Runtime.InteropServices;
48 |
49 | <#@ include file=".\SimdLinqExtensions.ttinclude" #>
50 |
--------------------------------------------------------------------------------
/src/SimdLinq/SimdLinqExtensions.MinMax.tt:
--------------------------------------------------------------------------------
1 | <#@ template debug="false" hostspecific="false" language="C#" #>
2 | <#@ assembly name="System.Core" #>
3 | <#@ import namespace="System.Linq" #>
4 | <#@ import namespace="System.Text" #>
5 | <#@ import namespace="System.Collections.Generic" #>
6 | <#@ output extension=".cs" #>
7 | <#
8 | string summary(string x, bool nullable)
9 | {
10 | var remark = "";
11 | if(x is "double" or "float")
12 | {
13 | remark += """
14 |
15 | /// This method does not check for / values and should not be used when may contain one.
16 | """;
17 | }
18 | var exception = "";
19 | if (nullable)
20 | {
21 | exception += """
22 |
23 | /// is .
24 | """;
25 | }
26 |
27 | return $"""
28 | ///
29 | /// Returns the minimum and maximum values in a sequence of values using SIMD acceleration.
30 | /// {remark}
31 | /// A sequence of values to determine the minimum and maximum value of.
32 | /// A tuple of the minimum and maximum values in the sequence.{exception}
33 | /// contains no elements.
34 | """;
35 | }
36 |
37 | var retType = (string x) => $"({x} Min, {x} Max)";
38 |
39 | var methodName = "MinMax";
40 | var types = new []
41 | {
42 | "byte", "sbyte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double"
43 | };
44 | #>
45 | namespace SimdLinq;
46 |
47 | using System.Runtime.InteropServices;
48 |
49 | <#@ include file=".\SimdLinqExtensions.ttinclude" #>
50 |
--------------------------------------------------------------------------------
/src/SimdLinq/SimdLinqExtensions.MinMaxCore.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using System.Numerics;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Runtime.Intrinsics;
6 | using System.Runtime.Intrinsics.X86;
7 |
8 | namespace SimdLinq;
9 |
10 | public static partial class SimdLinqExtensions
11 | {
12 | static T MinCore(ReadOnlySpan source)
13 | where T : struct, INumber
14 | {
15 | if (source.IsEmpty) ThrowNoElements();
16 |
17 | if (!Vector128.IsHardwareAccelerated || source.Length < Vector128.Count)
18 | {
19 | // Not SIMD supported or small source.
20 | var min = source[0];
21 | for (int i = 1; i < source.Length; i++)
22 | {
23 | if (source[i] < min)
24 | {
25 | min = source[i];
26 | }
27 | }
28 | return min;
29 | }
30 | else if (!Vector256.IsHardwareAccelerated || source.Length < Vector256.Count)
31 | {
32 | // Only 128bit SIMD supported or small source.
33 | ref var current = ref MemoryMarshal.GetReference(source);
34 | ref var to = ref Unsafe.Add(ref current, source.Length - Vector128.Count);
35 |
36 | var vectorMin = Vector128.LoadUnsafe(ref current);
37 | current = ref Unsafe.Add(ref current, Vector128.Count);
38 | while (Unsafe.IsAddressLessThan(ref current, ref to))
39 | {
40 | vectorMin = Vector128.Min(vectorMin, Vector128.LoadUnsafe(ref current));
41 | current = ref Unsafe.Add(ref current, Vector128.Count);
42 | }
43 | vectorMin = Vector128.Min(vectorMin, Vector128.LoadUnsafe(ref to));
44 |
45 | var min = vectorMin[0];
46 | for (int i = 1; i < Vector128.Count; i++)
47 | {
48 | if (vectorMin[i] < min)
49 | {
50 | min = vectorMin[i];
51 | }
52 | }
53 | return min;
54 | }
55 | else
56 | {
57 | // 256bit SIMD supported
58 | ref var current = ref MemoryMarshal.GetReference(source);
59 | ref var to = ref Unsafe.Add(ref current, source.Length - Vector256.Count);
60 |
61 | var vectorMin = Vector256.LoadUnsafe(ref current);
62 | current = ref Unsafe.Add(ref current, Vector256.Count);
63 | while (Unsafe.IsAddressLessThan(ref current, ref to))
64 | {
65 | vectorMin = Vector256.Min(vectorMin, Vector256.LoadUnsafe(ref current));
66 | current = ref Unsafe.Add(ref current, Vector256.Count);
67 | }
68 | vectorMin = Vector256.Min(vectorMin, Vector256.LoadUnsafe(ref to));
69 |
70 | var min = vectorMin[0];
71 | for (int i = 1; i < Vector256.Count; i++)
72 | {
73 | if (vectorMin[i] < min)
74 | {
75 | min = vectorMin[i];
76 | }
77 | }
78 | return min;
79 | }
80 | }
81 |
82 | static T MaxCore(ReadOnlySpan source)
83 | where T : struct, INumber
84 | {
85 | if (source.IsEmpty) ThrowNoElements();
86 |
87 | if (!Vector128.IsHardwareAccelerated || source.Length < Vector128.Count)
88 | {
89 | // Not SIMD supported or small source.
90 | var max = source[0];
91 | for (int i = 1; i < source.Length; i++)
92 | {
93 | if (source[i] > max)
94 | {
95 | max = source[i];
96 | }
97 | }
98 | return max;
99 | }
100 | else if (!Vector256.IsHardwareAccelerated || source.Length < Vector256.Count)
101 | {
102 | // Only 128bit SIMD supported or small source.
103 | ref var current = ref MemoryMarshal.GetReference(source);
104 | ref var to = ref Unsafe.Add(ref current, source.Length - Vector128.Count);
105 |
106 | var vectorMax = Vector128.LoadUnsafe(ref current);
107 | current = ref Unsafe.Add(ref current, Vector128.Count);
108 | while (Unsafe.IsAddressLessThan(ref current, ref to))
109 | {
110 | vectorMax = Vector128.Max(vectorMax, Vector128.LoadUnsafe(ref current));
111 | current = ref Unsafe.Add(ref current, Vector128.Count);
112 | }
113 | vectorMax = Vector128.Max(vectorMax, Vector128.LoadUnsafe(ref to));
114 |
115 | var max = vectorMax[0];
116 | for (int i = 1; i < Vector128.Count; i++)
117 | {
118 | if (vectorMax[i] > max)
119 | {
120 | max = vectorMax[i];
121 | }
122 | }
123 | return max;
124 | }
125 | else
126 | {
127 | // 256bit SIMD supported
128 | ref var current = ref MemoryMarshal.GetReference(source);
129 | ref var to = ref Unsafe.Add(ref current, source.Length - Vector256.Count);
130 |
131 | var vectorMax = Vector256.LoadUnsafe(ref current);
132 | current = ref Unsafe.Add(ref current, Vector256.Count);
133 | while (Unsafe.IsAddressLessThan(ref current, ref to))
134 | {
135 | vectorMax = Vector256.Max(vectorMax, Vector256.LoadUnsafe(ref current));
136 | current = ref Unsafe.Add(ref current, Vector256.Count);
137 | }
138 | vectorMax = Vector256.Max(vectorMax, Vector256.LoadUnsafe(ref to));
139 |
140 | var max = vectorMax[0];
141 | for (int i = 1; i < Vector256.Count; i++)
142 | {
143 | if (vectorMax[i] > max)
144 | {
145 | max = vectorMax[i];
146 | }
147 | }
148 | return max;
149 | }
150 | }
151 |
152 | static (T Min, T Max) MinMaxCore(ReadOnlySpan source)
153 | where T : struct, INumber
154 | {
155 | if (source.IsEmpty) ThrowNoElements();
156 |
157 | if (!Vector128.IsHardwareAccelerated || source.Length < Vector128.Count)
158 | {
159 | // Not SIMD supported or small source.
160 | var min = source[0];
161 | var max = min;
162 | for (int i = 1; i < source.Length; i++)
163 | {
164 | if (source[i] < min)
165 | {
166 | min = source[i];
167 | }
168 | if (source[i] > max)
169 | {
170 | max = source[i];
171 | }
172 | }
173 | return (min, max);
174 | }
175 | else if (!Vector256.IsHardwareAccelerated || source.Length < Vector256.Count)
176 | {
177 | // Only 128bit SIMD supported or small source.
178 | ref var current = ref MemoryMarshal.GetReference(source);
179 | ref var to = ref Unsafe.Add(ref current, source.Length - Vector128.Count);
180 |
181 | var vectorMin = Vector128.LoadUnsafe(ref current);
182 | var vectorMax = vectorMin;
183 | current = ref Unsafe.Add(ref current, Vector128.Count);
184 | while (Unsafe.IsAddressLessThan(ref current, ref to))
185 | {
186 | vectorMin = Vector128.Min(vectorMin, Vector128.LoadUnsafe(ref current));
187 | vectorMax = Vector128.Max(vectorMax, Vector128.LoadUnsafe(ref current));
188 | current = ref Unsafe.Add(ref current, Vector128.Count);
189 | }
190 | vectorMin = Vector128.Min(vectorMin, Vector128.LoadUnsafe(ref to));
191 | vectorMax = Vector128.Max(vectorMax, Vector128.LoadUnsafe(ref to));
192 |
193 | var min = vectorMin[0];
194 | var max = vectorMax[0];
195 | for (int i = 1; i < Vector128.Count; i++)
196 | {
197 | if (vectorMin[i] < min)
198 | {
199 | min = vectorMin[i];
200 | }
201 | if (vectorMax[i] > max)
202 | {
203 | max = vectorMax[i];
204 | }
205 | }
206 | return (min, max);
207 | }
208 | else
209 | {
210 | // 256bit SIMD supported
211 | ref var current = ref MemoryMarshal.GetReference(source);
212 | ref var to = ref Unsafe.Add(ref current, source.Length - Vector256.Count);
213 |
214 | var vectorMin = Vector256.LoadUnsafe(ref current);
215 | var vectorMax = vectorMin;
216 | current = ref Unsafe.Add(ref current, Vector256.Count);
217 | while (Unsafe.IsAddressLessThan(ref current, ref to))
218 | {
219 | vectorMin = Vector256.Min(vectorMin, Vector256.LoadUnsafe(ref current));
220 | vectorMax = Vector256.Max(vectorMax, Vector256.LoadUnsafe(ref current));
221 | current = ref Unsafe.Add(ref current, Vector256.Count);
222 | }
223 | vectorMin = Vector256.Min(vectorMin, Vector256.LoadUnsafe(ref to));
224 | vectorMax = Vector256.Max(vectorMax, Vector256.LoadUnsafe(ref to));
225 |
226 | var min = vectorMin[0];
227 | var max = vectorMax[0];
228 | for (int i = 1; i < Vector256.Count; i++)
229 | {
230 | if (vectorMin[i] < min)
231 | {
232 | min = vectorMin[i];
233 | }
234 | if (vectorMax[i] > max)
235 | {
236 | max = vectorMax[i];
237 | }
238 | }
239 | return (min, max);
240 | }
241 | }
242 | }
243 |
--------------------------------------------------------------------------------
/src/SimdLinq/SimdLinqExtensions.SequenceEqual.tt:
--------------------------------------------------------------------------------
1 | <#@ template debug="false" hostspecific="false" language="C#" #>
2 | <#@ assembly name="System.Core" #>
3 | <#@ import namespace="System.Linq" #>
4 | <#@ import namespace="System.Text" #>
5 | <#@ import namespace="System.Collections.Generic" #>
6 | <#@ output extension=".cs" #>
7 | <#
8 | string summary(string x, bool firstNull, bool secondNull)
9 | {
10 | var exception = "";
11 | if (firstNull)
12 | {
13 | exception += """
14 |
15 | /// is .
16 | """;
17 | }
18 | if (secondNull)
19 | {
20 | exception += """
21 |
22 | /// is .
23 | """;
24 | }
25 |
26 | return $$"""
27 | ///
28 | /// Determines whether two sequences are equal by comparing the elements using the default .
29 | ///
30 | /// The first sequence to compare.
31 | /// The second sequence to compare.
32 | /// if the two sequences are equal; otherwise, .{{exception}}
33 | """;
34 | }
35 | var types = new []
36 | {
37 | "byte", "sbyte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double"
38 | };
39 | #>
40 | namespace SimdLinq;
41 |
42 | using System.Runtime.InteropServices;
43 |
44 | // SequenceEqual(this ReadOnlySpan span, ReadOnlySpan other) and if T is bitwise equatable, using SIMD.
45 |
46 | public static partial class SimdLinqExtensions
47 | {
48 | <# foreach(var type in types) { #>
49 | <#= summary(type, true, true) #>
50 | public static bool SequenceEqual(<#= type #>[] first, <#= type #>[] second)
51 | {
52 | ArgumentNullException.ThrowIfNull(first);
53 | ArgumentNullException.ThrowIfNull(second);
54 | return MemoryExtensions.SequenceEqual(first.AsSpan(), second.AsSpan());
55 | }
56 |
57 | <#= summary(type, true, false) #>
58 | public static bool SequenceEqual(<#= type #>[] first, ReadOnlySpan<<#= type #>> second)
59 | {
60 | ArgumentNullException.ThrowIfNull(first);
61 | return MemoryExtensions.SequenceEqual(first.AsSpan(), second);
62 | }
63 |
64 | <#= summary(type, true, true) #>
65 | public static bool SequenceEqual(List<<#= type #>> first, List<<#= type #>> second)
66 | {
67 | ArgumentNullException.ThrowIfNull(first);
68 | ArgumentNullException.ThrowIfNull(second);
69 | return MemoryExtensions.SequenceEqual((ReadOnlySpan<<#= type #>>)CollectionsMarshal.AsSpan(first), (ReadOnlySpan<<#= type #>>)CollectionsMarshal.AsSpan(second));
70 | }
71 |
72 | <#= summary(type, true, false) #>
73 | public static bool SequenceEqual(List<<#= type #>> first, ReadOnlySpan<<#= type #>> second)
74 | {
75 | ArgumentNullException.ThrowIfNull(first);
76 | return MemoryExtensions.SequenceEqual((ReadOnlySpan<<#= type #>>)CollectionsMarshal.AsSpan(first), second);
77 | }
78 |
79 | <#= summary(type, false, false) #>
80 | public static bool SequenceEqual(Memory<<#= type #>> first, Memory<<#= type #>> second)
81 | {
82 | return MemoryExtensions.SequenceEqual((ReadOnlySpan<<#= type #>>)first.Span, (ReadOnlySpan<<#= type #>>)second.Span);
83 | }
84 |
85 | <#= summary(type, false, false) #>
86 | public static bool SequenceEqual(Memory<<#= type #>> first, ReadOnlySpan<<#= type #>> second)
87 | {
88 | return MemoryExtensions.SequenceEqual((ReadOnlySpan<<#= type #>>)first.Span, second);
89 | }
90 |
91 | <#= summary(type, false, false) #>
92 | public static bool SequenceEqual(ReadOnlyMemory<<#= type #>> first, ReadOnlyMemory<<#= type #>> second)
93 | {
94 | return MemoryExtensions.SequenceEqual(first.Span, second.Span);
95 | }
96 |
97 | <#= summary(type, false, false) #>
98 | public static bool SequenceEqual(ReadOnlyMemory<<#= type #>> first, ReadOnlySpan<<#= type #>> second)
99 | {
100 | return MemoryExtensions.SequenceEqual(first.Span, second);
101 | }
102 |
103 | <#= summary(type, false, false) #>
104 | public static bool SequenceEqual(Span<<#= type #>> first, Span<<#= type #>> second)
105 | {
106 | return MemoryExtensions.SequenceEqual((ReadOnlySpan<<#= type #>>)first, (ReadOnlySpan<<#= type #>>)second);
107 | }
108 |
109 | <#= summary(type, false, false) #>
110 | public static bool SequenceEqual(Span<<#= type #>> first, ReadOnlySpan<<#= type #>> second)
111 | {
112 | return MemoryExtensions.SequenceEqual((ReadOnlySpan<<#= type #>>)first, second);
113 | }
114 |
115 | <# } #>
116 | }
117 |
--------------------------------------------------------------------------------
/src/SimdLinq/SimdLinqExtensions.Sum.cs:
--------------------------------------------------------------------------------
1 | namespace SimdLinq;
2 |
3 | using System.Runtime.InteropServices;
4 |
5 | public static partial class SimdLinqExtensions
6 | {
7 | ///
8 | /// Computes the sum of a sequence of values using SIMD acceleration.
9 | ///
10 | ///
11 | /// Note that unlike this method is unchecked and will not throw an .
12 | ///
13 | /// A sequence of values to calculate the sum of.
14 | /// The sum of the values in the sequence.
15 | /// is .
16 | public static int Sum(this int[] source)
17 | {
18 | ArgumentNullException.ThrowIfNull(source);
19 | return SumCore(new ReadOnlySpan(source));
20 | }
21 |
22 | ///
23 | /// Computes the sum of a sequence of values using SIMD acceleration.
24 | ///
25 | ///
26 | /// Note that unlike this method is unchecked and will not throw an .
27 | ///
28 | /// A sequence of values to calculate the sum of.
29 | /// The sum of the values in the sequence.
30 | /// is .
31 | public static int Sum(this List source)
32 | {
33 | ArgumentNullException.ThrowIfNull(source);
34 | return SumCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
35 | }
36 |
37 | ///
38 | /// Computes the sum of a sequence of values using SIMD acceleration.
39 | ///
40 | ///
41 | /// Note that unlike this method is unchecked and will not throw an .
42 | ///
43 | /// A sequence of values to calculate the sum of.
44 | /// The sum of the values in the sequence.
45 | public static int Sum(this Memory source)
46 | {
47 | return SumCore((ReadOnlySpan)source.Span);
48 | }
49 |
50 | ///
51 | /// Computes the sum of a sequence of values using SIMD acceleration.
52 | ///
53 | ///
54 | /// Note that unlike this method is unchecked and will not throw an .
55 | ///
56 | /// A sequence of values to calculate the sum of.
57 | /// The sum of the values in the sequence.
58 | public static int Sum(this ReadOnlyMemory source)
59 | {
60 | return SumCore(source.Span);
61 | }
62 |
63 | ///
64 | /// Computes the sum of a sequence of values using SIMD acceleration.
65 | ///
66 | ///
67 | /// Note that unlike this method is unchecked and will not throw an .
68 | ///
69 | /// A sequence of values to calculate the sum of.
70 | /// The sum of the values in the sequence.
71 | public static int Sum(this Span source)
72 | {
73 | return SumCore((ReadOnlySpan)source);
74 | }
75 |
76 | ///
77 | /// Computes the sum of a sequence of values using SIMD acceleration.
78 | ///
79 | ///
80 | /// Note that unlike this method is unchecked and will not throw an .
81 | ///
82 | /// A sequence of values to calculate the sum of.
83 | /// The sum of the values in the sequence.
84 | public static int Sum(this ReadOnlySpan source)
85 | {
86 | return SumCore(source);
87 | }
88 |
89 | ///
90 | /// Computes the sum of a sequence of values using SIMD acceleration.
91 | ///
92 | ///
93 | /// Note that unlike this method is unchecked and will not throw an .
94 | ///
95 | /// A sequence of values to calculate the sum of.
96 | /// The sum of the values in the sequence.
97 | /// is .
98 | public static uint Sum(this uint[] source)
99 | {
100 | ArgumentNullException.ThrowIfNull(source);
101 | return SumCore(new ReadOnlySpan(source));
102 | }
103 |
104 | ///
105 | /// Computes the sum of a sequence of values using SIMD acceleration.
106 | ///
107 | ///
108 | /// Note that unlike this method is unchecked and will not throw an .
109 | ///
110 | /// A sequence of values to calculate the sum of.
111 | /// The sum of the values in the sequence.
112 | /// is .
113 | public static uint Sum(this List source)
114 | {
115 | ArgumentNullException.ThrowIfNull(source);
116 | return SumCore((ReadOnlySpan)CollectionsMarshal.AsSpan(source));
117 | }
118 |
119 | ///
120 | /// Computes the sum of a sequence of values using SIMD acceleration.
121 | ///
122 | ///
123 | /// Note that unlike this method is unchecked and will not throw an .
124 | ///
125 | /// A sequence of values to calculate the sum of.
126 | /// The sum of the values in the sequence.
127 | public static uint Sum(this Memory source)
128 | {
129 | return SumCore((ReadOnlySpan)source.Span);
130 | }
131 |
132 | ///
133 | /// Computes the sum of a sequence of