├── .gitattributes
├── .gitignore
├── .nuget
├── NuGet.Config
└── NuGet.exe
├── .travis.yml
├── LICENSE.md
├── LinqOptimizer.sln
├── README.md
├── RELEASE_NOTES.md
├── appveyor.yml
├── benchmarks
├── LinqOptimizer.Benchmarks.CSharp
│ ├── LinqOptimizer.Benchmarks.CSharp.csproj
│ ├── Program.cs
│ └── results-csharp.txt
└── LinqOptimizer.Benchmarks.FSharp
│ ├── LinqOptimizer.Benchmarks.FSharp.fsproj
│ ├── Program.fs
│ └── results-fsharp.txt
├── build.cmd
├── build.fsx
├── build.sh
├── nuget
├── LinqOptimizer.CSharp.nuspec
└── LinqOptimizer.FSharp.nuspec
├── src
├── LinqOptimizer.Base
│ ├── LinqOptimizer.Base.csproj
│ ├── ParallelQueryExpr.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ └── QueryExpr.cs
├── LinqOptimizer.CSharp
│ ├── Extensions.cs
│ ├── LinqOptimizer.CSharp.csproj
│ ├── ParallelExtensions.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ └── QueryExpr.cs
├── LinqOptimizer.Core
│ ├── AccessChecker.fs
│ ├── AnonymousTypeEraser.fs
│ ├── AssemblyInfo.fs
│ ├── CSharpExpressionOptimizer.fs
│ ├── Collector.fs
│ ├── Compiler.fs
│ ├── ConstantLiftingTransformer.fs
│ ├── ExpressionHelpers.fs
│ ├── ExpressionTransformer.fs
│ ├── ExtensionMethods.fs
│ ├── FSharpExpressionOptimizer.fs
│ ├── FreeVariablesVisitor.fs
│ ├── GroupingHelpers.fs
│ ├── LinqOptimizer.Core.fsproj
│ ├── ParallelismHelpers.fs
│ ├── QueryExpr.fs
│ ├── ReflectionHelpers.fs
│ ├── Session.fs
│ ├── SortingHelpers.fs
│ ├── TupleElimination.fs
│ ├── TypeCollectorVisitor.fs
│ └── Utils.fs
└── LinqOptimizer.FSharp
│ ├── AssemblyInfo.fs
│ ├── FSharpParallelQuery.fs
│ ├── FSharpQuery.fs
│ ├── LinqOptimizer.FSharp.fsproj
│ └── QueryBuilder.fs
└── tests
├── LinqOptimizer.Tests.CSharp
├── LinqOptimizer.Tests.CSharp.csproj
├── ParallelQueryTests.cs
├── QueryTests.cs
└── packages.config
└── LinqOptimizer.Tests.FSharp
├── App.config
├── LinqOptimizer.Tests.FSharp.fsproj
├── PQuery.fs
├── PrecompileHelpers.fs
└── QueryExpr.fs
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # Unix style backup files
5 | *~
6 |
7 | # User-specific files
8 | *.suo
9 | *.user
10 | *.sln.docstates
11 | *.userprefs
12 |
13 | # Build results
14 |
15 | [Dd]ebug/
16 | [Rr]elease/
17 | x64/
18 | build/
19 | [Bb]in/
20 | [Oo]bj/
21 |
22 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
23 | !packages/*/build/
24 |
25 | # MSTest test Results
26 | [Tt]est[Rr]esult*/
27 | [Bb]uild[Ll]og.*
28 |
29 | *_i.c
30 | *_p.c
31 | *.ilk
32 | *.meta
33 | *.obj
34 | *.pch
35 | *.pdb
36 | *.pgc
37 | *.pgd
38 | *.rsp
39 | *.sbr
40 | *.tlb
41 | *.tli
42 | *.tlh
43 | *.tmp
44 | *.tmp_proj
45 | *.log
46 | *.vspscc
47 | *.vssscc
48 | .builds
49 | *.pidb
50 | *.log
51 | *.scc
52 |
53 | # Visual C++ cache files
54 | ipch/
55 | *.aps
56 | *.ncb
57 | *.opensdf
58 | *.sdf
59 | *.cachefile
60 |
61 | # Visual Studio profiler
62 | *.psess
63 | *.vsp
64 | *.vspx
65 |
66 | # Guidance Automation Toolkit
67 | *.gpState
68 |
69 | # ReSharper is a .NET coding add-in
70 | _ReSharper*/
71 | *.[Rr]e[Ss]harper
72 |
73 | # TeamCity is a build add-in
74 | _TeamCity*
75 |
76 | # DotCover is a Code Coverage Tool
77 | *.dotCover
78 |
79 | # NCrunch
80 | *.ncrunch*
81 | .*crunch*.local.xml
82 |
83 | # Installshield output folder
84 | [Ee]xpress/
85 |
86 | # DocProject is a documentation generator add-in
87 | DocProject/buildhelp/
88 | DocProject/Help/*.HxT
89 | DocProject/Help/*.HxC
90 | DocProject/Help/*.hhc
91 | DocProject/Help/*.hhk
92 | DocProject/Help/*.hhp
93 | DocProject/Help/Html2
94 | DocProject/Help/html
95 |
96 | # Click-Once directory
97 | publish/
98 |
99 | # Publish Web Output
100 | *.Publish.xml
101 |
102 | # Enable nuget.exe in the .nuget folder (though normally executables are not tracked)
103 | !.nuget/NuGet.exe
104 |
105 | # Windows Azure Build Output
106 | csx
107 | *.build.csdef
108 |
109 | # Windows Store app package directory
110 | AppPackages/
111 |
112 | # Others
113 | sql/
114 | *.Cache
115 | ClientBin/
116 | [Ss]tyle[Cc]op.*
117 | ~$*
118 | *~
119 | *.dbmdl
120 | *.[Pp]ublish.xml
121 | *.pfx
122 | *.publishsettings
123 |
124 | # RIA/Silverlight projects
125 | Generated_Code/
126 |
127 | # Backup & report files from converting an old project file to a newer
128 | # Visual Studio version. Backup files are not needed, because we have git ;-)
129 | _UpgradeReport_Files/
130 | Backup*/
131 | UpgradeLog*.XML
132 | UpgradeLog*.htm
133 |
134 | # SQL Server files
135 | App_Data/*.mdf
136 | App_Data/*.ldf
137 |
138 |
139 | #LightSwitch generated files
140 | GeneratedArtifacts/
141 | _Pvt_Extensions/
142 | ModelManifest.xml
143 |
144 | # =========================
145 | # Windows detritus
146 | # =========================
147 |
148 | # Windows image file caches
149 | Thumbs.db
150 | ehthumbs.db
151 |
152 | # Folder config file
153 | Desktop.ini
154 |
155 | # Recycle Bin used on file shares
156 | $RECYCLE.BIN/
157 |
158 | # Mac desktop service store files
159 | .DS_Store
160 |
161 | # ===================================================
162 | # Exclude F# project specific directories and files
163 | # ===================================================
164 |
165 | # NuGet Packages Directory
166 | packages/
167 |
168 | # Generated documentation folder
169 | docs/output/
170 |
171 | # Temp folder used for publishing docs
172 | temp/
173 |
174 | # Test results produced by build
175 | TestResults.xml
176 |
177 | # Nuget outputs
178 | nuget/*.nupkg
179 |
180 | paket-files/
181 | .fake/
182 | temp/
183 | .vs/
184 | artifacts/
--------------------------------------------------------------------------------
/.nuget/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.nuget/NuGet.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nessos/LinqOptimizer/2803f13258a27649bffb2d4691e8525909e28bed/.nuget/NuGet.exe
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: objective-c
2 |
3 | env:
4 | matrix:
5 | - MONO_VERSION="3.4.0"
6 |
7 | install:
8 | - wget "http://download.mono-project.com/archive/${MONO_VERSION}/macos-10-x86/MonoFramework-MDK-${MONO_VERSION}.macos10.xamarin.x86.pkg"
9 | - sudo installer -pkg "MonoFramework-MDK-${MONO_VERSION}.macos10.xamarin.x86.pkg" -target /
10 |
11 | script:
12 | - ./build.sh Release-Mono
13 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright 2013
2 |
3 | Nick Palladinos
4 | Nessos Information Technologies SA
5 |
6 | Licensed under the Apache License, Version 2.0 (the "License");
7 | you may not use this file except in compliance with the License.
8 | You may obtain a copy of the License at
9 |
10 | http://www.apache.org/licenses/LICENSE-2.0
11 |
12 | Unless required by applicable law or agreed to in writing, software
13 | distributed under the License is distributed on an "AS IS" BASIS,
14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | See the License for the specific language governing permissions and
16 | limitations under the License.
17 |
18 |
19 | Apache License, Version 2.0
20 | ===========================
21 |
22 | Apache License
23 | Version 2.0, January 2004
24 | http://www.apache.org/licenses/
25 |
26 | ------------------------------------------------------------
27 |
28 | ### TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
29 |
30 |
31 | **1. Definitions.**
32 |
33 | - "License" shall mean the terms and conditions for use, reproduction,
34 | and distribution as defined by Sections 1 through 9 of this document.
35 |
36 | - "Licensor" shall mean the copyright owner or entity authorized by
37 | the copyright owner that is granting the License.
38 |
39 | - "Legal Entity" shall mean the union of the acting entity and all
40 | other entities that control, are controlled by, or are under common
41 | control with that entity. For the purposes of this definition,
42 | "control" means (i) the power, direct or indirect, to cause the
43 | direction or management of such entity, whether by contract or
44 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
45 | outstanding shares, or (iii) beneficial ownership of such entity.
46 |
47 | - "You" (or "Your") shall mean an individual or Legal Entity
48 | exercising permissions granted by this License.
49 |
50 | - "Source" form shall mean the preferred form for making modifications,
51 | including but not limited to software source code, documentation
52 | source, and configuration files.
53 |
54 | - "Object" form shall mean any form resulting from mechanical
55 | transformation or translation of a Source form, including but
56 | not limited to compiled object code, generated documentation,
57 | and conversions to other media types.
58 |
59 | - "Work" shall mean the work of authorship, whether in Source or
60 | Object form, made available under the License, as indicated by a
61 | copyright notice that is included in or attached to the work
62 | (an example is provided in the Appendix below).
63 |
64 | - "Derivative Works" shall mean any work, whether in Source or Object
65 | form, that is based on (or derived from) the Work and for which the
66 | editorial revisions, annotations, elaborations, or other modifications
67 | represent, as a whole, an original work of authorship. For the purposes
68 | of this License, Derivative Works shall not include works that remain
69 | separable from, or merely link (or bind by name) to the interfaces of,
70 | the Work and Derivative Works thereof.
71 |
72 | - "Contribution" shall mean any work of authorship, including
73 | the original version of the Work and any modifications or additions
74 | to that Work or Derivative Works thereof, that is intentionally
75 | submitted to Licensor for inclusion in the Work by the copyright owner
76 | or by an individual or Legal Entity authorized to submit on behalf of
77 | the copyright owner. For the purposes of this definition, "submitted"
78 | means any form of electronic, verbal, or written communication sent
79 | to the Licensor or its representatives, including but not limited to
80 | communication on electronic mailing lists, source code control systems,
81 | and issue tracking systems that are managed by, or on behalf of, the
82 | Licensor for the purpose of discussing and improving the Work, but
83 | excluding communication that is conspicuously marked or otherwise
84 | designated in writing by the copyright owner as "Not a Contribution."
85 |
86 | - "Contributor" shall mean Licensor and any individual or Legal Entity
87 | on behalf of whom a Contribution has been received by Licensor and
88 | subsequently incorporated within the Work.
89 |
90 | **2. Grant of Copyright License.**
91 | Subject to the terms and conditions of
92 | this License, each Contributor hereby grants to You a perpetual,
93 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
94 | copyright license to reproduce, prepare Derivative Works of,
95 | publicly display, publicly perform, sublicense, and distribute the
96 | Work and such Derivative Works in Source or Object form.
97 |
98 | **3. Grant of Patent License.**
99 | Subject to the terms and conditions of
100 | this License, each Contributor hereby grants to You a perpetual,
101 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
102 | (except as stated in this section) patent license to make, have made,
103 | use, offer to sell, sell, import, and otherwise transfer the Work,
104 | where such license applies only to those patent claims licensable
105 | by such Contributor that are necessarily infringed by their
106 | Contribution(s) alone or by combination of their Contribution(s)
107 | with the Work to which such Contribution(s) was submitted. If You
108 | institute patent litigation against any entity (including a
109 | cross-claim or counterclaim in a lawsuit) alleging that the Work
110 | or a Contribution incorporated within the Work constitutes direct
111 | or contributory patent infringement, then any patent licenses
112 | granted to You under this License for that Work shall terminate
113 | as of the date such litigation is filed.
114 |
115 | **4. Redistribution.**
116 | You may reproduce and distribute copies of the
117 | Work or Derivative Works thereof in any medium, with or without
118 | modifications, and in Source or Object form, provided that You
119 | meet the following conditions:
120 |
121 | - You must give any other recipients of the Work or
122 | Derivative Works a copy of this License; and
123 |
124 | - You must cause any modified files to carry prominent notices
125 | stating that You changed the files; and
126 |
127 | - You must retain, in the Source form of any Derivative Works
128 | that You distribute, all copyright, patent, trademark, and
129 | attribution notices from the Source form of the Work,
130 | excluding those notices that do not pertain to any part of
131 | the Derivative Works; and
132 |
133 | - If the Work includes a "NOTICE" text file as part of its
134 | distribution, then any Derivative Works that You distribute must
135 | include a readable copy of the attribution notices contained
136 | within such NOTICE file, excluding those notices that do not
137 | pertain to any part of the Derivative Works, in at least one
138 | of the following places: within a NOTICE text file distributed
139 | as part of the Derivative Works; within the Source form or
140 | documentation, if provided along with the Derivative Works; or,
141 | within a display generated by the Derivative Works, if and
142 | wherever such third-party notices normally appear. The contents
143 | of the NOTICE file are for informational purposes only and
144 | do not modify the License. You may add Your own attribution
145 | notices within Derivative Works that You distribute, alongside
146 | or as an addendum to the NOTICE text from the Work, provided
147 | that such additional attribution notices cannot be construed
148 | as modifying the License.
149 |
150 | You may add Your own copyright statement to Your modifications and
151 | may provide additional or different license terms and conditions
152 | for use, reproduction, or distribution of Your modifications, or
153 | for any such Derivative Works as a whole, provided Your use,
154 | reproduction, and distribution of the Work otherwise complies with
155 | the conditions stated in this License.
156 |
157 | **5. Submission of Contributions.**
158 | Unless You explicitly state otherwise,
159 | any Contribution intentionally submitted for inclusion in the Work
160 | by You to the Licensor shall be under the terms and conditions of
161 | this License, without any additional terms or conditions.
162 | Notwithstanding the above, nothing herein shall supersede or modify
163 | the terms of any separate license agreement you may have executed
164 | with Licensor regarding such Contributions.
165 |
166 | **6. Trademarks.**
167 | This License does not grant permission to use the trade
168 | names, trademarks, service marks, or product names of the Licensor,
169 | except as required for reasonable and customary use in describing the
170 | origin of the Work and reproducing the content of the NOTICE file.
171 |
172 | **7. Disclaimer of Warranty.**
173 | Unless required by applicable law or
174 | agreed to in writing, Licensor provides the Work (and each
175 | Contributor provides its Contributions) on an "AS IS" BASIS,
176 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
177 | implied, including, without limitation, any warranties or conditions
178 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
179 | PARTICULAR PURPOSE. You are solely responsible for determining the
180 | appropriateness of using or redistributing the Work and assume any
181 | risks associated with Your exercise of permissions under this License.
182 |
183 | **8. Limitation of Liability.**
184 | In no event and under no legal theory,
185 | whether in tort (including negligence), contract, or otherwise,
186 | unless required by applicable law (such as deliberate and grossly
187 | negligent acts) or agreed to in writing, shall any Contributor be
188 | liable to You for damages, including any direct, indirect, special,
189 | incidental, or consequential damages of any character arising as a
190 | result of this License or out of the use or inability to use the
191 | Work (including but not limited to damages for loss of goodwill,
192 | work stoppage, computer failure or malfunction, or any and all
193 | other commercial damages or losses), even if such Contributor
194 | has been advised of the possibility of such damages.
195 |
196 | **9. Accepting Warranty or Additional Liability.**
197 | While redistributing
198 | the Work or Derivative Works thereof, You may choose to offer,
199 | and charge a fee for, acceptance of support, warranty, indemnity,
200 | or other liability obligations and/or rights consistent with this
201 | License. However, in accepting such obligations, You may act only
202 | on Your own behalf and on Your sole responsibility, not on behalf
203 | of any other Contributor, and only if You agree to indemnify,
204 | defend, and hold each Contributor harmless for any liability
205 | incurred by, or claims asserted against, such Contributor by reason
206 | of your accepting any such warranty or additional liability.
207 |
--------------------------------------------------------------------------------
/LinqOptimizer.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29009.5
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{9C33CACA-C699-4C93-BA82-9AE99C9A0D3C}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{8D1A3558-9D76-4744-BDFE-AD25F8A1EE30}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{EC71519F-9DEA-49C1-9A29-8D97093C6BBF}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LinqOptimizer.Tests.CSharp", "tests\LinqOptimizer.Tests.CSharp\LinqOptimizer.Tests.CSharp.csproj", "{1E812A89-C8F1-463E-8E1E-765389A0A4BC}"
13 | EndProject
14 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "LinqOptimizer.Tests.FSharp", "tests\LinqOptimizer.Tests.FSharp\LinqOptimizer.Tests.FSharp.fsproj", "{38A228D2-764C-4103-9AC2-58452630CBA2}"
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LinqOptimizer.Base", "src\LinqOptimizer.Base\LinqOptimizer.Base.csproj", "{F21F83A3-BDBD-4965-87D6-94E65A069A7B}"
17 | EndProject
18 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "LinqOptimizer.Core", "src\LinqOptimizer.Core\LinqOptimizer.Core.fsproj", "{6FEA9D47-1291-40CE-9716-619BCC2F485A}"
19 | EndProject
20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LinqOptimizer.CSharp", "src\LinqOptimizer.CSharp\LinqOptimizer.CSharp.csproj", "{E606C2C2-C219-4E51-B928-5B192E283A54}"
21 | EndProject
22 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "LinqOptimizer.FSharp", "src\LinqOptimizer.FSharp\LinqOptimizer.FSharp.fsproj", "{5C5FE9F9-91DF-44E2-A68D-B9A49828E0BB}"
23 | EndProject
24 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LinqOptimizer.Benchmarks.CSharp", "benchmarks\LinqOptimizer.Benchmarks.CSharp\LinqOptimizer.Benchmarks.CSharp.csproj", "{2BB3E227-CB90-4FFD-9763-4DCD00B61D79}"
25 | EndProject
26 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "LinqOptimizer.Benchmarks.FSharp", "benchmarks\LinqOptimizer.Benchmarks.FSharp\LinqOptimizer.Benchmarks.FSharp.fsproj", "{99EC4E0D-C8F6-40DC-97D1-1E30A261E67F}"
27 | EndProject
28 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "project", "project", "{3C6ADEC7-0540-4614-8454-3D8C7FD44C42}"
29 | ProjectSection(SolutionItems) = preProject
30 | .travis.yml = .travis.yml
31 | appveyor.yml = appveyor.yml
32 | build.cmd = build.cmd
33 | build.fsx = build.fsx
34 | build.sh = build.sh
35 | LICENSE.md = LICENSE.md
36 | nuget\LinqOptimizer.CSharp.nuspec = nuget\LinqOptimizer.CSharp.nuspec
37 | nuget\LinqOptimizer.FSharp.nuspec = nuget\LinqOptimizer.FSharp.nuspec
38 | README.md = README.md
39 | RELEASE_NOTES.md = RELEASE_NOTES.md
40 | EndProjectSection
41 | EndProject
42 | Global
43 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
44 | Debug|Any CPU = Debug|Any CPU
45 | Release|Any CPU = Release|Any CPU
46 | Release-Mono|Any CPU = Release-Mono|Any CPU
47 | EndGlobalSection
48 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
49 | {1E812A89-C8F1-463E-8E1E-765389A0A4BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50 | {1E812A89-C8F1-463E-8E1E-765389A0A4BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
51 | {1E812A89-C8F1-463E-8E1E-765389A0A4BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
52 | {1E812A89-C8F1-463E-8E1E-765389A0A4BC}.Release|Any CPU.Build.0 = Release|Any CPU
53 | {1E812A89-C8F1-463E-8E1E-765389A0A4BC}.Release-Mono|Any CPU.ActiveCfg = Release|Any CPU
54 | {1E812A89-C8F1-463E-8E1E-765389A0A4BC}.Release-Mono|Any CPU.Build.0 = Release|Any CPU
55 | {38A228D2-764C-4103-9AC2-58452630CBA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
56 | {38A228D2-764C-4103-9AC2-58452630CBA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
57 | {38A228D2-764C-4103-9AC2-58452630CBA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
58 | {38A228D2-764C-4103-9AC2-58452630CBA2}.Release|Any CPU.Build.0 = Release|Any CPU
59 | {38A228D2-764C-4103-9AC2-58452630CBA2}.Release-Mono|Any CPU.ActiveCfg = Release|Any CPU
60 | {38A228D2-764C-4103-9AC2-58452630CBA2}.Release-Mono|Any CPU.Build.0 = Release|Any CPU
61 | {F21F83A3-BDBD-4965-87D6-94E65A069A7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
62 | {F21F83A3-BDBD-4965-87D6-94E65A069A7B}.Debug|Any CPU.Build.0 = Debug|Any CPU
63 | {F21F83A3-BDBD-4965-87D6-94E65A069A7B}.Release|Any CPU.ActiveCfg = Release|Any CPU
64 | {F21F83A3-BDBD-4965-87D6-94E65A069A7B}.Release|Any CPU.Build.0 = Release|Any CPU
65 | {F21F83A3-BDBD-4965-87D6-94E65A069A7B}.Release-Mono|Any CPU.ActiveCfg = Release|Any CPU
66 | {F21F83A3-BDBD-4965-87D6-94E65A069A7B}.Release-Mono|Any CPU.Build.0 = Release|Any CPU
67 | {6FEA9D47-1291-40CE-9716-619BCC2F485A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
68 | {6FEA9D47-1291-40CE-9716-619BCC2F485A}.Debug|Any CPU.Build.0 = Debug|Any CPU
69 | {6FEA9D47-1291-40CE-9716-619BCC2F485A}.Release|Any CPU.ActiveCfg = Release|Any CPU
70 | {6FEA9D47-1291-40CE-9716-619BCC2F485A}.Release|Any CPU.Build.0 = Release|Any CPU
71 | {6FEA9D47-1291-40CE-9716-619BCC2F485A}.Release-Mono|Any CPU.ActiveCfg = Release|Any CPU
72 | {6FEA9D47-1291-40CE-9716-619BCC2F485A}.Release-Mono|Any CPU.Build.0 = Release|Any CPU
73 | {E606C2C2-C219-4E51-B928-5B192E283A54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
74 | {E606C2C2-C219-4E51-B928-5B192E283A54}.Debug|Any CPU.Build.0 = Debug|Any CPU
75 | {E606C2C2-C219-4E51-B928-5B192E283A54}.Release|Any CPU.ActiveCfg = Release|Any CPU
76 | {E606C2C2-C219-4E51-B928-5B192E283A54}.Release|Any CPU.Build.0 = Release|Any CPU
77 | {E606C2C2-C219-4E51-B928-5B192E283A54}.Release-Mono|Any CPU.ActiveCfg = Release|Any CPU
78 | {E606C2C2-C219-4E51-B928-5B192E283A54}.Release-Mono|Any CPU.Build.0 = Release|Any CPU
79 | {5C5FE9F9-91DF-44E2-A68D-B9A49828E0BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
80 | {5C5FE9F9-91DF-44E2-A68D-B9A49828E0BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
81 | {5C5FE9F9-91DF-44E2-A68D-B9A49828E0BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
82 | {5C5FE9F9-91DF-44E2-A68D-B9A49828E0BB}.Release|Any CPU.Build.0 = Release|Any CPU
83 | {5C5FE9F9-91DF-44E2-A68D-B9A49828E0BB}.Release-Mono|Any CPU.ActiveCfg = Release|Any CPU
84 | {5C5FE9F9-91DF-44E2-A68D-B9A49828E0BB}.Release-Mono|Any CPU.Build.0 = Release|Any CPU
85 | {2BB3E227-CB90-4FFD-9763-4DCD00B61D79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
86 | {2BB3E227-CB90-4FFD-9763-4DCD00B61D79}.Debug|Any CPU.Build.0 = Debug|Any CPU
87 | {2BB3E227-CB90-4FFD-9763-4DCD00B61D79}.Release|Any CPU.ActiveCfg = Release|Any CPU
88 | {2BB3E227-CB90-4FFD-9763-4DCD00B61D79}.Release|Any CPU.Build.0 = Release|Any CPU
89 | {2BB3E227-CB90-4FFD-9763-4DCD00B61D79}.Release-Mono|Any CPU.ActiveCfg = Release|Any CPU
90 | {2BB3E227-CB90-4FFD-9763-4DCD00B61D79}.Release-Mono|Any CPU.Build.0 = Release|Any CPU
91 | {99EC4E0D-C8F6-40DC-97D1-1E30A261E67F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
92 | {99EC4E0D-C8F6-40DC-97D1-1E30A261E67F}.Debug|Any CPU.Build.0 = Debug|Any CPU
93 | {99EC4E0D-C8F6-40DC-97D1-1E30A261E67F}.Release|Any CPU.ActiveCfg = Release|Any CPU
94 | {99EC4E0D-C8F6-40DC-97D1-1E30A261E67F}.Release|Any CPU.Build.0 = Release|Any CPU
95 | {99EC4E0D-C8F6-40DC-97D1-1E30A261E67F}.Release-Mono|Any CPU.ActiveCfg = Release|Any CPU
96 | {99EC4E0D-C8F6-40DC-97D1-1E30A261E67F}.Release-Mono|Any CPU.Build.0 = Release|Any CPU
97 | EndGlobalSection
98 | GlobalSection(SolutionProperties) = preSolution
99 | HideSolutionNode = FALSE
100 | EndGlobalSection
101 | GlobalSection(NestedProjects) = preSolution
102 | {1E812A89-C8F1-463E-8E1E-765389A0A4BC} = {8D1A3558-9D76-4744-BDFE-AD25F8A1EE30}
103 | {38A228D2-764C-4103-9AC2-58452630CBA2} = {8D1A3558-9D76-4744-BDFE-AD25F8A1EE30}
104 | {F21F83A3-BDBD-4965-87D6-94E65A069A7B} = {9C33CACA-C699-4C93-BA82-9AE99C9A0D3C}
105 | {6FEA9D47-1291-40CE-9716-619BCC2F485A} = {9C33CACA-C699-4C93-BA82-9AE99C9A0D3C}
106 | {E606C2C2-C219-4E51-B928-5B192E283A54} = {9C33CACA-C699-4C93-BA82-9AE99C9A0D3C}
107 | {5C5FE9F9-91DF-44E2-A68D-B9A49828E0BB} = {9C33CACA-C699-4C93-BA82-9AE99C9A0D3C}
108 | {2BB3E227-CB90-4FFD-9763-4DCD00B61D79} = {EC71519F-9DEA-49C1-9A29-8D97093C6BBF}
109 | {99EC4E0D-C8F6-40DC-97D1-1E30A261E67F} = {EC71519F-9DEA-49C1-9A29-8D97093C6BBF}
110 | EndGlobalSection
111 | GlobalSection(ExtensibilityGlobals) = postSolution
112 | SolutionGuid = {151A705D-0F2B-4C7B-B845-BBE63F2B48BC}
113 | EndGlobalSection
114 | EndGlobal
115 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # LinqOptimizer
2 |
3 | An automatic query optimizer-compiler for Sequential and Parallel LINQ.
4 |
5 | More information [here](http://nessos.github.io/LinqOptimizer/).
6 |
7 | ### Build Status
8 |
9 | Head (branch `master`), Build & Unit tests
10 |
11 | * Windows/.NET [](https://ci.appveyor.com/project/nessos/linqoptimizer)
12 | * Mac OS X/Mono 3.2.x [](https://travis-ci.org/nessos/LinqOptimizer/branches)
13 |
--------------------------------------------------------------------------------
/RELEASE_NOTES.md:
--------------------------------------------------------------------------------
1 | #### 0.7.0
2 | * Target netstandard2.0
3 |
4 | #### 0.6.3
5 | * Bug fixes.
6 | * Minor code updates.
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | configuration: Release
2 | test:
3 | assemblies:
4 | - LinqOptimizer.Tests.CSharp.exe
5 | - LinqOptimizer.Tests.FSharp.exe
6 |
--------------------------------------------------------------------------------
/benchmarks/LinqOptimizer.Benchmarks.CSharp/LinqOptimizer.Benchmarks.CSharp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.2;net461
5 | AnyCPU
6 | true
7 | true
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/benchmarks/LinqOptimizer.Benchmarks.CSharp/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using BenchmarkDotNet.Attributes;
4 | using BenchmarkDotNet.Columns;
5 | using BenchmarkDotNet.Configs;
6 | using BenchmarkDotNet.Exporters;
7 | using BenchmarkDotNet.Jobs;
8 | using BenchmarkDotNet.Loggers;
9 | using BenchmarkDotNet.Running;
10 | using Nessos.LinqOptimizer.CSharp;
11 |
12 | namespace LinqOptimizer.Benchmarks.CSharp
13 | {
14 | class Program
15 | {
16 | static void Main(string[] args)
17 | {
18 | typeof(SequentialBenchmarks)
19 | .GetNestedTypes()
20 | .Concat(typeof(ParallelBenchmarks).GetNestedTypes())
21 | .ToList()
22 | .ForEach(type => BenchmarkRunner.Run(type, new CustomConfig()));
23 | }
24 | }
25 |
26 | internal class CustomConfig : ManualConfig
27 | {
28 | public CustomConfig()
29 | {
30 | Add(Job.Default.WithLaunchCount(1));
31 | Add(StatisticColumn.Median, StatisticColumn.StdDev);
32 | Add(MarkdownExporter.GitHub);
33 | Add(new ConsoleLogger());
34 | }
35 | }
36 |
37 | public class BenchmarkBase
38 | {
39 | // the size used to be 200000000 but currently BenchmarkDotNet does not support gcAllowVeryLargeObjects (https://github.com/PerfDotNet/BenchmarkDotNet/issues/76)
40 | [Params(0, 10, 100, 10000, 1000000)]
41 | public int Count;
42 |
43 | const int useSameSeedEveryTimeToHaveSameData = 08041988;
44 | protected Random rnd = new Random(useSameSeedEveryTimeToHaveSameData);
45 |
46 | protected double[] values;
47 |
48 | [GlobalSetup]
49 | public virtual void SetUp()
50 | {
51 | values = Enumerable.Range(1, Count).Select(x => rnd.NextDouble()).ToArray();
52 | }
53 | }
54 |
55 | public class SequentialBenchmarks
56 | {
57 | public class SumBechmarks : BenchmarkBase
58 | {
59 | [Benchmark(Description = "Sum Linq", Baseline = true)]
60 | public double SumLinq()
61 | {
62 | return values.Sum();
63 | }
64 |
65 | [Benchmark(Description = "Sum Opt")]
66 | public double SumLinqOpt()
67 | {
68 | return values.AsQueryExpr().Sum().Run();
69 | }
70 | }
71 |
72 | public class SumOfSquaresBechmarks : BenchmarkBase
73 | {
74 | [Benchmark(Description = "Sum of Squares Linq", Baseline = true)]
75 | public double SumSqLinq()
76 | {
77 | return values.Select(x => x * x).Sum();
78 | }
79 |
80 | [Benchmark(Description = "Sum of Squares Opt")]
81 | public double SumSqLinqOpt()
82 | {
83 | return values.AsQueryExpr().Select(x => x * x).Sum().Run();
84 | }
85 | }
86 |
87 | public class CartesianBenchmarks : BenchmarkBase
88 | {
89 | private double[] dim1, dim2;
90 |
91 | [GlobalSetup]
92 | public override void SetUp()
93 | {
94 | base.SetUp();
95 |
96 | dim1 = values.Take(values.Length / 10).ToArray();
97 | dim2 = values.Take(20).ToArray();
98 | }
99 |
100 | [Benchmark(Description = "Cartesian Linq", Baseline = true)]
101 | public double CartLinq()
102 | {
103 | return (from x in dim1
104 | from y in dim2
105 | select x * y).Sum();
106 | }
107 |
108 | [Benchmark(Description = "Cartesian Opt")]
109 | public double CartLinqOpt()
110 | {
111 | return (from x in dim1.AsQueryExpr()
112 | from y in dim2
113 | select x * y).Sum().Run();
114 | }
115 | }
116 |
117 | public class GroupByBenchmarks : BenchmarkBase
118 | {
119 | [GlobalSetup]
120 | public override void SetUp()
121 | {
122 | base.SetUp();
123 |
124 | values =
125 | Enumerable.Range(1, Count).Select(x => 100000000 * rnd.NextDouble() - 50000000).ToArray();
126 | }
127 |
128 | [Benchmark(Description = "Group By Linq", Baseline = true)]
129 | public int[] GroupLinq()
130 | {
131 | return values
132 | .GroupBy(x => (int)x / 100)
133 | .OrderBy(x => x.Key)
134 | .Select(k => k.Count())
135 | .ToArray();
136 | }
137 |
138 | [Benchmark(Description = "Group By Opt")]
139 | public int[] GroupByOpt()
140 | {
141 | return values.AsQueryExpr()
142 | .GroupBy(x => (int)x / 100)
143 | .OrderBy(x => x.Key)
144 | .Select(k => k.Count())
145 | .ToArray()
146 | .Run();
147 | }
148 | }
149 |
150 | public class PythagoreanTriplesBenchmarks
151 | {
152 | [Params(0, 10, 100, 1000)]
153 | public int max = 1000;
154 |
155 | [Benchmark(Description = "Pythagorean Triples Linq", Baseline = true)]
156 | public int PythagoreanTriplesLinq()
157 | {
158 | return (from a in Enumerable.Range(1, max + 1)
159 | from b in Enumerable.Range(a, max + 1 - a)
160 | from c in Enumerable.Range(b, max + 1 - b)
161 | where a * a + b * b == c * c
162 | select true).Count();
163 | }
164 |
165 | [Benchmark(Description = "Pythagorean Triples Opt")]
166 | public int PythagoreanTriplesLinqOpt()
167 | {
168 | return (from a in QueryExpr.Range(1, max + 1)
169 | from b in Enumerable.Range(a, max + 1 - a)
170 | from c in Enumerable.Range(b, max + 1 - b)
171 | where a * a + b * b == c * c
172 | select true).Count().Run();
173 | }
174 | }
175 | }
176 |
177 | public class ParallelBenchmarks
178 | {
179 | public class ParallelSumBenchmarks : BenchmarkBase
180 | {
181 | [Benchmark(Description = "Parallel Sum Linq", Baseline = true)]
182 | public double ParallelSumLinq()
183 | {
184 | return values.AsParallel().Sum();
185 | }
186 |
187 | [Benchmark(Description = "Parallel Sum Opt")]
188 | public double ParallelSumOpt()
189 | {
190 | return values.AsParallelQueryExpr().Sum().Run();
191 | }
192 | }
193 |
194 | public class ParallelSumOfSquaresBenchmark : BenchmarkBase
195 | {
196 | [Benchmark(Description = "Parallel Sum of Squares Linq", Baseline = true)]
197 | public double ParallelSumSqLinq()
198 | {
199 | return values.AsParallel().Select(x => x * x).Sum();
200 | }
201 |
202 | [Benchmark(Description = "Parallel Sum of Squares Opt")]
203 | public double ParallelSumSqLinqOpt()
204 | {
205 | return values.AsParallelQueryExpr().Select(x => x * x).Sum().Run();
206 | }
207 | }
208 |
209 | public class ParallelCartesianBenchmarks : BenchmarkBase
210 | {
211 | private double[] dim1, dim2;
212 |
213 | [GlobalSetup]
214 | public override void SetUp()
215 | {
216 | base.SetUp();
217 |
218 | dim1 = values.Take(values.Length / 10).ToArray();
219 | dim2 = values.Take(20).ToArray();
220 | }
221 |
222 | [Benchmark(Description = "Parallel Cartesian Linq", Baseline = true)]
223 | public double ParallelCartLinq()
224 | {
225 | return (from x in dim1.AsParallel()
226 | from y in dim2
227 | select x * y).Sum();
228 | }
229 |
230 | [Benchmark(Description = "Parallel Cartesian Opt")]
231 | public double ParallelCartLinqOpt()
232 | {
233 | return (from x in dim1.AsParallelQueryExpr()
234 | from y in dim2
235 | select x * y).Sum().Run();
236 | }
237 | }
238 |
239 | public class ParallelGroupByBenchmarks : BenchmarkBase
240 | {
241 | [GlobalSetup]
242 | public override void SetUp()
243 | {
244 | values = Enumerable.Range(1, Count).Select(x => 100000000 * rnd.NextDouble() - 50000000).ToArray();
245 | }
246 |
247 | [Benchmark(Description = "Parallel Group By Linq", Baseline = true)]
248 | public int[] ParallelGroupLinq()
249 | {
250 | return values.AsParallel()
251 | .GroupBy(x => (int)x / 100)
252 | .OrderBy(x => x.Key)
253 | .Select(k => k.Count())
254 | .ToArray();
255 | }
256 |
257 | [Benchmark(Description = "Parallel Group By Opt")]
258 | public int[] ParallelGroupLinqOpt()
259 | {
260 | return values.AsParallelQueryExpr()
261 | .GroupBy(x => (int)x / 100)
262 | .OrderBy(x => x.Key)
263 | .Select(k => k.Count())
264 | .ToArray()
265 | .Run();
266 | }
267 | }
268 |
269 | public class ParallelPythagoreanTriplesBenchmarks
270 | {
271 | [Params(0, 10, 100, 1000)]
272 | public int max = 1000;
273 |
274 | [Benchmark(Description = "Parallel Pythagorean Triples Linq", Baseline = true)]
275 | public int ParallelPythagoreanTriplesLinq()
276 | {
277 | return (from a in Enumerable.Range(1, max + 1).AsParallel()
278 | from b in Enumerable.Range(a, max + 1 - a)
279 | from c in Enumerable.Range(b, max + 1 - b)
280 | where a * a + b * b == c * c
281 | select true).Count();
282 | }
283 |
284 | [Benchmark(Description = "Parallel Pythagorean Triples Opt")]
285 | public int ParallelPythagoreanTriplesLinqOpt()
286 | {
287 | return (from a in Enumerable.Range(1, max + 1).AsParallelQueryExpr()
288 | from b in Enumerable.Range(a, max + 1 - a)
289 | from c in Enumerable.Range(b, max + 1 - b)
290 | where a * a + b * b == c * c
291 | select true).Count().Run();
292 | }
293 | }
294 | }
295 | }
296 |
--------------------------------------------------------------------------------
/benchmarks/LinqOptimizer.Benchmarks.CSharp/results-csharp.txt:
--------------------------------------------------------------------------------
1 | // CLR - .NET 4.5.1
2 |
3 | PS C:\Users\krontogiannis\Desktop\Release> .\LinqOptimizer.Benchmarks.CSharp.exe
4 | "Sum Linq": 00:00:02.4882719
5 | "Sum Opt": 00:00:01.3065410
6 | Validate : True
7 |
8 | "Sum of Squares Linq": 00:00:05.2756961
9 | "Sum of Squares Opt": 00:00:00.9198907
10 | Validate : True
11 |
12 | "Cartesian Linq": 00:00:22.9570171
13 | "Cartesian Opt": 00:00:01.7985289
14 | Validate : True
15 |
16 | "GroupBy Linq": 00:00:30.8481043
17 | "GroupBy Opt": 00:00:24.1036471
18 | Validate : True
19 |
20 | "Pythagorean Triples Linq": 00:00:11.3666039
21 | "Pythagorean Triples Opt": 00:00:00.5531833
22 | Validate : True
23 |
24 | "Parallel Sum Linq": 00:00:00.5915235
25 | "Parallel Sum Opt": 00:00:00.6187390
26 | Validate : True
27 |
28 | "Parallel Sum of Squares Linq": 00:00:00.7773579
29 | "Parallel Sum of Squares Opt": 00:00:00.3443493
30 | Validate : True
31 |
32 | "Parallel Cartesian Linq": 00:00:04.1006209
33 | "Parallel Cartesian Opt": 00:00:00.2822599
34 | Validate : True
35 |
36 | "Parallel GroupBy Linq": 00:00:19.1792742
37 | "Parallel GroupBy Opt": 00:00:10.9898634
38 | Validate : True
39 |
40 | "Parallel Pythagorean Triples Linq": 00:00:06.1770607
41 | "Parallel Pythagorean Triples Opt": 00:00:00.1449303
42 | Validate : True
43 |
44 |
45 |
46 | krontogiannis@benchlinux:~> mono -V
47 | Mono JIT compiler version 3.2.3 (tarball Sat Sep 14 19:35:34 EDT 2013)
48 | Copyright (C) 2002-2012 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com
49 | TLS: __thread
50 | SIGSEGV: altstack
51 | Notifications: epoll
52 | Architecture: amd64
53 | Disabled: none
54 | Misc: softdebug
55 | LLVM: supported, not enabled.
56 | GC: sgen
57 |
58 | ## krontogiannis@benchmarks-suse:~> mono LinqOptimizer.Benchmarks.CSharp.exe --gc=boehm
59 | ## "Sum Linq": 00:00:05.6977650
60 | ## "Sum Opt": 00:00:01.5374375
61 | ## Validate : True
62 | ##
63 | ## "Sum of Squares Linq": 00:00:15.5541196
64 | ## "Sum of Squares Opt": 00:00:01.4809488
65 | ## Validate : True
66 | ##
67 | ## "Cartesian Linq": 00:00:33.3545918
68 | ## "Cartesian Opt": 00:00:03.2554914
69 | ## Validate : True
70 | ##
71 | ## "GroupBy Linq": INF
72 | ## "GroupBy Opt": 00:00:14.4638251
73 | ## Validate : True
74 | ##
75 | ## "Pythagorean Triples Linq": 00:00:41.2675174
76 | ## "Pythagorean Triples Opt": 00:00:00.9898223
77 | ## Validate : True
78 | ##
79 | ## "Parallel Sum Linq": 00:00:38.6001130
80 | ## "Parallel Sum Opt": 00:00:04.6659863
81 | ## Validate : False
82 | ## Values 100002834.384839, 100002834.384842
83 | ##
84 | ## "Parallel Sum of Squares Linq": 00:00:39.3777961
85 | ## "Parallel Sum of Squares Opt": 00:00:01.9471529
86 | ## Validate : False
87 | ## Values 66669094.7481553, 66669094.7481595
88 | ##
89 | ## "Parallel Cartesian Linq": 00:01:19.0215583
90 | ## "Parallel Cartesian Opt": 00:00:00.5005809
91 | ## Validate : False
92 | ## Values 119693758.794496, 119693758.794503
93 | ##
94 | ## "Parallel GroupBy Linq": 00:01:03.8937090
95 | ## "Parallel GroupBy Opt": 00:00:20.0196676
96 | ## Validate : True
97 | ##
98 | ## "Parallel Pythagorean Triples Linq": 00:00:49.3715419
99 | ## "Parallel Pythagorean Triples Opt": 00:00:00.1776788
100 | ## Validate : True
101 |
102 |
103 |
104 |
105 | ## krontogiannis@benchmarks-suse:~> mono LinqOptimizer.Benchmarks.CSharp.exe --gc=sgen
106 | ## "Sum Linq": 00:00:05.7998593
107 | ## "Sum Opt": 00:00:01.5160914
108 | ## Validate : True
109 | ##
110 | ## "Sum of Squares Linq": 00:00:17.7935761
111 | ## "Sum of Squares Opt": 00:00:01.4834264
112 | ## Validate : True
113 | ##
114 | ## "Cartesian Linq": 00:00:34.3443617
115 | ## "Cartesian Opt": 00:00:03.2562661
116 | ## Validate : True
117 | ##
118 | ## "GroupBy Linq": 00:00:00.0000408
119 | ## "GroupBy Opt": 00:00:14.5225747
120 | ## Validate : True
121 | ##
122 | ## "Pythagorean Triples Linq": 00:00:41.3427813
123 | ## "Pythagorean Triples Opt": 00:00:00.9896847
124 | ## Validate : True
125 | ##
126 | ## "Parallel Sum Linq": 00:00:28.6741257
127 | ## "Parallel Sum Opt": 00:00:01.8270092
128 | ## Validate : False
129 | ## Values 99998558.1943546, 99998558.194349
130 | ##
131 | ## "Parallel Sum of Squares Linq": 00:00:32.8208704
132 | ## "Parallel Sum of Squares Opt": 00:00:02.6051483
133 | ## Validate : False
134 | ## Values 66665945.3143208, 66665945.3143219
135 | ##
136 | ## "Parallel Cartesian Linq": 00:00:55.5233462
137 | ## "Parallel Cartesian Opt": 00:00:00.4901280
138 | ## Validate : False
139 | ## Values 80982627.9073865, 80982627.9073846
140 | ##
141 | ## "Parallel GroupBy Linq": 00:00:56.3008424
142 | ## "Parallel GroupBy Opt": 00:00:32.1725844
143 | ## Validate : True
144 | ##
145 | ## "Parallel Pythagorean Triples Linq": 00:00:27.1318891
146 | ## "Parallel Pythagorean Triples Opt": 00:00:00.1645247
147 | ## Validate : True
148 |
149 |
150 | krontogiannis@benchlinux:~/cs> mono LinqOptimizer.Benchmarks.CSharp.exe --gc=sgen
151 | "Sum Linq": 00:00:06.2144176
152 | "Sum Opt": 00:00:01.6351289
153 | Validate : True
154 |
155 | "Sum of Squares Linq": 00:00:20.2180162
156 | "Sum of Squares Opt": 00:00:01.4805840
157 | Validate : True
158 |
159 | "Cartesian Linq": 00:01:31.1709912
160 | "Cartesian Opt": 00:00:03.2036639
161 | Validate : True
162 |
163 | "GroupBy Linq": 00:00:00.0000448
164 | "GroupBy Opt": 00:00:18.3385379
165 | Validate : True
166 |
167 | "Pythagorean Triples Linq": 00:00:21.6280102
168 | "Pythagorean Triples Opt": 00:00:01.0116490
169 | Validate : True
170 |
171 | "Parallel Sum Linq": 00:00:35.3149295
172 | "Parallel Sum Opt": 00:00:01.0298428
173 | Validate : False
174 | Values 99995226.0075829, 99995226.0075779
175 |
176 | "Parallel Sum of Squares Linq": 00:00:33.5472698
177 | "Parallel Sum of Squares Opt": 00:00:02.1278030
178 | Validate : False
179 | Values 66661804.8318145, 66661804.8318127
180 |
181 | "Parallel Cartesian Linq": 00:00:49.4028521
182 | "Parallel Cartesian Opt": 00:00:00.5670794
183 | Validate : False
184 | Values 112652980.318857, 112652980.318853
185 |
186 | "Parallel GroupBy Linq": 00:01:26.9799121
187 | "Parallel GroupBy Opt": 00:00:12.4332692
188 | Validate : True
189 |
190 | "Parallel Pythagorean Triples Linq": 00:00:46.2175854
191 | "Parallel Pythagorean Triples Opt": 00:00:00.1705963
192 | Validate : True
193 |
194 |
195 |
--------------------------------------------------------------------------------
/benchmarks/LinqOptimizer.Benchmarks.FSharp/LinqOptimizer.Benchmarks.FSharp.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.2;net461
5 | AnyCPU
6 | true
7 | true
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/benchmarks/LinqOptimizer.Benchmarks.FSharp/Program.fs:
--------------------------------------------------------------------------------
1 | module Program
2 |
3 | open System
4 | open System.Linq
5 | open System.Collections
6 | open System.Collections.Generic
7 | open System.Diagnostics
8 | open Nessos.LinqOptimizer.FSharp
9 | open Nessos.LinqOptimizer.Base
10 |
11 | let measuref<'T>(title1, action1 : unit -> 'T, title2, action2 : unit -> 'T, validate : 'T * 'T -> bool) =
12 | let sw = new Stopwatch();
13 | sw.Start();
14 | let t1 = action1();
15 | sw.Stop();
16 | Console.WriteLine("\"{0}\":\t{1}",title1, sw.Elapsed);
17 | sw.Restart();
18 | let t2 = action2();
19 | sw.Stop();
20 | Console.WriteLine("\"{0}\":\t{1}", title2, sw.Elapsed);
21 | let v = validate(t1, t2)
22 | if not v then
23 | Console.WriteLine("Values {0}, {1}", t1, t2)
24 | Console.WriteLine("Validate : {0}", v);
25 | Console.WriteLine();
26 |
27 | let SumLinq(values : double []) =
28 | Seq.sum values
29 |
30 | let SumLinqOpt(values : double[]) =
31 | values |> Query.ofSeq |> Query.sum |> Query.run
32 |
33 | let SumSqLinq(values : double[]) =
34 | values |> Seq.map (fun x -> x * x) |> Seq.sum
35 |
36 | let SumSqLinqOpt(values : double[]) =
37 | values |> Query.ofSeq |> Query.map(fun x -> x * x) |> Query.sum |> Query.run
38 |
39 | let CartLinq (dim1 : double[], dim2 : double[]) =
40 | dim1 |> Seq.collect (fun x -> Seq.map (fun y -> x * y) dim2) |> Seq.sum
41 |
42 | let CartLinqOpt(dim1 : double[], dim2 : double[]) =
43 | dim1 |> Query.ofSeq |> Query.collect (fun x -> Seq.map (fun y -> x * y) dim2) |> Query.sum |> Query.run
44 |
45 |
46 | let GroupLinq(values : double[]) =
47 | values
48 | |> Seq.groupBy(fun x -> int x / 100)
49 | |> Seq.sortBy (fun (key, vs) -> key)
50 | |> Seq.map(fun (key, vs) -> Seq.length vs)
51 | |> Seq.toArray
52 |
53 | let GroupLinqOpt(values : double[]) =
54 | values
55 | |> Query.ofSeq
56 | |> Query.groupBy(fun x -> int x / 100)
57 | |> Query.sortBy (fun (key, vs) -> key)
58 | |> Query.map(fun (key, vs) -> Seq.length vs)
59 | |> Query.toArray
60 | |> Query.run
61 |
62 |
63 | let PythagoreanTriplesLinq(max) =
64 | Enumerable.Range(1, max + 1)
65 | |> Seq.collect(fun a ->
66 | Enumerable.Range(a, max + 1 - a)
67 | |> Seq.collect(fun b ->
68 | Enumerable.Range(b, max + 1 - b)
69 | |> Seq.map (fun c -> a, b, c)))
70 | |> Seq.filter (fun (a,b,c) -> a * a + b * b = c * c)
71 | |> Seq.length
72 |
73 |
74 | let PythagoreanTriplesLinqOpt(max) =
75 | Query.range(1, max + 1)
76 | |> Query.collect(fun a ->
77 | Enumerable.Range(a, max + 1 - a)
78 | |> Seq.collect(fun b ->
79 | Enumerable.Range(b, max + 1 - b)
80 | |> Seq.map (fun c -> a, b, c)))
81 | |> Query.filter (fun (a,b,c) -> a * a + b * b = c * c)
82 | |> Query.length
83 | |> Query.run
84 |
85 |
86 | //////////////////////////////////////////////////////////////////
87 |
88 | let ParallelSumLinq(values : double []) =
89 | values.AsParallel().Sum()
90 |
91 | let ParallelSumLinqOpt(values : double[]) =
92 | values |> PQuery.ofSeq |> PQuery.sum |> PQuery.run
93 |
94 | let ParallelSumSqLinq(values : double[]) =
95 | values.AsParallel().Select(fun x -> x * x).Sum()
96 |
97 | let ParallelSumSqLinqOpt(values : double[]) =
98 | values |> PQuery.ofSeq |> PQuery.map(fun x -> x * x) |> PQuery.sum |> PQuery.run
99 |
100 | let ParallelCartLinq (dim1 : double[], dim2 : double[]) =
101 | dim1.AsParallel().SelectMany(fun x -> dim2.Select(fun y -> x * y)).Sum()
102 |
103 | let ParallelCartLinqOpt(dim1 : double[], dim2 : double[]) =
104 | dim1 |> PQuery.ofSeq |> PQuery.collect (fun x -> Seq.map (fun y -> x * y) dim2) |> PQuery.sum |> PQuery.run
105 |
106 | let ParallelGroupLinq(values : double[]) =
107 | values.AsParallel()
108 | .GroupBy(fun x -> (int)x / 100)
109 | .OrderBy(fun (x : IGrouping<_,_>) -> x.Key)
110 | .Select(fun (k : IGrouping<_,_>) -> k.Count())
111 | .ToArray()
112 |
113 | let ParallelGroupLinqOpt(values : double[]) =
114 | values
115 | |> PQuery.ofSeq
116 | |> PQuery.groupBy(fun x -> int x / 100)
117 | |> PQuery.sortBy (fun (key, vs) -> key)
118 | |> PQuery.map(fun (key, vs) -> Seq.length vs)
119 | |> PQuery.toArray
120 | |> PQuery.run
121 |
122 |
123 | let ParallelPythagoreanTriplesLinq(max) =
124 | Enumerable.Range(1, max + 1).AsParallel()
125 | .SelectMany(fun a ->
126 | Enumerable.Range(a, max + 1 - a)
127 | .SelectMany(fun b ->
128 | Enumerable.Range(b, max + 1 - b)
129 | .Select(fun c -> (a,b,c))))
130 | .Where(fun (a,b,c) -> a * a + b * b = c * c)
131 | .Count()
132 |
133 | let ParallelPythagoreanTriplesLinqOpt(max) =
134 | Enumerable.Range(1, max + 1)
135 | |> PQuery.ofSeq
136 | |> PQuery.collect(fun a ->
137 | Enumerable.Range(a, max + 1 - a)
138 | |> Seq.collect(fun b ->
139 | Enumerable.Range(b, max + 1 - b)
140 | |> Seq.map (fun c -> a, b, c)))
141 | |> PQuery.filter (fun (a,b,c) -> a * a + b * b = c * c)
142 | |> PQuery.length
143 | |> PQuery.run
144 |
145 | //////////////////////////////////////////////////////////////////
146 |
147 | []
148 | let main argv =
149 | let rnd = new Random()
150 | let v = Enumerable.Range(1, 200000000).Select(fun x -> rnd.NextDouble()).ToArray()
151 | let cmp (x1, x2 : double) = Math.Abs(x1 - x2) < 1E-07
152 |
153 | measuref("Sum Seq", (fun () -> SumLinq v),
154 | "Sum Opt", (fun () -> SumLinqOpt v),
155 | cmp)
156 |
157 | measuref("Sum Squares Seq", (fun () -> SumSqLinq v),
158 | "Sum Squares Opt", (fun () -> SumSqLinqOpt v),
159 | cmp)
160 |
161 | let v1 = v.Take(v.Length / 10).ToArray()
162 | let v2 = v.Take(20).ToArray()
163 | measuref("Cartesian Seq", (fun () -> CartLinq(v1, v2)),
164 | "Cartesian Linq Opt", (fun () -> CartLinqOpt(v1, v2)),
165 | cmp)
166 |
167 | let g = Enumerable.Range(1, 20000000).Select(fun x -> 100000000. * rnd.NextDouble() - 50000000.).ToArray()
168 | measuref("Group Seq", (fun () -> GroupLinq g),
169 | "Group Opt", (fun () -> GroupLinqOpt g),
170 | fun (x, y) -> Enumerable.SequenceEqual(x, y))
171 |
172 | let n = 1000
173 | measuref("Pythagorean Seq", (fun () -> PythagoreanTriplesLinq n),
174 | "Pythagorean Opt", (fun () -> PythagoreanTriplesLinqOpt n ),
175 | fun (x, y) -> x = y)
176 |
177 | ////////////////////////////////////////////////////////////////////////////
178 |
179 | measuref("Parallel Sum Seq", (fun () -> ParallelSumLinq v),
180 | "Parallel Sum Opt", (fun () -> ParallelSumLinqOpt v),
181 | cmp)
182 |
183 | measuref("Parallel Sum Squares Seq", (fun () -> ParallelSumSqLinq v),
184 | "Parallel Sum Squares Opt", (fun () -> ParallelSumSqLinqOpt v),
185 | cmp)
186 |
187 | measuref("Parallel Cartesian Linq", (fun () -> ParallelCartLinq(v1, v2)),
188 | "Parallel Cartesian Opt", (fun () -> ParallelCartLinqOpt(v1, v2)),
189 | cmp)
190 |
191 | measuref("Parallel Group Linq", (fun () -> ParallelGroupLinq g),
192 | "Parallel Group Opt", (fun () -> ParallelGroupLinqOpt g),
193 | fun (x, y) -> Enumerable.SequenceEqual(x, y))
194 |
195 | measuref("Parallel Pythagorean Linq", (fun () -> ParallelPythagoreanTriplesLinq n),
196 | "Parallel Pythagorean Opt", (fun () -> ParallelPythagoreanTriplesLinqOpt n ),
197 | fun (x, y) -> x = y)
198 |
199 | 0
200 |
--------------------------------------------------------------------------------
/benchmarks/LinqOptimizer.Benchmarks.FSharp/results-fsharp.txt:
--------------------------------------------------------------------------------
1 | PS C:\Users\krontogiannis\Desktop\fsharp> .\LinqOptimizer.Benchmarks.FSharp.exe
2 | "Sum Seq": 00:00:02.8199866
3 | "Sum Opt": 00:00:01.1505856
4 | Validate : True
5 |
6 | "Sum Squares Seq": 00:00:07.3947223
7 | "Sum Squares Opt": 00:00:02.4090330
8 | Validate : True
9 |
10 | "Cartesian Seq": 00:00:33.3088133
11 | "Cartesian Linq Opt": 00:00:01.9047700
12 | Validate : True
13 |
14 | "Group Seq": 00:01:12.7457616
15 | "Group Opt": 00:00:24.6694342
16 | Validate : True
17 |
18 | ## "Pythagorean Seq": 00:00:26.0756178
19 | ## "Pythagorean Opt": 00:00:59.7736766
20 | ## Validate : True
21 | "Pythagorean Seq": 00:00:24.1093710
22 | "Pythagorean Opt": 00:00:00.5481868
23 | Validate : True
24 |
25 | "Parallel Sum Seq": 00:00:00.5089718
26 | "Parallel Sum Opt": 00:00:00.6138561
27 | Validate : True
28 |
29 | "Parallel Sum Squares Seq": 00:00:01.0234322
30 | "Parallel Sum Squares Opt": 00:00:00.5227186
31 | Validate : True
32 |
33 | "Parallel Cartesian Linq": 00:00:07.3078305
34 | "Parallel Cartesian Opt": 00:00:00.4272950
35 | Validate : True
36 |
37 | "Parallel Group Linq": 00:00:18.6451904
38 | "Parallel Group Opt": 00:00:09.1253919
39 | Validate : True
40 |
41 | ## "Parallel Pythagorean Linq": 00:00:09.0055781
42 | ## "Parallel Pythagorean Opt": 00:01:02.9909313
43 | ## Validate : True
44 | "Parallel Pythagorean Linq": 00:00:08.6221434
45 | "Parallel Pythagorean Opt": 00:00:00.1720263
46 | Validate : True
47 |
48 |
49 |
50 |
51 |
52 |
53 | ## krontogiannis@benchmarks-suse:~/fsharptest> mono LinqOptimizer.Benchmarks.FSharp.exe --gc=boehm
54 | ## "Sum Seq": 00:00:05.7900152
55 | ## "Sum Opt": 00:00:01.9753200
56 | ## Validate : True
57 | ##
58 | ## "Sum Squares Seq": 00:00:12.5300960
59 | ## "Sum Squares Opt": 00:00:01.7239223
60 | ## Validate : True
61 | ##
62 | ## "Cartesian Seq": 00:00:43.5201732
63 | ## "Cartesian Linq Opt": 00:00:03.1048342
64 | ## Validate : True
65 | ##
66 | ## "Group Seq": 00:00:21.0231150
67 | ## "Group Opt": 00:00:17.5624660
68 | ## Validate : True
69 | ##
70 | ## "Pythagorean Seq": 00:00:30.3968180
71 | ## "Pythagorean Opt": 00:03:29.2544626
72 | ## Validate : True
73 | ##
74 | ## "Parallel Sum Seq": 00:00:29.2880201
75 | ## "Parallel Sum Opt": 00:00:00.9779574
76 | ## Values 100001344.894611, 100001344.894609
77 | ## Validate : False
78 | ##
79 | ## "Parallel Sum Squares Seq": 00:00:42.6457523
80 | ## "Parallel Sum Squares Opt": 00:00:00.9633095
81 | ## Values 66667650.1864986, 66667650.1865005
82 | ## Validate : False
83 | ##
84 | ## "Parallel Cartesian Linq": 00:01:18.9780920
85 | ## "Parallel Cartesian Opt": 00:00:00.5017145
86 | ## Values 103369593.171146, 103369593.171139
87 | ## Validate : False
88 | ##
89 | ## "Parallel Group Linq": 00:00:54.8168058
90 | ## "Parallel Group Opt": 00:00:33.7890481
91 | ## Validate : True
92 | ##
93 | ## "Parallel Pythagorean Linq": 00:00:44.9215935
94 | ## "Parallel Pythagorean Opt": 00:07:41.5885317
95 | ## Validate : True
96 | ##
97 | ##
98 | ## krontogiannis@benchmarks-suse:~/fsharptest> mono LinqOptimizer.Benchmarks.FSharp --gc=sgen
99 | ## "Sum Seq": 00:00:05.6858785
100 | ## "Sum Opt": 00:00:01.7235633
101 | ## Validate : True
102 | ##
103 | ## "Sum Squares Seq": 00:00:12.0939924
104 | ## "Sum Squares Opt": 00:00:01.7389354
105 | ## Validate : True
106 | ##
107 | ## "Cartesian Seq": 00:00:42.4520462
108 | ## "Cartesian Linq Opt": 00:00:03.1192097
109 | ## Validate : True
110 | ##
111 | ## "Group Seq": 00:00:21.2268391
112 | ## "Group Opt": 00:00:16.8401549
113 | ## Validate : True
114 | ##
115 | ## "Pythagorean Seq": 00:00:28.5491907
116 | ## "Pythagorean Opt": 00:03:21.7066634
117 | ## Validate : True
118 | ##
119 | ## "Parallel Sum Seq": 00:00:27.7542858
120 | ## "Parallel Sum Opt": 00:00:02.4945969
121 | ## Values 100001454.444871, 100001454.444874
122 | ## Validate : False
123 | ##
124 | ## "Parallel Sum Squares Seq": 00:00:30.1456743
125 | ## "Parallel Sum Squares Opt": 00:00:03.6991790
126 | ## Values 66667389.8417738, 66667389.8417703
127 | ## Validate : False
128 | ##
129 | ## "Parallel Cartesian Linq": 00:01:00.1074199
130 | ## "Parallel Cartesian Opt": 00:00:00.4679154
131 | ## Values 92563924.8012052, 92563924.8012029
132 | ## Validate : False
133 | ##
134 | ## "Parallel Group Linq": 00:00:49.7698066
135 | ## "Parallel Group Opt": 00:00:32.6395047
136 | ## Validate : True
137 | ##
138 | ## "Parallel Pythagorean Linq": 00:00:36.6311734
139 | ## "Parallel Pythagorean Opt": 00:07:58.5362407
140 | ## Validate : True
141 |
142 |
143 | krontogiannis@benchlinux:~/fs> mono LinqOptimizer.Benchmarks.FSharp.exe --gc=sgen
144 | "Sum Seq": 00:00:05.4402164
145 | "Sum Opt": 00:00:01.6571258
146 | Validate : True
147 |
148 | "Sum Squares Seq": 00:00:20.1435323
149 | "Sum Squares Opt": 00:00:01.7821322
150 | Validate : True
151 |
152 | "Cartesian Seq": 00:01:48.8153359
153 | "Cartesian Linq Opt": 00:00:03.2311105
154 | Validate : True
155 |
156 | "Group Seq": 00:00:29.2657332
157 | "Group Opt": 00:00:18.1786424
158 | Validate : True
159 |
160 | "Pythagorean Seq": 00:00:28.6682705
161 | "Pythagorean Opt": 00:00:00.9956975
162 | Validate : True
163 |
164 | "Parallel Sum Seq": 00:00:33.6194553
165 | "Parallel Sum Opt": 00:00:01.0214878
166 | Values 100004014.538589, 100004014.53859
167 | Validate : False
168 |
169 | "Parallel Sum Squares Seq": 00:00:32.4996878
170 | "Parallel Sum Squares Opt": 00:00:01.0878441
171 | Values 66669777.2572909, 66669777.257289
172 | Validate : False
173 |
174 | "Parallel Cartesian Linq": 00:01:03.8188772
175 | "Parallel Cartesian Opt": 00:00:00.5069616
176 | Values 82871437.6990987, 82871437.699096
177 | Validate : False
178 |
179 | "Parallel Group Linq": 00:01:20.9994334
180 | "Parallel Group Opt": 00:00:30.8935315
181 | Validate : True
182 |
183 | "Parallel Pythagorean Linq": 00:00:57.4454289
184 | "Parallel Pythagorean Opt": 00:00:00.1678209
185 | Validate : True
186 |
187 |
--------------------------------------------------------------------------------
/build.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | if not exist packages\FAKE\tools\Fake.exe (
4 | .nuget\NuGet.exe install FAKE -OutputDirectory packages -ExcludeVersion -Version 4.64.17
5 | )
6 |
7 | packages\FAKE\tools\FAKE.exe build.fsx %*
--------------------------------------------------------------------------------
/build.fsx:
--------------------------------------------------------------------------------
1 | // --------------------------------------------------------------------------------------
2 | // FAKE build script
3 | // --------------------------------------------------------------------------------------
4 |
5 | #I "packages/FAKE/tools"
6 | #r "packages/FAKE/tools/FakeLib.dll"
7 |
8 | open System
9 | open Fake
10 | open Fake.Git
11 | open Fake.ReleaseNotesHelper
12 | open Fake.AssemblyInfoFile
13 |
14 | // --------------------------------------------------------------------------------------
15 | // Information about the project to be used at NuGet and in AssemblyInfo files
16 | // --------------------------------------------------------------------------------------
17 |
18 | let project = "LinqOptimizer"
19 | let authors = ["Nessos Information Technologies, Nick Palladinos, Kostas Rontogiannis"]
20 | let summary = "An automatic query optimizer for LINQ to Objects and PLINQ."
21 |
22 | let description = """
23 | An automatic query optimizer for LINQ to Objects and PLINQ.
24 | LinqOptimizer compiles declarative LINQ queries into fast loop-based imperative code.
25 | The compiled code has fewer virtual calls, better data locality and speedups of up to 15x.
26 | """
27 |
28 | let tags = "C# F# linq optimization"
29 |
30 | let gitHome = "https://github.com/nessos"
31 | let gitName = "LinqOptimizer"
32 | let gitRaw = environVarOrDefault "gitRaw" "https://raw.github.com/nessos"
33 | let nugetSource = "https://api.nuget.org/v3/index.json"
34 | let nugetApiKey = environVarOrDefault "NUGET_KEY" ""
35 |
36 | let testProjects =
37 | [
38 | "tests/LinqOptimizer.Tests.CSharp"
39 | "tests/LinqOptimizer.Tests.FSharp"
40 | ]
41 |
42 |
43 | // --------------------------------------------------------------------------------------
44 | // The rest of the code is standard F# build script
45 | // --------------------------------------------------------------------------------------
46 |
47 | // Read release notes & version info from RELEASE_NOTES.md
48 | Environment.CurrentDirectory <- __SOURCE_DIRECTORY__
49 | let release = parseReleaseNotes (IO.File.ReadAllLines "RELEASE_NOTES.md")
50 | let nugetVersion = release.NugetVersion
51 |
52 | Target "BuildVersion" (fun _ ->
53 | Shell.Exec("appveyor", sprintf "UpdateBuild -Version \"%s\"" nugetVersion) |> ignore
54 | )
55 |
56 | // Generate assembly info files with the right version & up-to-date information
57 | Target "AssemblyInfo" (fun _ ->
58 | let attributes =
59 | [
60 | Attribute.Title project
61 | Attribute.Product project
62 | Attribute.Company "Nessos Information Technologies"
63 | Attribute.Version release.AssemblyVersion
64 | Attribute.FileVersion release.AssemblyVersion
65 | ]
66 |
67 | CreateCSharpAssemblyInfo "src/LinqOptimizer.Base/Properties/AssemblyInfo.cs" attributes
68 | CreateFSharpAssemblyInfo "src/LinqOptimizer.Core/AssemblyInfo.fs" attributes
69 | CreateCSharpAssemblyInfo "src/LinqOptimizer.CSharp/Properties/AssemblyInfo.cs" attributes
70 | CreateFSharpAssemblyInfo "src/LinqOptimizer.FSharp/AssemblyInfo.fs" attributes
71 | )
72 |
73 |
74 | // --------------------------------------------------------------------------------------
75 | // Clean build results & restore NuGet packages
76 |
77 | Target "RestorePackages" (fun _ ->
78 | !! "./**/packages.config"
79 | |> Seq.iter (RestorePackage (fun p -> { p with ToolPath = "./.nuget/NuGet.exe" }))
80 | )
81 |
82 | Target "Clean" (fun _ ->
83 | CleanDirs (!! "**/bin/Release/")
84 | )
85 |
86 | //
87 | //// --------------------------------------------------------------------------------------
88 | //// Build library & test project
89 |
90 | let configuration = environVarOrDefault "Configuration" "Release"
91 |
92 | Target "Build" (fun _ ->
93 | DotNetCli.Build (fun c ->
94 | { c with
95 | Project = project + ".sln"
96 | Configuration = configuration })
97 | )
98 |
99 | // --------------------------------------------------------------------------------------
100 | // Run the unit tests using test runner & kill test runner when complete
101 |
102 | Target "RunTests" (fun _ ->
103 | for proj in testProjects do
104 | DotNetCli.Test (fun c ->
105 | { c with
106 | Project = proj
107 | Configuration = configuration })
108 | )
109 |
110 | //
111 | //// --------------------------------------------------------------------------------------
112 | //// Build a NuGet package
113 |
114 | Target "NuGet" (fun _ ->
115 |
116 | let mkNuGetPackage project =
117 | // Format the description to fit on a single line (remove \r\n and double-spaces)
118 | let description = description.Replace("\r", "").Replace("\n", "").Replace(" ", " ")
119 | let nugetPath = ".nuget/NuGet.exe"
120 | NuGet (fun p ->
121 | { p with
122 | Authors = authors
123 | Project = project
124 | Summary = summary
125 | Description = description
126 | Version = nugetVersion
127 | ReleaseNotes = String.concat " " release.Notes
128 | Tags = tags
129 | OutputPath = "nuget"
130 | ToolPath = nugetPath
131 | AccessKey = getBuildParamOrDefault "nugetkey" ""
132 | Publish = hasBuildParam "nugetkey" })
133 | ("nuget/" + project + ".nuspec")
134 |
135 | mkNuGetPackage "LinqOptimizer.CSharp"
136 | mkNuGetPackage "LinqOptimizer.FSharp"
137 | )
138 |
139 | Target "PublishNuGet" (fun _ ->
140 | if String.IsNullOrEmpty nugetApiKey then failwith "build did not specify a nuget API key"
141 |
142 | let pushPkg nupkg =
143 | // omg dotnet pack doesn't work wih absolute paths
144 | let relativePath = ProduceRelativePath __SOURCE_DIRECTORY__ nupkg
145 | DotNetCli.RunCommand
146 | (fun c -> { c with WorkingDir = __SOURCE_DIRECTORY__ })
147 | (sprintf "nuget push %s -s %s -k %s" relativePath nugetSource nugetApiKey)
148 |
149 | !! "NuGet/*.nupkg" |> Seq.toArray |> Array.Parallel.iter pushPkg
150 | )
151 |
152 |
153 | Target "Release" DoNothing
154 |
155 | // --------------------------------------------------------------------------------------
156 | // Run all targets by default. Invoke 'build ' to override
157 |
158 | Target "Prepare" DoNothing
159 | Target "PrepareRelease" DoNothing
160 | Target "All" DoNothing
161 |
162 | "Clean"
163 | ==> "RestorePackages"
164 | ==> "AssemblyInfo"
165 | ==> "Prepare"
166 | ==> "Build"
167 | ==> "RunTests"
168 | ==> "All"
169 |
170 | "All"
171 | ==> "PrepareRelease"
172 | ==> "NuGet"
173 | ==> "PublishNuGet"
174 | ==> "Release"
175 |
176 | RunTargetOrDefault "NuGet"
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ "X$OS" = "XWindows_NT" ] ; then
4 | # use .Net
5 |
6 | .nuget/NuGet.exe install FAKE -OutputDirectory packages -ExcludeVersion -Version 4.64.17
7 | exit_code=$?
8 | if [ $exit_code -ne 0 ]; then
9 | exit $exit_code
10 | fi
11 |
12 | packages/FAKE/tools/FAKE.exe $@ --fsiargs build.fsx
13 | else
14 |
15 | mono .nuget/NuGet.exe install FAKE -OutputDirectory packages -ExcludeVersion -Version 4.64.17
16 | exit_code=$?
17 | if [ $exit_code -ne 0 ]; then
18 | exit $exit_code
19 | fi
20 |
21 | mono packages/FAKE/tools/FAKE.exe $@ --fsiargs -d:MONO build.fsx
22 | fi
--------------------------------------------------------------------------------
/nuget/LinqOptimizer.CSharp.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | @project@
5 | @build.number@
6 | @authors@
7 | @authors@
8 | https://github.com/palladin/LinqOptimizer/blob/master/LICENSE.md
9 | https://github.com/palladin/LinqOptimizer
10 | https://avatars1.githubusercontent.com/u/5538081
11 | false
12 | @summary@
13 | @description@
14 | @releaseNotes@
15 | Copyright 2014
16 | @tags@
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/nuget/LinqOptimizer.FSharp.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | @project@
5 | @build.number@
6 | @authors@
7 | @authors@
8 | https://github.com/palladin/LinqOptimizer/blob/master/LICENSE.md
9 | https://github.com/palladin/LinqOptimizer
10 | https://avatars1.githubusercontent.com/u/5538081
11 | false
12 | @summary@
13 | @description@
14 | @releaseNotes@
15 | Copyright 2014
16 | @tags@
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.Base/LinqOptimizer.Base.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0;net45
5 | AnyCPU
6 | false
7 | true
8 | true
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.Base/ParallelQueryExpr.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using Nessos.LinqOptimizer.Core;
6 |
7 | namespace Nessos.LinqOptimizer.Base
8 | {
9 | ///
10 | /// This interface represents an optimized parallel query.
11 | ///
12 | public interface IParallelQueryExpr
13 | {
14 | ///
15 | /// The expression representing the query.
16 | ///
17 | QueryExpr Expr { get; }
18 | }
19 |
20 | ///
21 | /// This interface represents an optimized parallel query.
22 | ///
23 | /// The type of the query.
24 | public interface IParallelQueryExpr : IParallelQueryExpr { }
25 |
26 | ///
27 | /// The concrete implementation of an optimized query.
28 | ///
29 | public class ParallelQueryExprVoid : IParallelQueryExpr
30 | {
31 | private QueryExpr _expr;
32 | ///
33 | /// The expression representing the query.
34 | ///
35 | public QueryExpr Expr { get { return _expr; } }
36 |
37 | ///
38 | /// Initializes a new instance of the class.
39 | ///
40 | /// The expression.
41 | public ParallelQueryExprVoid(QueryExpr query)
42 | {
43 | _expr = query;
44 | }
45 | }
46 |
47 | ///
48 | /// The concrete implementation of an optimized query.
49 | ///
50 | public class ParallelQueryExpr : IParallelQueryExpr
51 | {
52 | private QueryExpr _expr;
53 | ///
54 | /// The expression representing the query.
55 | ///
56 | public QueryExpr Expr { get { return _expr; } }
57 |
58 | ///
59 | /// Initializes a new instance of the class.
60 | ///
61 | /// The expression.
62 | public ParallelQueryExpr(QueryExpr query)
63 | {
64 | _expr = query;
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.Base/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System.Reflection;
3 |
4 | [assembly: AssemblyTitleAttribute("LinqOptimizer")]
5 | [assembly: AssemblyProductAttribute("LinqOptimizer")]
6 | [assembly: AssemblyCompanyAttribute("Nessos Information Technologies")]
7 | [assembly: AssemblyVersionAttribute("0.7.0")]
8 | [assembly: AssemblyFileVersionAttribute("0.7.0")]
9 | namespace System {
10 | internal static class AssemblyVersionInformation {
11 | internal const System.String AssemblyTitle = "LinqOptimizer";
12 | internal const System.String AssemblyProduct = "LinqOptimizer";
13 | internal const System.String AssemblyCompany = "Nessos Information Technologies";
14 | internal const System.String AssemblyVersion = "0.7.0";
15 | internal const System.String AssemblyFileVersion = "0.7.0";
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.Base/QueryExpr.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using Nessos.LinqOptimizer.Core;
6 |
7 | namespace Nessos.LinqOptimizer.Base
8 | {
9 | ///
10 | /// This interface represents an optimized query.
11 | ///
12 | public interface IQueryExpr
13 | {
14 | ///
15 | /// The expression representing the query.
16 | ///
17 | QueryExpr Expr { get; }
18 | }
19 |
20 | ///
21 | /// This interface represents an optimized query.
22 | ///
23 | /// The type of the query.
24 | public interface IQueryExpr : IQueryExpr { }
25 |
26 | ///
27 | /// The concrete implementation of an optimized query.
28 | ///
29 | public class QueryExprVoid : IQueryExpr
30 | {
31 | private QueryExpr _expr;
32 | ///
33 | /// The expression representing the query.
34 | ///
35 | public QueryExpr Expr { get { return _expr; } }
36 |
37 | ///
38 | /// Initializes a new instance of the class.
39 | ///
40 | /// The expression.
41 | public QueryExprVoid(QueryExpr query)
42 | {
43 | _expr = query;
44 | }
45 | }
46 |
47 | ///
48 | /// The concrete implementation of an optimized query.
49 | ///
50 | /// The type of the query.
51 | public class QueryExpr : IQueryExpr
52 | {
53 | private QueryExpr _expr;
54 | ///
55 | /// The expression representing the query.
56 | ///
57 | public QueryExpr Expr { get { return _expr; } }
58 |
59 | ///
60 | /// Initializes a new instance of the class.
61 | ///
62 | /// The expression.
63 | public QueryExpr(QueryExpr query)
64 | {
65 | _expr = query;
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.CSharp/LinqOptimizer.CSharp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0;net45
5 | AnyCPU
6 | false
7 | true
8 | true
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.CSharp/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System.Reflection;
3 |
4 | [assembly: AssemblyTitleAttribute("LinqOptimizer")]
5 | [assembly: AssemblyProductAttribute("LinqOptimizer")]
6 | [assembly: AssemblyCompanyAttribute("Nessos Information Technologies")]
7 | [assembly: AssemblyVersionAttribute("0.7.0")]
8 | [assembly: AssemblyFileVersionAttribute("0.7.0")]
9 | namespace System {
10 | internal static class AssemblyVersionInformation {
11 | internal const System.String AssemblyTitle = "LinqOptimizer";
12 | internal const System.String AssemblyProduct = "LinqOptimizer";
13 | internal const System.String AssemblyCompany = "Nessos Information Technologies";
14 | internal const System.String AssemblyVersion = "0.7.0";
15 | internal const System.String AssemblyFileVersion = "0.7.0";
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.CSharp/QueryExpr.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 | using System.Text;
6 | using Nessos.LinqOptimizer.Base;
7 | using QExpr = Nessos.LinqOptimizer.Core.QueryExpr;
8 |
9 | namespace Nessos.LinqOptimizer.CSharp
10 | {
11 | ///
12 | /// Provides a set of static methods for creating queries.
13 | ///
14 | public static class QueryExpr
15 | {
16 | ///
17 | /// Creates a new query that generates a sequence of integral numbers within a specified range.
18 | ///
19 | /// The value of the first integer in the sequence.
20 | /// The number of sequential integers to generate.
21 | /// A query that contains a range of sequential integral numbers.
22 | public static IQueryExpr> Range(int start, int count)
23 | {
24 | if(count < 0 || ((long)start + (long) count) - 1L > Int64.MaxValue)
25 | throw new ArgumentOutOfRangeException("count");
26 | else
27 | return new QueryExpr>(QExpr.NewRangeGenerator(Expression.Constant(start), Expression.Constant(count)));
28 | }
29 |
30 | ///
31 | /// Creates a new query that generates a sequence that contains one repeated value.
32 | ///
33 | /// The value to be repeated.
34 | /// The number of sequential integers to generate.
35 | /// A query that contains a repeated value.
36 | public static IQueryExpr> Repeat(TResult element, int count)
37 | {
38 | if (count < 0)
39 | throw new ArgumentOutOfRangeException("count");
40 | else
41 | return new QueryExpr>(QExpr.NewRepeatGenerator(Expression.Constant(element, typeof(TResult)), Expression.Constant(count)));
42 | }
43 |
44 | ///
45 | /// Creates a query that applies a specified function to the corresponding elements of two sequences, producing a sequence of the results.
46 | ///
47 | /// The first sequence to merge.
48 | /// The first sequence to merge.
49 | /// A function that specifies how to merge the elements from the two sequences.
50 | /// A query that contains merged elements of two input sequences.
51 | public static IQueryExpr> Zip(IEnumerable first, IEnumerable second, Expression> resultSelector)
52 | {
53 | return new QueryExpr>(QExpr.NewZipWith(Expression.Constant(first), Expression.Constant(second), resultSelector));
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.Core/AccessChecker.fs:
--------------------------------------------------------------------------------
1 | namespace Nessos.LinqOptimizer.Core
2 |
3 | open System
4 | open System.Collections
5 | open System.Collections.Generic
6 | open System.Linq
7 | open System.Linq.Expressions
8 | open System.Reflection
9 | open System.Collections.Concurrent
10 |
11 | type private AccessCheckerVisitor () =
12 | inherit ExpressionVisitor() with
13 |
14 | let rec isPublic (ty : Type) =
15 | if ty = null || ty = typeof then true
16 | else ty.IsPublic && isPublic ty.DeclaringType
17 |
18 | let invalidAccesses = List()
19 |
20 | member this.InvalidAccesses with get () = invalidAccesses
21 |
22 | override this.VisitNew(expr : NewExpression) =
23 | if not(isPublic expr.Type) then
24 | invalidAccesses.Add(expr :> _, Some expr.Type.FullName)
25 | expr.Update(this.Visit expr.Arguments) :> _
26 |
27 | override this.VisitMember(expr : MemberExpression) =
28 | match expr.Member with
29 | | :? PropertyInfo as pi ->
30 | if pi.GetGetMethod() = null then invalidAccesses.Add(expr :> _, Some expr.Member.DeclaringType.FullName)
31 | | :? FieldInfo as fi ->
32 | if not fi.IsPublic || not (isPublic fi.DeclaringType) then invalidAccesses.Add(expr :> _, Some expr.Member.DeclaringType.FullName)
33 | | _ -> ()
34 |
35 | expr.Update(this.Visit expr.Expression) :> _
36 |
37 | override this.VisitMethodCall(expr : MethodCallExpression) =
38 | if not expr.Method.IsPublic || not (isPublic expr.Method.DeclaringType) then
39 | invalidAccesses.Add(expr :> _, Some expr.Method.DeclaringType.FullName)
40 | expr.Update(this.Visit expr.Object, this.Visit expr.Arguments) :> _
41 |
42 | // cast
43 | override this.VisitUnary(expr : UnaryExpression) =
44 | if expr.NodeType = ExpressionType.Convert && not (isPublic expr.Type) then invalidAccesses.Add(expr :> _, Some(expr.Type.FullName.ToString()))
45 | expr.Update(this.Visit expr.Operand) :> _
46 |
47 | module AccessChecker =
48 | let check(expr : Expression) : seq<_> option =
49 | let ac = new AccessCheckerVisitor()
50 | ac.Visit(expr) |> ignore
51 | if ac.InvalidAccesses.Count > 0 then Some(ac.InvalidAccesses :> _) else None
52 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.Core/AnonymousTypeEraser.fs:
--------------------------------------------------------------------------------
1 | namespace Nessos.LinqOptimizer.Core
2 |
3 | open System
4 | open System.Collections
5 | open System.Collections.Generic
6 | open System.Linq
7 | open System.Linq.Expressions
8 | open System.Reflection
9 | open System.Collections.Concurrent
10 | open System.Runtime.CompilerServices
11 |
12 | // Transform anonymous type access and transparent identifiers
13 | // to local variables.
14 | type private AnonymousTypeEraserVisitor () =
15 | inherit ExpressionVisitor() with
16 |
17 |
18 |
19 | let generated = sprintf "___%s___"
20 |
21 | let mappings = Dictionary()
22 | let existing = List()
23 | let redundants = List()
24 |
25 | override this.VisitMember(expr : MemberExpression) =
26 | match expr.Member.MemberType with
27 | | MemberTypes.Property when isAnonymousType expr.Member.DeclaringType ->
28 | let p = mappings.Values.SingleOrDefault(fun p -> p.Name = generated expr.Member.Name) //Expression.Parameter(expr.Type, expr.Member.Name)
29 | if p = null then
30 | let e = existing.SingleOrDefault(fun p -> p.Name = expr.Member.Name)
31 | if e = null then expr.Update(this.Visit expr.Expression) :> _ else e :> _
32 | else
33 | p :> _
34 | | _ ->
35 | //expr :> _
36 | expr.Update(this.Visit expr.Expression) :> _
37 |
38 | override this.VisitBinary(expr : BinaryExpression) =
39 | match expr with
40 | | AnonymousTypeAssign(left, right) ->
41 | let first = right.Arguments.First() :?> ParameterExpression
42 | if not <| isTransparentIdentifier first then existing.Add(first)
43 |
44 | let right' = this.Visit(right.Arguments.Last())
45 | let left' = Expression.Parameter(right'.Type, generated(right.Members.Last().Name))
46 | mappings.Add(left, left')
47 | Expression.Assign(left', right') :> _
48 | | TransparentIdentifierIdentityAssignment(ti1, ti2) ->
49 | redundants.Add(ti1)
50 | redundants.Add(ti2)
51 | Expression.Empty() :> _
52 | | _ ->
53 | expr.Update(this.Visit(expr.Left), null, this.Visit(expr.Right)) :> _
54 |
55 | override this.VisitBlock(expr : BlockExpression) =
56 | let exprs = this.Visit(expr.Expressions)
57 | let vars = expr.Variables
58 | |> Seq.map (fun p -> if mappings.ContainsKey(p) then mappings.[p] else p)
59 | |> Seq.filter (fun p -> not(Seq.exists ((=) p) redundants))
60 | Expression.Block(vars, exprs ) :> _
61 |
62 | module AnonymousTypeEraser =
63 | let apply(expr : Expression) =
64 | let ate = new AnonymousTypeEraserVisitor()
65 | ate.Visit(expr)
--------------------------------------------------------------------------------
/src/LinqOptimizer.Core/AssemblyInfo.fs:
--------------------------------------------------------------------------------
1 | // Auto-Generated by FAKE; do not edit
2 | namespace System
3 | open System.Reflection
4 |
5 | []
6 | []
7 | []
8 | []
9 | []
10 | do ()
11 |
12 | module internal AssemblyVersionInformation =
13 | let [] AssemblyTitle = "LinqOptimizer"
14 | let [] AssemblyProduct = "LinqOptimizer"
15 | let [] AssemblyCompany = "Nessos Information Technologies"
16 | let [] AssemblyVersion = "0.7.0"
17 | let [] AssemblyFileVersion = "0.7.0"
18 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.Core/CSharpExpressionOptimizer.fs:
--------------------------------------------------------------------------------
1 | namespace Nessos.LinqOptimizer.Core
2 |
3 | open System
4 | open System.Collections
5 | open System.Collections.Generic
6 | open System.Linq
7 | open System.Linq.Expressions
8 | open System.Reflection
9 | open System.Collections.Concurrent
10 |
11 | // C#/Linq call patterns
12 | // TODO: expr type checks
13 | module private CSharpExpressionOptimizerHelpers =
14 | let private sourceOfExpr (expr : Expression) sourceType : QueryExpr =
15 | if expr.Type.IsArray then
16 | Source (expr, expr.Type.GetElementType(), sourceType)
17 | elif expr.Type.IsGenericType && expr.Type.GetGenericTypeDefinition() = typedefof> then
18 | Source(expr, expr.Type.GetGenericArguments().[0], sourceType)
19 | elif expr.Type.IsGenericType then
20 | Source (expr, expr.Type.GetInterface("IEnumerable`1").GetGenericArguments().[0], sourceType)
21 | else
22 | failwithf "Not supported source %A" expr
23 |
24 | let rec toQueryExpr (expr : Expression) : QueryExpr =
25 | match expr with
26 | | MethodCall (_, MethodName "Select" _, [expr'; LambdaOrQuote ([_], bodyExpr, f')]) ->
27 | Transform (f', toQueryExpr expr')
28 |
29 | | MethodCall (_, MethodName "Select" _, [expr'; LambdaOrQuote ([_; _], bodyExpr, f')]) ->
30 | TransformIndexed (f', toQueryExpr expr')
31 |
32 | | MethodCall (_, MethodName "Where" _, [expr'; LambdaOrQuote ([paramExpr], _, f')]) ->
33 | Filter (f', toQueryExpr expr')
34 |
35 | | MethodCall (_, MethodName "Where" _, [expr'; LambdaOrQuote ([paramExpr; indexExpr], _,f')]) ->
36 | FilterIndexed (f' , toQueryExpr expr')
37 |
38 | | MethodCall (_, MethodName "Aggregate" _, [expr'; seedExpr; LambdaOrQuote ([_;_] as paramsExpr, bodyExpr, f') ] ) ->
39 | Aggregate(seedExpr, f' , toQueryExpr expr')
40 |
41 | | MethodCall (_, MethodName "Generate" _, [startExpr; Lambda (_,_) as pred; LambdaOrQuote(_,_,state); LambdaOrQuote(_,_,result)]) ->
42 | Generate(startExpr, pred :?> LambdaExpression, state, result)
43 |
44 | | MethodCall (_, MethodName "Take" _, [expr'; countExpr]) when countExpr.Type = typeof ->
45 | Take (countExpr, toQueryExpr expr' )
46 |
47 | | MethodCall (_, MethodName "TakeWhile" _, [expr'; LambdaOrQuote ([paramExpr], _,f')]) ->
48 | TakeWhile(f', toQueryExpr expr' )
49 |
50 | | MethodCall (_, MethodName "SkipWhile" _, [expr'; LambdaOrQuote ([paramExpr], _,f')]) ->
51 | SkipWhile(f', toQueryExpr expr' )
52 |
53 | | MethodCall (_, MethodName "Skip" _, [expr'; countExpr]) when countExpr.Type = typeof ->
54 | Skip (countExpr, toQueryExpr expr')
55 |
56 | | MethodCall (_, (MethodName "SelectMany" _), [expr'; LambdaOrQuote ([paramExpr], bodyExpr, _)]) ->
57 | NestedQuery ((paramExpr, toQueryExpr (bodyExpr)), toQueryExpr expr')
58 |
59 | | MethodCall (_, (MethodName "SelectMany" _), [expr'; LambdaOrQuote ([paramExpr], bodyExpr, _); LambdaOrQuote (_,_,lam)]) ->
60 | NestedQueryTransform((paramExpr, toQueryExpr (bodyExpr)), lam, toQueryExpr expr')
61 |
62 | | MethodCall (_, MethodName "GroupBy" _, [expr'; LambdaOrQuote ([paramExpr], bodyExpr,f')]) ->
63 | GroupBy (f', toQueryExpr expr', typedefof>.MakeGenericType [|bodyExpr.Type; paramExpr.Type|])
64 |
65 | | MethodCall (_, MethodName "OrderBy" _, [expr'; LambdaOrQuote ([paramExpr], bodyExpr, f')]) ->
66 | OrderBy ([f', Order.Ascending], toQueryExpr expr')
67 |
68 | | MethodCall (_, MethodName "OrderByDescending" _, [expr'; LambdaOrQuote ([paramExpr], bodyExpr, f')]) ->
69 | OrderBy ([f', Order.Descending], toQueryExpr expr')
70 |
71 | | MethodCall (_, MethodName "Count" _, [expr']) ->
72 | Count(toQueryExpr expr')
73 |
74 | | MethodCall (_, MethodName "Range" _, [startExpr; countExpr]) ->
75 | RangeGenerator(startExpr, countExpr)
76 |
77 | | MethodCall (_, MethodName "Sum" _, [expr']) ->
78 | Sum(toQueryExpr expr')
79 |
80 | | MethodCall (_, MethodName "ToList" _, [expr']) ->
81 | ToList(toQueryExpr expr')
82 |
83 | | MethodCall (_, MethodName "ToArray" _, [expr']) ->
84 | ToArray(toQueryExpr expr')
85 |
86 | | MethodCall (expr', MethodName "ForEach" _, [ LambdaOrQuote ([paramExpr], bodyExpr, f')])
87 | | MethodCall (_, MethodName "ForEach" _, [expr'; LambdaOrQuote ([paramExpr], bodyExpr, f')]) ->
88 | ForEach(f', toQueryExpr expr')
89 |
90 | | MethodCall (_, MethodName "AsQueryExpr" _, [expr']) ->
91 | sourceOfExpr expr' QueryExprType.Sequential
92 |
93 | | MethodCall (_, MethodName "AsParallelQueryExpr" _, [expr']) ->
94 | sourceOfExpr expr' QueryExprType.Parallel
95 |
96 | | NotNull expr' ->
97 | sourceOfExpr expr' QueryExprType.Sequential
98 |
99 | | _ ->
100 | invalidArg "expr" "Cannot extract QueryExpr from null Expression"
101 |
102 | and private transformer (expr : Expression) : Expression option =
103 | match expr with
104 | | MethodCall (_, MethodName "AsQueryExpr" _, [_ ])
105 | | MethodCall (_, MethodName "Select" _, [_; LambdaOrQuote _ ])
106 | | MethodCall (_, MethodName "Select" _, [_; LambdaOrQuote _ ])
107 | | MethodCall (_, MethodName "Where" _, [_; LambdaOrQuote _ ])
108 | | MethodCall (_, MethodName "Where" _, [_; LambdaOrQuote _ ])
109 | | MethodCall (_, MethodName "Aggregate" _, [_; _; LambdaOrQuote ([_;_], _, _) ])
110 | | MethodCall (_, MethodName "Take" _, [_; _ ])
111 | | MethodCall (_, MethodName "TakeWhile" _, [_; LambdaOrQuote _ ])
112 | | MethodCall (_, MethodName "SkipWhile" _, [_; LambdaOrQuote _ ])
113 | | MethodCall (_, MethodName "Skip" _, [_; _ ])
114 | | MethodCall (_, MethodName "SelectMany" _, [_; LambdaOrQuote _ ])
115 | | MethodCall (_, MethodName "SelectMany" _, [_; LambdaOrQuote _; LambdaOrQuote _])
116 | | MethodCall (_, MethodName "GroupBy" _, [_; LambdaOrQuote _ ])
117 | | MethodCall (_, MethodName "OrderBy" _, [_; LambdaOrQuote _ ])
118 | | MethodCall (_, MethodName "OrderByDescending" _, [_; LambdaOrQuote _ ])
119 | | MethodCall (_, MethodName "Count" _, [_ ])
120 | | MethodCall (_, MethodName "Range" _, [_; _ ])
121 | | MethodCall (_, MethodName "Sum" _, [_ ])
122 | | MethodCall (_, MethodName "ToList" _, [_ ])
123 | | MethodCall (_, MethodName "ToArray" _, [_ ])
124 | | MethodCall (_, MethodName "ForEach" _, [_; LambdaOrQuote _ ])
125 | | MethodCall (_, MethodName "ForEach" _, [ LambdaOrQuote _ ]) ->
126 | let query = toQueryExpr expr
127 | let expr = (Compiler.compileToSequential query optimize)
128 | Some expr
129 | | _ ->
130 | None
131 |
132 | and private opt = ExpressionTransformer.transform transformer
133 |
134 | and optimize (expr : Expression) : Expression = opt expr
135 |
136 | type CSharpExpressionOptimizer =
137 | static member Optimize(expr : Expression) : Expression =
138 | CSharpExpressionOptimizerHelpers.optimize(expr)
139 | static member ToQueryExpr(expr : Expression) : QueryExpr =
140 | CSharpExpressionOptimizerHelpers.toQueryExpr(expr)
141 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.Core/Collector.fs:
--------------------------------------------------------------------------------
1 | namespace Nessos.LinqOptimizer.Core
2 |
3 | open System
4 | open System.Collections
5 | open System.Collections.Generic
6 | open System.Linq
7 | open System.Linq.Expressions
8 | open System.Reflection
9 | open System.Collections.Concurrent
10 |
11 | type ArrayCollector<'T> () =
12 |
13 | let ls = List<'T [] * int>()
14 |
15 | let mutable isAdd = false
16 |
17 | let bufferSize = 1024
18 | let mutable currentBufferSize = bufferSize
19 | let newBuffer( size ) = currentBufferSize <- size; Array.zeroCreate<'T> size
20 | let mutable buffer : 'T [] = newBuffer bufferSize
21 | let mutable idx = 0
22 |
23 | let flushBuffer () =
24 | ls.Add(buffer, idx)
25 | buffer <- newBuffer bufferSize
26 | isAdd <- false
27 | idx <- 0
28 |
29 | member this.AddRange(collector : ArrayCollector<'T>) =
30 | this.Flush()
31 | collector.Flush()
32 | Seq.iter ls.Add collector.ArrayList
33 |
34 | member this.Add(elem : 'T) =
35 | isAdd <- true
36 | if idx >= currentBufferSize then
37 | let newBuf = newBuffer (currentBufferSize * 2)
38 | Array.Copy(buffer, newBuf, idx)
39 | buffer <- newBuf
40 | this.Add(elem)
41 | else
42 | buffer.[idx] <- elem
43 | idx <- idx + 1
44 |
45 | member this.ToArray () : 'T [] =
46 | this.Flush()
47 |
48 | let mutable length = 0
49 | for (_, l) in ls do
50 | length <- length + l
51 |
52 | let final = Array.zeroCreate length
53 | let mutable currIdx = 0
54 | for (array, l) in ls do
55 | Array.Copy(array, 0, final, currIdx, l)
56 | currIdx <- currIdx + l
57 | final
58 |
59 | member this.ToList () : List<'T> =
60 | List<'T>(this.ToArray())
61 |
62 | member internal this.ArrayList with get () = ls
63 |
64 | member internal this.Flush () =
65 | if isAdd then flushBuffer ()
66 |
67 | interface IEnumerable<'T> with
68 | member this.GetEnumerator() =
69 | let array = this.ToArray() :> IEnumerable<'T>
70 | array.GetEnumerator()
71 |
72 | member this.GetEnumerator() =
73 | let list = this.ToArray()
74 | list.GetEnumerator()
75 |
76 | interface IOrderedEnumerable<'T> with
77 | member __.CreateOrderedEnumerable<'TKey>(keySelector : Func<'T, 'TKey> , comparer : IComparer<'TKey>, desc : bool) =
78 | raise <| NotImplementedException()
79 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.Core/ConstantLiftingTransformer.fs:
--------------------------------------------------------------------------------
1 | namespace Nessos.LinqOptimizer.Core
2 |
3 | open System
4 | open System.Collections
5 | open System.Collections.Generic
6 | open System.Linq
7 | open System.Linq.Expressions
8 | open System.Reflection
9 | open System.Collections.Concurrent
10 |
11 | // Lift constants and member access into parameters
12 | // due to the live-object limitation.
13 | type private ConstantLiftingVisitor () =
14 | inherit ExpressionVisitor() with
15 | let mutable x = 0
16 | let getName () =
17 | x <- x + 1
18 | sprintf "___param%d___" x
19 |
20 | let x = Dictionary()
21 |
22 | member this.Environment with get () = x
23 |
24 | override this.VisitConstant(expr : ConstantExpression) =
25 | if not <| isPrimitive expr && expr.Value <> null then
26 | let p = Expression.Parameter(expr.Type, getName())
27 | this.Environment.Add(p, expr.Value)
28 | p :> _
29 | else
30 | expr :> _
31 |
32 | override this.VisitMember(expr : MemberExpression) =
33 | if expr.Expression :? ConstantExpression then
34 |
35 | let obj = (expr.Expression :?> ConstantExpression).Value
36 |
37 | let (value, p) =
38 | match expr.Member.MemberType with
39 | | MemberTypes.Field ->
40 | let fi = expr.Member :?> FieldInfo
41 | fi.GetValue(obj), Expression.Parameter(expr.Type, sprintf "___param%s___" fi.Name)
42 | | MemberTypes.Property ->
43 | let pi = expr.Member :?> PropertyInfo
44 | let indexed = pi.GetIndexParameters() |> Seq.cast |> Seq.toArray
45 | pi.GetValue(obj, indexed), Expression.Parameter(expr.Type, sprintf "___param%s___" pi.Name)
46 | | _ ->
47 | failwithf "Internal error : Accessing non Field or Property from MemberExpression %A" expr
48 |
49 | this.Environment.Add(p, value)
50 | p :> _
51 | else
52 | expr.Update(this.Visit expr.Expression) :> _
53 |
54 | module ConstantLiftingTransformer =
55 | let apply(expr : Expression) =
56 | let clv = new ConstantLiftingVisitor()
57 | let expr = clv.Visit(expr)
58 | expr, clv.Environment.Keys.ToArray(), clv.Environment.Values.ToArray()
--------------------------------------------------------------------------------
/src/LinqOptimizer.Core/ExpressionHelpers.fs:
--------------------------------------------------------------------------------
1 |
2 | namespace Nessos.LinqOptimizer.Core
3 | open System
4 | open System.Linq
5 | open System.Linq.Expressions
6 | open System.Reflection
7 | open System.Runtime.CompilerServices
8 |
9 | []
10 | module ExpressionHelpers =
11 | // F# friendly Expression functions
12 | let empty = Expression.Empty()
13 | let ``default`` t = Expression.Default(t)
14 | let block (varExprs : seq) (exprs : seq) =
15 | Expression.Block(varExprs, exprs) :> Expression
16 | let tryfinally bodyExpr finallyExpr =
17 | Expression.TryFinally(bodyExpr, finallyExpr)
18 | let assign leftExpr rightExpr =
19 | Expression.Assign(leftExpr, rightExpr) :> Expression
20 | let addAssign leftExpr rightExpr =
21 | Expression.AddAssign (leftExpr, rightExpr) :> Expression
22 | let subAssign leftExpr rightExpr =
23 | Expression.SubtractAssign(leftExpr, rightExpr) :> Expression
24 |
25 | let cast expr ty =
26 | Expression.Convert(expr, ty) :> Expression
27 |
28 | let var name (t : Type) = Expression.Parameter(t, name)
29 | let lambda paramExprs bodyExpr =
30 | Expression.Lambda(bodyExpr, paramExprs)
31 |
32 | let labelTarget (name : string) = Expression.Label(name)
33 | let label (labelTarget : LabelTarget) = Expression.Label(labelTarget)
34 | let goto (labelTarget : LabelTarget) = Expression.Goto(labelTarget)
35 |
36 | let constant (value : obj) : Expression = Expression.Constant(value) :> _
37 |
38 | let ``new`` (t : Type) = Expression.New(t)
39 | let call (methodInfo : MethodInfo) (instance : Expression)
40 | (args : seq) =
41 | Expression.Call(instance, methodInfo, args) :> Expression
42 |
43 | let ifThenElse boolExpr thenExpr elseExpr =
44 | Expression.IfThenElse(boolExpr, thenExpr, elseExpr) :> Expression
45 | let ifThen boolExpr thenExpr =
46 | Expression.IfThen(boolExpr, thenExpr) :> Expression
47 | let loop bodyExpr breakLabel continueLabel =
48 | Expression.Loop(bodyExpr, breakLabel, continueLabel)
49 | let ``break`` (label : LabelTarget) =
50 | Expression.Break(label)
51 | let ``continue`` (label : LabelTarget) =
52 | Expression.Continue(label)
53 |
54 |
55 | let equal leftExpr rightExpr = Expression.Equal(leftExpr, rightExpr)
56 | let greaterThan leftExpr rightExpr = Expression.GreaterThan(leftExpr, rightExpr)
57 | let greaterThanOrEqual leftExpr rightExpr = Expression.GreaterThanOrEqual(leftExpr, rightExpr)
58 | let lessThan leftExpr rightExpr = Expression.LessThan(leftExpr, rightExpr)
59 | let lessThanOrEqual leftExpr rightExpr = Expression.LessThanOrEqual(leftExpr, rightExpr)
60 | let notEqual leftExpr rightExpr = Expression.NotEqual(leftExpr, rightExpr)
61 |
62 | let notExpr expr = Expression.Not(expr)
63 |
64 | let arrayNew (t : Type) (lengthExpr : Expression) =
65 | Expression.NewArrayBounds(t, [|lengthExpr|])
66 | let arrayAccess (arrayExpr : Expression) (indexExpr : Expression) =
67 | Expression.ArrayAccess(arrayExpr, indexExpr)
68 | let arrayIndex (arrayExpr : Expression) (indexExpr : Expression) =
69 | Expression.ArrayIndex(arrayExpr, indexExpr)
70 | let arrayLength arrayExpr = Expression.ArrayLength(arrayExpr)
71 |
72 | let isPrimitive (expr : ConstantExpression) =
73 | expr.Type.IsPrimitive || expr.Type = typeof
74 |
75 | let (|Map|) (f : 'T -> 'S) (v : 'T) = f v
76 |
77 | let (|QuotedLambda|_|) (expr : Expression) =
78 | match expr with
79 | | :? UnaryExpression as unaryExpr when unaryExpr.NodeType = ExpressionType.Quote ->
80 | match unaryExpr.Operand with
81 | | :? LambdaExpression as lam -> Some lam
82 | | _ -> None
83 | | _ -> None
84 |
85 | // Expression Active Patterns
86 | let (|Lambda|_|) (expr : Expression) =
87 | match expr with
88 | | :? LambdaExpression as lam -> Some (Seq.toList lam.Parameters, lam.Body)
89 | | _ -> None
90 |
91 | // Expression Active Patterns
92 | let (|LambdaOrQuote|_|) (expr : Expression) =
93 | match expr with
94 | | Lambda(param, body) ->
95 | Some (Seq.toList param, body, expr :?> LambdaExpression)
96 | | QuotedLambda lam ->
97 | Some (Seq.toList lam.Parameters, lam.Body, lam)
98 | | _ ->
99 | None
100 |
101 |
102 | let (|MethodCall|_|) (expr : Expression) =
103 | if expr <> null && (expr.NodeType = ExpressionType.Call && (expr :? MethodCallExpression))
104 | then
105 | let methodCallExpr = expr :?> MethodCallExpression
106 | Some (methodCallExpr.Object, methodCallExpr.Method, Seq.toList methodCallExpr.Arguments)
107 | else None
108 |
109 | let (|NotNull|_|) (expr : Expression) =
110 | if expr <> null then Some expr else None
111 |
112 | let (|ExprType|) (expr : Expression) = ExprType expr.Type
113 |
114 | let (|Constant|_|) (expr : Expression) =
115 | match expr with
116 | | :? ConstantExpression as constExpr -> Some (constExpr.Value, constExpr.Type)
117 | | _ -> None
118 |
119 | let (|Parameter|_|) (expr : Expression) =
120 | match expr with
121 | | :? ParameterExpression as paramExpr -> Some (paramExpr)
122 | | _ -> None
123 |
124 | let (|Assign|_|) (expr : Expression) =
125 | match expr with
126 | | :? BinaryExpression as assignExpr
127 | when assignExpr.NodeType = ExpressionType.Assign -> Some (assignExpr.Left, assignExpr.Right)
128 | | _ -> None
129 |
130 | let (|AddAssign|_|) (expr : Expression) =
131 | match expr with
132 | | :? BinaryExpression as assignExpr
133 | when assignExpr.NodeType = ExpressionType.AddAssign -> Some (assignExpr.Left, assignExpr.Right)
134 | | _ -> None
135 |
136 | let (|Plus|_|) (expr : Expression) =
137 | match expr with
138 | | :? BinaryExpression as plusExpr
139 | when plusExpr.NodeType = ExpressionType.Add -> Some (plusExpr.Left, plusExpr.Right)
140 | | _ -> None
141 |
142 | let (|Subtract|_|) (expr : Expression) =
143 | match expr with
144 | | :? BinaryExpression as plusExpr
145 | when plusExpr.NodeType = ExpressionType.Subtract -> Some (plusExpr.Left, plusExpr.Right)
146 | | _ -> None
147 |
148 | let (|Divide|_|) (expr : Expression) =
149 | match expr with
150 | | :? BinaryExpression as plusExpr
151 | when plusExpr.NodeType = ExpressionType.Divide -> Some (plusExpr.Left, plusExpr.Right)
152 | | _ -> None
153 |
154 | let (|Times|_|) (expr : Expression) =
155 | match expr with
156 | | :? BinaryExpression as plusExpr
157 | when plusExpr.NodeType = ExpressionType.Multiply -> Some (plusExpr.Left, plusExpr.Right)
158 | | _ -> None
159 |
160 | let (|Modulo|_|) (expr : Expression) =
161 | match expr with
162 | | :? BinaryExpression as plusExpr
163 | when plusExpr.NodeType = ExpressionType.Modulo -> Some (plusExpr.Left, plusExpr.Right)
164 | | _ -> None
165 |
166 | let (|Negate|_|) (expr : Expression) =
167 | match expr with
168 | | :? UnaryExpression as unaryExpr
169 | when unaryExpr.NodeType = ExpressionType.Negate -> Some (unaryExpr.Operand)
170 | | _ -> None
171 |
172 | let (|IfThenElse|_|) (expr : Expression) =
173 | match expr with
174 | | :? ConditionalExpression as contExpr ->
175 | Some (contExpr.Test, contExpr.IfTrue, contExpr.IfFalse)
176 | | _ -> None
177 |
178 |
179 | let (|Equal|_|) (expr : Expression) =
180 | match expr with
181 | | :? BinaryExpression as equalExpr
182 | when equalExpr.NodeType = ExpressionType.Equal -> Some (equalExpr.Left, equalExpr.Right)
183 | | _ -> None
184 |
185 | let (|NotEqual|_|) (expr : Expression) =
186 | match expr with
187 | | :? BinaryExpression as equalExpr
188 | when equalExpr.NodeType = ExpressionType.NotEqual -> Some (equalExpr.Left, equalExpr.Right)
189 | | _ -> None
190 |
191 | let (|GreaterThan|_|) (expr : Expression) =
192 | match expr with
193 | | :? BinaryExpression as equalExpr
194 | when equalExpr.NodeType = ExpressionType.GreaterThan -> Some (equalExpr.Left, equalExpr.Right)
195 | | _ -> None
196 |
197 | let (|GreaterThanOrEqual|_|) (expr : Expression) =
198 | match expr with
199 | | :? BinaryExpression as equalExpr
200 | when equalExpr.NodeType = ExpressionType.GreaterThanOrEqual -> Some (equalExpr.Left, equalExpr.Right)
201 | | _ -> None
202 |
203 | let (|LessThan|_|) (expr : Expression) =
204 | match expr with
205 | | :? BinaryExpression as equalExpr
206 | when equalExpr.NodeType = ExpressionType.LessThan -> Some (equalExpr.Left, equalExpr.Right)
207 | | _ -> None
208 |
209 | let (|LessThanOrEqual|_|) (expr : Expression) =
210 | match expr with
211 | | :? BinaryExpression as equalExpr
212 | when equalExpr.NodeType = ExpressionType.LessThanOrEqual -> Some (equalExpr.Left, equalExpr.Right)
213 | | _ -> None
214 |
215 | let (|Nop|_|) (expr : Expression) =
216 | match expr with
217 | | :? DefaultExpression as defaultExpr -> Some (defaultExpr)
218 | | _ -> None
219 |
220 | let (|Goto|_|) (expr : Expression) =
221 | match expr with
222 | | :? GotoExpression as gotoExpr -> Some (gotoExpr.Kind, gotoExpr.Target, gotoExpr.Value)
223 | | _ -> None
224 |
225 | let (|Block|_|) (expr : Expression) =
226 | match expr with
227 | | :? BlockExpression as blockExpr -> Some (blockExpr.Variables, blockExpr.Expressions, blockExpr.Result)
228 | | _ -> None
229 |
230 | let (|Convert|_|) (expr : Expression) =
231 | match expr with
232 | | :? UnaryExpression as unaryExpr when unaryExpr.NodeType = ExpressionType.Convert ->
233 | Some (unaryExpr.Operand, unaryExpr.Type)
234 | | _ -> None
235 |
236 |
237 | // http://stackoverflow.com/questions/1650681/determining-whether-a-type-is-an-anonymous-type
238 | // TODO : Mono?
239 | let isAnonymousType (ty : Type) =
240 | ty.GetCustomAttributes(typeof, false).Count() > 0
241 | && ty.FullName.Contains "AnonymousType"
242 | && ty.Namespace = null
243 | && ty.IsSealed
244 | && not ty.IsPublic
245 |
246 | // TODO : Mono?
247 | let isTransparentIdentifier (expr : Expression) =
248 | match expr with
249 | | :? ParameterExpression as expr -> expr.Name.Contains "TransparentIdentifier"
250 | | _ -> false
251 |
252 | let isAnonymousConstructor (expr : Expression) =
253 | match expr with
254 | | :? NewExpression as expr -> isAnonymousType expr.Constructor.DeclaringType
255 | | _ -> false
256 |
257 | let (|AnonymousTypeAssign|_|) (expr : Expression) =
258 | if expr.NodeType = ExpressionType.Assign then
259 | let expr = expr :?> BinaryExpression
260 | if isTransparentIdentifier expr.Left && isAnonymousConstructor expr.Right then
261 | let left = expr.Left :?> ParameterExpression
262 | let right = expr.Right :?> NewExpression
263 | Some(left, right)
264 | else None
265 | else None
266 |
267 | let (|AnonymousTypeConstruction|_|) (expr : Expression) =
268 | match expr with
269 | | :? NewExpression as expr when isAnonymousType expr.Constructor.DeclaringType -> Some (expr.Members, expr.Arguments)
270 | | _ -> None
271 |
272 | let (|TransparentIdentifierIdentityAssignment|_|) (expr : BinaryExpression) =
273 | if expr.NodeType = ExpressionType.Assign
274 | && isTransparentIdentifier expr.Left
275 | && isTransparentIdentifier expr.Right then
276 | let left = expr.Left :?> ParameterExpression
277 | let right = expr.Right :?> ParameterExpression
278 | if left.Name = right.Name then Some (left, right)
279 | else None
280 | else
281 | None
282 |
283 | let (|AnonymousTypeMember|_|) (expr : Expression) =
284 | match expr with
285 | | :? MemberExpression as expr ->
286 | match expr.Member.MemberType with
287 | | MemberTypes.Property when isAnonymousType expr.Member.DeclaringType -> Some expr
288 | | _ -> None
289 | | _ -> None
290 |
291 | let (|FieldMember|_|) (expr : Expression) =
292 | match expr with
293 | | :? MemberExpression as expr ->
294 | match expr.Member.MemberType with
295 | | MemberTypes.Field -> Some (expr.Expression, expr.Member :?> FieldInfo)
296 | | _ -> None
297 | | _ -> None
298 |
299 | let (|PropertyMember|_|) (expr : Expression) =
300 | match expr with
301 | | :? MemberExpression as expr ->
302 | match expr.Member.MemberType with
303 | | MemberTypes.Property -> Some (expr.Expression, expr.Member)
304 | | _ -> None
305 | | _ -> None
306 |
307 | let (|ValueTypeMemberInit|_|) (expr : Expression) =
308 | match expr with
309 | | :? MemberInitExpression as expr when expr.Type.IsValueType -> Some (expr.NewExpression, expr.Bindings |> Seq.map (fun binding -> binding :?> MemberAssignment))
310 | | _ -> None
311 |
312 | let (|Loop|_|) (expr : Expression) =
313 | match expr with
314 | | :? LoopExpression as expr -> Some (expr.Body, expr.BreakLabel, expr.ContinueLabel)
315 | | _ -> None
316 |
317 | type internal Expression with
318 | static member ofFSharpFunc<'T,'R>(func : Expression>) = func
319 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.Core/ExpressionTransformer.fs:
--------------------------------------------------------------------------------
1 | namespace Nessos.LinqOptimizer.Core
2 |
3 | open System
4 | open System.Collections
5 | open System.Collections.Generic
6 | open System.Linq
7 | open System.Linq.Expressions
8 | open System.Reflection
9 | open System.Collections.Concurrent
10 |
11 | module internal ExpressionTransformer =
12 |
13 | type private ExpressionTransformer (transformer : Expression -> Expression option) =
14 |
15 | inherit ExpressionVisitor() with
16 | override this.Visit (expr : Expression) =
17 | match transformer expr with
18 | | None -> base.Visit(expr)
19 | | Some expr -> expr
20 |
21 | let transform (transformer : Expression -> Expression option) =
22 | let t = new ExpressionTransformer(transformer)
23 | fun (expr : Expression) -> t.Visit expr
24 |
25 |
26 | // inherit ExpressionVisitor() with
27 | // member private this.Transformer = transformer
28 | //
29 | // override this.VisitBinary(expr : BinaryExpression) =
30 | // match transformer expr with
31 | // | None ->
32 | // let l = this.Visit expr.Left
33 | // let r = this.Visit expr.Right
34 | // Expression.MakeBinary(expr.NodeType, l, r) :> _
35 | // | Some expr ->
36 | // expr
37 | //
38 | // override this.VisitBlock(expr : BlockExpression) =
39 | // let exprs' = this.Visit expr.Expressions
40 | // let e = Expression.Block(exprs')
41 | // defaultArg (transformer e) (e :> _)
42 | //
43 | // override this.VisitCatchBlock(expr : CatchBlock) =
44 | // ExpressionTransformer.VisitCatchBlockWrapper(this, expr)
45 | //
46 | // override this.VisitConditional(expr : ConditionalExpression) =
47 | // let ifTrue = this.Visit expr.IfTrue
48 | // let ifFalse = this.Visit expr.IfFalse
49 | // let test = this.Visit expr.Test
50 | // let e = Expression.Condition(test, ifTrue, ifFalse, expr.Type)
51 | // defaultArg (transformer e) (e :> _)
52 | //
53 | // override this.VisitConstant(expr : ConstantExpression) =
54 | // defaultArg (transformer expr) (expr :> _)
55 | //
56 | // override this.VisitDebugInfo(expr : DebugInfoExpression) =
57 | // defaultArg (transformer expr) (expr :> _)
58 | //
59 | // override this.VisitDefault(expr : DefaultExpression) =
60 | // defaultArg (transformer expr) (expr :> _)
61 | //
62 | // override this.VisitDynamic(expr : DynamicExpression) =
63 | // let args = this.Visit expr.Arguments
64 | // let e = expr.Update(args)
65 | // defaultArg (transformer e) (e :> _)
66 | //
67 | // override this.VisitElementInit(expr : ElementInit) =
68 | // ExpressionTransformer.VisitElementInitWrapper(this, expr)
69 | //
70 | // override this.VisitExtension(expr : Expression) =
71 | // defaultArg (transformer expr) expr
72 | //
73 | // override this.VisitGoto(expr : GotoExpression) =
74 | // let value = this.Visit expr.Value
75 | // let e = expr.Update(expr.Target, value)
76 | // defaultArg (transformer e) (e :> _)
77 | //
78 | // override this.VisitIndex(expr : IndexExpression) =
79 | // let o = this.Visit expr.Object
80 | // let args = this.Visit expr.Arguments
81 | // let e = expr.Update(o, args)
82 | // defaultArg (transformer e) (e :> _)
83 | //
84 | // override this.VisitInvocation(expr : InvocationExpression) =
85 | // let expr' = this.Visit expr.Expression
86 | // let args = this.Visit expr.Arguments
87 | // let e = expr.Update(expr', args)
88 | // defaultArg (transformer e) (e :> _)
89 | //
90 | // override this.VisitLabel(expr : LabelExpression) =
91 | // let value = this.Visit(expr.DefaultValue)
92 | // let e = expr.Update(expr.Target, value)
93 | // defaultArg (transformer e) (e :> _)
94 | //
95 | // override this.VisitLabelTarget(expr : LabelTarget) =
96 | // expr
97 | //
98 | // override this.VisitLambda<'T>(expr : Expression<'T>) =
99 | // let body = this.Visit expr.Body
100 | // let par = expr.Parameters |> Seq.map (fun e -> ExpressionTransformer.VisitParameterWrapper(this,e)) |> Seq.cast
101 | // let e = expr.Update(body, par)
102 | // defaultArg (transformer e) (e :> _)
103 | //
104 | // override this.VisitListInit(expr : ListInitExpression) =
105 | // let newExpr = this.Visit expr.NewExpression :?> NewExpression
106 | // let inits = expr.Initializers |> Seq.map (fun ei -> ExpressionTransformer.VisitElementInitWrapper(this, ei))
107 | // let e = expr.Update(newExpr, inits)
108 | // defaultArg (transformer e) (e :> _)
109 | //
110 | // override this.VisitLoop(expr : LoopExpression) =
111 | // let body = this.Visit expr.Body
112 | // let e = expr.Update(expr.BreakLabel, expr.ContinueLabel, body)
113 | // defaultArg (transformer e) (e :> _)
114 | //
115 | // override this.VisitMember(expr : MemberExpression) =
116 | // let expr' = this.Visit expr.Expression
117 | // let e = expr.Update(expr')
118 | // defaultArg (transformer e) (e :> _)
119 | //
120 | // override this.VisitMemberAssignment(expr : MemberAssignment) =
121 | // let expr' = this.Visit expr.Expression
122 | // expr.Update(expr')
123 | //
124 | // override this.VisitMemberBinding(expr : MemberBinding) =
125 | // ExpressionTransformer.VisitMemberBindingWrapper(this, expr)
126 | //
127 | // override this.VisitMemberInit(expr : MemberInitExpression) =
128 | // let newExpr = this.Visit expr.NewExpression :?> NewExpression
129 | // let bindings = expr.Bindings |> Seq.map (fun mb -> ExpressionTransformer.VisitMemberBindingWrapper(this, mb))
130 | // let e = expr.Update(newExpr, bindings)
131 | // defaultArg (transformer e) (e :> _)
132 | //
133 | // override this.VisitMemberListBinding(expr : MemberListBinding) =
134 | // let inits = expr.Initializers |> Seq.map (fun ei -> ExpressionTransformer.VisitElementInitWrapper(this,ei))
135 | // expr.Update(inits)
136 | //
137 | // override this.VisitMemberMemberBinding(expr : MemberMemberBinding) =
138 | // let binds = expr.Bindings |> Seq.map (fun mb -> ExpressionTransformer.VisitMemberBindingWrapper(this, mb))
139 | // expr.Update(binds)
140 | //
141 | // override this.VisitMethodCall(expr : MethodCallExpression) =
142 | // match transformer expr with
143 | // | None ->
144 | // let o = this.Visit expr.Object
145 | // let args = this.Visit expr.Arguments
146 | // expr.Update(o, args) :> _
147 | // | Some expr ->
148 | // this.Visit expr
149 | //
150 | // override this.VisitNew(expr : NewExpression) =
151 | // let args = this.Visit expr.Arguments
152 | // let e = expr.Update(args)
153 | // defaultArg (transformer e) (e :> _)
154 | //
155 | // override this.VisitNewArray(expr : NewArrayExpression) =
156 | // let exprs = this.Visit expr.Expressions
157 | // let e = expr.Update(exprs)
158 | // defaultArg (transformer e) (e :> _)
159 | //
160 | // override this.VisitParameter(expr : ParameterExpression) =
161 | // ExpressionTransformer.VisitParameterWrapper(this,expr)
162 | //
163 | // override this.VisitRuntimeVariables(expr : RuntimeVariablesExpression) =
164 | // let vars = expr.Variables |> Seq.map (fun e -> ExpressionTransformer.VisitParameterWrapper(this,e)) |> Seq.cast
165 | // let e = expr.Update(vars)
166 | // defaultArg (transformer e) (e :> _)
167 | //
168 | // override this.VisitSwitch(expr : SwitchExpression) =
169 | // let value = this.Visit expr.SwitchValue
170 | // let cases = expr.Cases |> Seq.map (fun sc -> ExpressionTransformer.VisitSwitchCaseWrapper(this, sc))
171 | // let defaultBody = this.Visit expr.DefaultBody
172 | // let e = expr.Update(value, cases, defaultBody)
173 | // defaultArg (transformer e) (e :> _)
174 | //
175 | // override this.VisitSwitchCase(expr : SwitchCase) =
176 | // ExpressionTransformer.VisitSwitchCaseWrapper(this, expr)
177 | //
178 | // override this.VisitTry(expr : TryExpression) =
179 | // let body = this.Visit expr.Body
180 | // let handlers = expr.Handlers |> Seq.map (fun handler -> ExpressionTransformer.VisitCatchBlockWrapper(this, handler))
181 | // let finallyExpr = this.Visit expr.Finally
182 | // let fault = this.Visit expr.Fault
183 | // let e = expr.Update(body, handlers, finallyExpr, fault)
184 | // defaultArg (transformer e) (e :> _)
185 | //
186 | // override this.VisitTypeBinary(expr : TypeBinaryExpression) =
187 | // let expr' = this.Visit expr.Expression
188 | // let e = expr.Update(expr')
189 | // defaultArg (transformer e) (e :> _)
190 | //
191 | // override this.VisitUnary(expr : UnaryExpression) =
192 | // let expr' = this.Visit expr.Operand
193 | // let e = expr.Update(expr')
194 | // defaultArg (transformer e) (e :> _)
195 | //
196 | //
197 | // static member private VisitParameterWrapper(visitor : ExpressionTransformer, expr : ParameterExpression) =
198 | // defaultArg (visitor.Transformer (expr :> _)) (expr :> _)
199 | //
200 | // static member private VisitElementInitWrapper(visitor : ExpressionVisitor, expr : ElementInit) =
201 | // let args = visitor.Visit(expr.Arguments)
202 | // expr.Update(args)
203 | //
204 | // static member private VisitMemberBindingWrapper(visitor : ExpressionVisitor, expr : MemberBinding) =
205 | // expr
206 | //
207 | // static member private VisitSwitchCaseWrapper(visitor : ExpressionVisitor, expr : SwitchCase) =
208 | // let tests = visitor.Visit expr.TestValues
209 | // let body = visitor.Visit expr.Body
210 | // expr.Update(tests, body)
211 | //
212 | // static member private VisitCatchBlockWrapper(visitor : ExpressionVisitor, expr : CatchBlock) =
213 | // let var = visitor.Visit expr.Variable :?> ParameterExpression
214 | // let body = visitor.Visit expr.Body
215 | // let filter = visitor.Visit expr.Filter
216 | // expr.Update(var, filter, body)
--------------------------------------------------------------------------------
/src/LinqOptimizer.Core/ExtensionMethods.fs:
--------------------------------------------------------------------------------
1 |
2 | namespace Nessos.LinqOptimizer.Core
3 |
4 | open System
5 | open System.Collections
6 | open System.Collections.Generic
7 | open System.Linq
8 | open System.Linq.Expressions
9 | open System.Reflection
10 | open System.Reflection.Emit
11 |
12 |
13 | module CompiledThunks =
14 | let cache = Concurrent.ConcurrentDictionary()
15 |
16 | type CoreHelpers =
17 |
18 |
19 | static member AsQueryExpr(enumerable : IEnumerable, ty : Type) : QueryExpr =
20 | // Hack removed
21 | //// Hack to optimize Enumerable.Range and Enumerable.Repeat calls
22 | //// TODO : check Mono generated types
23 | //let t = enumerable.GetType()
24 | //match t.FullName with
25 | //| s when s.StartsWith "System.Linq.Enumerable+" ->
26 | // let start = t.GetFields().First(fun f -> f.Name.EndsWith "__start").GetValue(enumerable)
27 | // let count = t.GetFields().First(fun f -> f.Name.EndsWith "__count").GetValue(enumerable)
28 | // RangeGenerator(constant start , constant count)
29 | //| s when s.StartsWith "System.Linq.Enumerable+" ->
30 | // let element = t.GetFields().First(fun f -> f.Name.EndsWith "__element").GetValue(enumerable)
31 | // let count = t.GetFields().First(fun f -> f.Name.EndsWith "__count").GetValue(enumerable)
32 | // RepeatGenerator(Expression.Convert(constant element, ty) , constant count)
33 | //| _ ->
34 | Source (constant enumerable, ty, QueryExprType.Sequential)
35 |
36 | static member private CompileToMethod(query : QueryExpr, compile : QueryExpr -> Expression) : Func<'T> =
37 | let source = query.ToString()
38 | let expr = compile query
39 | let expr = TupleElimination.apply(expr)
40 | let expr = AnonymousTypeEraser.apply(expr)
41 | let expr, pms, objs = ConstantLiftingTransformer.apply(expr)
42 |
43 | if CompiledThunks.cache.ContainsKey(source) then
44 | let func = CompiledThunks.cache.[source] :?> Func
45 | Func<'T>(fun () -> func.Invoke(objs) :?> 'T)
46 | else
47 | let lambda = Expression.Lambda(expr, pms)
48 | let dele = Session.Compile(lambda)
49 | let iaccs = AccessChecker.check lambda
50 | let func = CoreHelpers.WrapInvocation(dele, iaccs)
51 | CompiledThunks.cache.TryAdd(source, func) |> ignore
52 | Func<'T>(fun () -> func.Invoke(objs) :?> 'T)
53 |
54 | static member private Compile(query : QueryExpr, compile : QueryExpr -> Expression) : Func<'T> =
55 | let source = sprintf "allowNonPublicMemberAccess query (%s)" <| query.ToString()
56 | let expr = compile query
57 | let expr = TupleElimination.apply(expr)
58 | let expr = AnonymousTypeEraser.apply(expr)
59 | let expr, pms, objs = ConstantLiftingTransformer.apply(expr)
60 |
61 | if CompiledThunks.cache.ContainsKey(source) then
62 | let func = CompiledThunks.cache.[source]
63 | Func<'T>(fun () -> func.DynamicInvoke(objs) :?> 'T)
64 | else
65 | let lambda = Expression.Lambda(expr, pms)
66 | let func = lambda.Compile()
67 | CompiledThunks.cache.TryAdd(source, func) |> ignore
68 | Func<'T>(fun () -> func.DynamicInvoke(objs) :?> 'T)
69 |
70 | static member private WrapInvocation<'T>(dele : Delegate, iaccs : seq option) : Func =
71 | Func(
72 | fun (args : obj[]) ->
73 | try dele.DynamicInvoke(args)
74 | with :? TargetInvocationException as ex ->
75 | match ex.InnerException with
76 | | :? MemberAccessException as innerEx ->
77 | let msg =
78 | "Attempting to access non public member or type from dynamic assembly. Consider making your type/member public or use the appropriate Run method.\n" +
79 | match iaccs with
80 | | None -> String.Empty
81 | | Some iaccs ->
82 | "Possible invalid accesses :\n" +
83 | (iaccs
84 | |> Seq.map (fun (expr, msg) ->
85 | let msg = match msg with None -> String.Empty | Some msg -> msg
86 | sprintf "At expression : %A, %s" expr msg)
87 | |> String.concat "\n")
88 | raise <| Exception(msg, innerEx)
89 | | e when e <> null -> raise e
90 | | _ -> reraise())
91 |
92 | static member Compile<'T>(queryExpr : QueryExpr, optimize : Func) : Func<'T> =
93 | CoreHelpers.Compile<'T>(queryExpr, optimize, false)
94 |
95 | static member Compile(queryExpr : QueryExpr, optimize : Func) : Action =
96 | CoreHelpers.Compile(queryExpr, optimize, false)
97 |
98 | static member CompileToParallel<'T>(queryExpr : QueryExpr, optimize : Func) : Func<'T> =
99 | CoreHelpers.CompileToParallel<'T>(queryExpr, optimize, false)
100 |
101 |
102 |
103 |
104 | static member Compile<'T>(queryExpr : QueryExpr, optimize : Func, allowNonPublicMemberAccess : bool) : Func<'T> =
105 | if allowNonPublicMemberAccess then
106 | CoreHelpers.Compile(queryExpr, fun expr -> Compiler.compileToSequential expr optimize.Invoke)
107 | else
108 | CoreHelpers.CompileToMethod(queryExpr, fun expr -> Compiler.compileToSequential expr optimize.Invoke )
109 |
110 | static member Compile(queryExpr : QueryExpr, optimize : Func, allowNonPublicMemberAccess : bool) : Action =
111 | if allowNonPublicMemberAccess then
112 | let func = CoreHelpers.Compile(queryExpr, fun expr -> Compiler.compileToSequential expr optimize.Invoke)
113 | Action(fun () -> func.Invoke())
114 | else
115 | let func = CoreHelpers.CompileToMethod(queryExpr, fun expr -> Compiler.compileToSequential expr optimize.Invoke )
116 | Action(fun () -> func.Invoke())
117 |
118 | static member CompileToParallel<'T>(queryExpr : QueryExpr, optimize : Func, allowNonPublicMemberAccess : bool ) : Func<'T> =
119 | if allowNonPublicMemberAccess then
120 | CoreHelpers.Compile(queryExpr, fun expr -> Compiler.compileToParallel expr optimize.Invoke)
121 | else
122 | CoreHelpers.CompileToMethod(queryExpr, fun expr -> Compiler.compileToParallel expr optimize.Invoke )
123 |
124 |
125 | static member CompileTemplateVariadic<'R>(parameters : ParameterExpression [], template : QueryExpr, optimize : Func, allowNonPublicMemberAccess : bool) =
126 | let func =
127 | if allowNonPublicMemberAccess then
128 | CoreHelpers.Compile(template,
129 | fun query ->
130 | let expr = Compiler.compileToSequential query optimize.Invoke
131 | let lam = lambda parameters expr
132 | lam :> Expression)
133 | else
134 | CoreHelpers.CompileToMethod(template,
135 | fun query ->
136 | let expr = Compiler.compileToSequential query optimize.Invoke
137 | let lam = lambda parameters expr
138 | lam :> _)
139 | let template = func.Invoke()
140 | template
141 |
142 | static member CompileActionTemplateVariadic(parameters : ParameterExpression [], template : QueryExpr, optimize : Func, allowNonPublicMemberAccess : bool) : obj =
143 | let func =
144 | if allowNonPublicMemberAccess then
145 | CoreHelpers.Compile(template,
146 | fun query ->
147 | let expr = Compiler.compileToSequential query optimize.Invoke
148 | let lam = lambda parameters expr
149 | lam :> Expression)
150 | else
151 | CoreHelpers.CompileToMethod(template,
152 | fun query ->
153 | let expr = Compiler.compileToSequential query optimize.Invoke
154 | let lam = lambda parameters expr
155 | lam :> _)
156 | let template = func.Invoke()
157 | template
158 |
159 | static member CompileTemplateToParallelVariadic<'R>(parameters : ParameterExpression [], template : QueryExpr, optimize : Func, allowNonPublicMemberAccess : bool) =
160 | let func =
161 | if allowNonPublicMemberAccess then
162 | CoreHelpers.Compile(template,
163 | fun query ->
164 | let expr = Compiler.compileToParallel query optimize.Invoke
165 | let lam = lambda parameters expr
166 | lam :> Expression)
167 | else
168 | CoreHelpers.CompileToMethod(template,
169 | fun query ->
170 | let expr = Compiler.compileToParallel query optimize.Invoke
171 | let lam = lambda parameters expr
172 | lam :> _)
173 | let template = func.Invoke()
174 | template
--------------------------------------------------------------------------------
/src/LinqOptimizer.Core/FreeVariablesVisitor.fs:
--------------------------------------------------------------------------------
1 | namespace Nessos.LinqOptimizer.Core
2 |
3 | open System
4 | open System.Collections
5 | open System.Collections.Generic
6 | open System.Linq
7 | open System.Linq.Expressions
8 | open System.Reflection
9 | open System.Collections.Concurrent
10 |
11 | /// Returns the free variables out of an expression.
12 | type private FreeVariablesVisitor () =
13 | inherit ExpressionVisitor() with
14 |
15 | let freeVars = HashSet()
16 | let localVars = HashSet()
17 |
18 | let addLocals(vars : seq) =
19 | vars |> Seq.iter (fun p -> localVars.Add(p) |> ignore)
20 |
21 | member this.Environment with get () = freeVars :> seq<_>
22 |
23 | override this.VisitParameter(expr : ParameterExpression) =
24 | if not <| localVars.Contains(expr) then
25 | freeVars.Add(expr) |> ignore
26 | expr :> _
27 |
28 | override this.VisitLambda(expr : Expression<'T>) =
29 | addLocals expr.Parameters
30 | expr.Update(this.Visit(expr.Body), expr.Parameters) :> _
31 |
32 | override this.VisitBlock(expr : BlockExpression) =
33 | addLocals expr.Variables
34 | expr.Update(expr.Variables, this.Visit(expr.Expressions)) :> _
35 |
36 | override this.VisitMember(expr : MemberExpression) =
37 | // TransparentIdentifier's free variable
38 | if expr.Member.MemberType = MemberTypes.Property then
39 | let pi = expr.Member :?> PropertyInfo
40 | if isTransparentIdentifier expr.Expression then
41 | let p = Expression.Parameter(expr.Type, pi.Name)
42 | freeVars.Add(p) |> ignore
43 | p :> _
44 | else
45 | expr :> _
46 | else
47 | expr :> _
48 |
49 | // override this.VisitMember(expr : MemberExpression) =
50 | // if expr.Expression :? ConstantExpression then
51 | // let obj = (expr.Expression :?> ConstantExpression).Value
52 | //
53 | // let (value, p) =
54 | // match expr.Member.MemberType with
55 | // | MemberTypes.Field ->
56 | // let fi = expr.Member :?> FieldInfo
57 | // fi.GetValue(obj), Expression.Parameter(expr.Type, sprintf "%s" fi.Name)
58 | // | MemberTypes.Property ->
59 | // let pi = expr.Member :?> PropertyInfo
60 | // let indexed = pi.GetIndexParameters() |> Seq.cast |> Seq.toArray
61 | // pi.GetValue(obj, indexed), Expression.Parameter(expr.Type, sprintf "%s" pi.Name)
62 | // | _ ->
63 | // failwithf "Internal error : Accessing non Field or Property from MemberExpression %A" expr
64 | // freeVars.Add(p) |> ignore
65 | // p :> _
66 | // else
67 | // expr.Update(this.Visit expr.Expression) :> _
68 |
69 |
70 | []
71 | module FreeVariablesVisitor =
72 | let get(expr : Expression) =
73 | let fvv = new FreeVariablesVisitor()
74 | let expr = fvv.Visit(expr)
75 | fvv.Environment
76 |
77 | let getWithExpr(expr : Expression) =
78 | let fvv = new FreeVariablesVisitor()
79 | let expr = fvv.Visit(expr)
80 | expr, fvv.Environment
--------------------------------------------------------------------------------
/src/LinqOptimizer.Core/GroupingHelpers.fs:
--------------------------------------------------------------------------------
1 | namespace Nessos.LinqOptimizer.Core
2 |
3 | open System
4 | open System.Collections
5 | open System.Collections.Generic
6 | open System.Linq
7 | open System.Linq.Expressions
8 | open System.Reflection
9 | open System.Threading;
10 | open System.Collections.Concurrent
11 |
12 | type Grouping<'Key, 'Value> =
13 | new(key, count, values) = { key = key; count = count; index = -1; position = -1; values = values }
14 | val private key : 'Key
15 | val private values : 'Value[]
16 | val mutable private count : int
17 | val mutable private index : int
18 | val mutable private position : int
19 |
20 |
21 | member self.IncrCount() = self.count <- self.count + 1
22 | member self.InterlockedIncrCount() = Interlocked.Increment(&self.count)
23 | member self.Count = self.count
24 | member self.Position = self.position
25 | member self.Index with get () = self.index
26 | and set (index) = self.index <- index
27 | member self.IncrPosition() = self.position <- self.position + 1
28 | member self.InterlockedIncrPosition() = Interlocked.Increment(&self.position)
29 |
30 |
31 |
32 | interface IGrouping<'Key, 'Value> with
33 | member self.Key = self.key
34 |
35 | member self.GetEnumerator() : IEnumerator<'Value> =
36 | let positionRef = ref self.index
37 | { new IEnumerator<'Value> with
38 | member __.Current = self.values.[!positionRef - 1]
39 | interface System.Collections.IEnumerator with
40 | member __.Current = self.values.[!positionRef] :> _
41 | member __.MoveNext() =
42 | if !positionRef >= self.index + self.count then
43 | false
44 | else
45 | incr positionRef
46 | true
47 | member __.Reset() = positionRef := self.index
48 | interface IDisposable with
49 | member __.Dispose() = () }
50 |
51 | member self.GetEnumerator() : IEnumerator =
52 | let enumerator : IEnumerator<'Value> = (self :> IGrouping<'Key, 'Value>).GetEnumerator()
53 | enumerator :> _
54 |
55 | type Grouping =
56 |
57 | static member GroupBy(keys : 'Key[], values : 'Value[]) : IEnumerable> =
58 | let values' : 'Value[] = Array.zeroCreate values.Length
59 | let dict = new Dictionary<'Key, Grouping<'Key, 'Value>>()
60 | let mutable grouping = Unchecked.defaultof>
61 | // grouping count
62 | for i = 0 to values.Length - 1 do
63 | let key = keys.[i]
64 | if not <| dict.TryGetValue(key, &grouping) then
65 | grouping <- new Grouping<'Key, 'Value>(key, 1, values')
66 | dict.Add(key, grouping)
67 | else
68 | grouping.IncrCount()
69 | // rearrange
70 | let mutable currentIndex = 0
71 | for i = 0 to values.Length - 1 do
72 | let key = keys.[i]
73 | let value = values.[i]
74 | dict.TryGetValue(key, &grouping) |> ignore
75 | if grouping.Index = -1 then
76 | grouping.Index <- currentIndex
77 | currentIndex <- grouping.Index + grouping.Count
78 |
79 | grouping.IncrPosition()
80 | values'.[grouping.Index + grouping.Position] <- value
81 | // collect results
82 | dict.Values |> Seq.map (fun value -> value :> _)
83 |
84 | static member ParallelGroupBy(keys : 'Key[], values : 'Value[]) : IEnumerable> =
85 | let values' : 'Value[] = Array.zeroCreate values.Length
86 | let dict = new ConcurrentDictionary<'Key, Grouping<'Key, 'Value>>()
87 | // grouping count
88 | ParallelismHelpers.ReduceCombine(keys, (fun () -> dict),
89 | (fun (dict : ConcurrentDictionary<'Key, Grouping<'Key, 'Value>>) key ->
90 | let mutable grouping = Unchecked.defaultof>
91 | if not <| dict.TryGetValue(key, &grouping) then
92 | grouping <- new Grouping<'Key, 'Value>(key, 1, values')
93 | if not <| dict.TryAdd(key, grouping) then
94 | dict.TryGetValue(key, &grouping) |> ignore
95 | grouping.InterlockedIncrCount() |> ignore
96 | else
97 | grouping.InterlockedIncrCount() |> ignore
98 | dict
99 | ), (fun dict _ -> dict), (fun x -> x)) |> ignore
100 | // fix grouping index
101 | let mutable currentIndex = 0
102 | for grouping in dict.Values do
103 | grouping.Index <- currentIndex
104 | currentIndex <- currentIndex + grouping.Count
105 | // rearrange
106 | ParallelismHelpers.ReduceCombine(keys.Length, (fun () -> dict),
107 | (fun (dict : ConcurrentDictionary<'Key, Grouping<'Key, 'Value>>) index ->
108 | let mutable grouping = Unchecked.defaultof>
109 | dict.TryGetValue(keys.[index], &grouping) |> ignore
110 |
111 | let position = grouping.InterlockedIncrPosition()
112 | values'.[grouping.Index + position] <- values.[index]
113 | dict
114 | ), (fun dict _ -> dict), (fun x -> x)) |> ignore
115 | // collect results
116 | dict.Values |> Seq.map (fun value -> value :> _)
117 |
118 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.Core/LinqOptimizer.Core.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0;net45
5 | AnyCPU
6 | false
7 | true
8 | true
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.Core/ParallelismHelpers.fs:
--------------------------------------------------------------------------------
1 | namespace Nessos.LinqOptimizer.Core
2 | open System
3 | open System.Collections.Generic
4 | open System.Linq
5 | open System.Linq.Expressions
6 | open System.Reflection
7 | open System.Collections.Concurrent
8 | open System.Threading.Tasks
9 |
10 | type ParallelismHelpers =
11 | static member TotalWorkers = int (2.0 ** float (int (Math.Log(float Environment.ProcessorCount, 2.0))))
12 |
13 | static member GetPartitions (s : int, e : int) =
14 | let toSeq (enum : IEnumerator<_>)=
15 | seq {
16 | while enum.MoveNext() do
17 | yield enum.Current
18 | }
19 | let partitioner = Partitioner.Create(s, e)
20 | let partitions = partitioner.GetPartitions(ParallelismHelpers.TotalWorkers) |> Seq.collect toSeq |> Seq.toArray
21 | partitions
22 |
23 | static member ReduceCombine<'T, 'Acc, 'R>( array : 'T[],
24 | init : Func<'Acc>,
25 | reducer : Func<'T[], int, int, 'Acc, 'Acc>,
26 | combiner : Func<'Acc, 'Acc, 'Acc>,
27 | selector : Func<'Acc, 'R>) : 'R =
28 | if array.Length = 0 then
29 | selector.Invoke(init.Invoke())
30 | else
31 | let partitions = ParallelismHelpers.GetPartitions(0, array.Length)
32 | let cells = partitions |> Array.map (fun _ -> ref Unchecked.defaultof<'Acc>)
33 | let tasks = partitions |> Array.mapi (fun index (s, e) ->
34 | Task.Factory.StartNew(fun () ->
35 | let result = reducer.Invoke(array, s - 1, e - 1, init.Invoke())
36 | cells.[index] := result
37 | ()))
38 | Task.WaitAll(tasks)
39 | let result = cells |> Array.fold (fun acc cell -> combiner.Invoke(acc, cell.Value)) (init.Invoke())
40 | selector.Invoke(result)
41 |
42 | static member ReduceCombine<'T, 'Acc, 'R>(values : IList<'T>,
43 | init : Func<'Acc>,
44 | reducer : Func<'Acc, 'T, 'Acc>,
45 | combiner : Func<'Acc, 'Acc, 'Acc>,
46 | selector : Func<'Acc, 'R>) : 'R =
47 | ParallelismHelpers.ReduceCombine(values.Count, init, (fun acc index -> reducer.Invoke(acc, values.[index])), combiner, selector)
48 |
49 |
50 | static member ReduceCombine<'T, 'Acc, 'R>(length : int,
51 | init : Func<'Acc>,
52 | reducer : Func<'Acc, int, 'Acc>,
53 | combiner : Func<'Acc, 'Acc, 'Acc>,
54 | selector : Func<'Acc, 'R>) : 'R =
55 | if length = 0 then
56 | selector.Invoke(init.Invoke())
57 | else
58 | let partitions = ParallelismHelpers.GetPartitions(0, length)
59 | let cells = partitions |> Array.map (fun _ -> ref Unchecked.defaultof<'Acc>)
60 | let tasks = partitions |> Array.mapi (fun index (s, e) ->
61 | Task.Factory.StartNew(fun () ->
62 | let result =
63 | let mutable r = init.Invoke()
64 | for i = s to e - 1 do
65 | r <- reducer.Invoke(r, i)
66 | r
67 | cells.[index] := result
68 | ()))
69 | Task.WaitAll(tasks)
70 | let result = cells |> Array.fold (fun acc cell -> combiner.Invoke(acc, cell.Value)) (init.Invoke())
71 | selector.Invoke(result)
72 |
73 |
74 |
75 | static member ReduceCombine<'T, 'Acc, 'R>( partitioner : Partitioner<'T>,
76 | init : Func<'Acc>,
77 | reducer : Func<'Acc, 'T, 'Acc>,
78 | combiner : Func<'Acc, 'Acc, 'Acc>,
79 | selector : Func<'Acc, 'R>) : 'R =
80 | let partitions = partitioner.GetPartitions(ParallelismHelpers.TotalWorkers).ToArray()
81 | let cells = partitions |> Array.map (fun _ -> ref Unchecked.defaultof<'Acc>)
82 | let tasks = partitions |> Array.mapi (fun index partition ->
83 | Task.Factory.StartNew(fun () ->
84 | let result =
85 | let mutable r = init.Invoke()
86 | while partition.MoveNext() do
87 | r <- reducer.Invoke(r, partition.Current)
88 | r
89 | cells.[index] := result
90 | ()))
91 | Task.WaitAll(tasks)
92 | let result = cells |> Array.fold (fun acc cell -> combiner.Invoke(acc, cell.Value)) (init.Invoke())
93 | selector.Invoke(result)
94 |
95 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.Core/QueryExpr.fs:
--------------------------------------------------------------------------------
1 | namespace Nessos.LinqOptimizer.Core
2 |
3 | open System
4 | open System.Collections
5 | open System.Collections.Generic
6 | open System.Linq
7 | open System.Linq.Expressions
8 | open System.Reflection
9 | open System.Text
10 | open Nessos.LinqOptimizer.Core.Utils
11 |
12 | // // Typed Wrapper for QueryExpr
13 | // type QueryExpr<'T>(queryExpr : QueryExpr) =
14 | // member self.QueryExpr = queryExpr
15 | // type ParallelQueryExpr<'T>(queryExpr : QueryExpr) =
16 | // member self.QueryExpr = queryExpr
17 | // C# friendly QueryExpr
18 | // and QueryExprVoid(queryExpr : QueryExpr) =
19 | // member self.QueryExpr = queryExpr
20 | // and ParallelQueryExprVoid(queryExpr : QueryExpr) =
21 | // member self.QueryExpr = queryExpr
22 | // Main Query representation
23 | type QueryExprType = Sequential | Parallel | Gpu
24 | type Order = Ascending | Descending
25 | type ReductionType = Map | Filter | Count | Sum | Aggregate | ToList | ToArray | Iter | NestedQueryTransform
26 | /// The type representing an query expression.
27 | and QueryExpr =
28 | | Source of Expression * Type * QueryExprType
29 | | Generate of Expression * LambdaExpression * LambdaExpression * LambdaExpression
30 | | Transform of LambdaExpression * QueryExpr
31 | | TransformIndexed of LambdaExpression * QueryExpr
32 | | Filter of LambdaExpression * QueryExpr
33 | | FilterIndexed of LambdaExpression * QueryExpr
34 | | NestedQuery of (ParameterExpression * QueryExpr) * QueryExpr
35 | | NestedQueryTransform of (ParameterExpression * QueryExpr) * LambdaExpression * QueryExpr
36 | | Aggregate of Expression * LambdaExpression * QueryExpr
37 | | Sum of QueryExpr
38 | | Count of QueryExpr
39 | | Take of Expression * QueryExpr
40 | | TakeWhile of LambdaExpression * QueryExpr
41 | | SkipWhile of LambdaExpression * QueryExpr
42 | | Skip of Expression * QueryExpr
43 | | ForEach of LambdaExpression * QueryExpr
44 | | GroupBy of LambdaExpression * QueryExpr * Type
45 | | OrderBy of (LambdaExpression * Order) list * QueryExpr
46 | | ToList of QueryExpr
47 | | ToArray of QueryExpr
48 | | RangeGenerator of Expression * Expression
49 | | RepeatGenerator of Expression * Expression
50 | | ZipWith of Expression * Expression * LambdaExpression
51 | with
52 |
53 | member self.Type =
54 | match self with
55 | | Source (_, t, _) -> t
56 | | Generate(_,_,_, selector) -> selector.Body.Type
57 | | Transform (lambda, _) -> lambda.Body.Type
58 | | TransformIndexed (lambda, _) -> lambda.Body.Type
59 | | Filter (_, q) -> q.Type
60 | | FilterIndexed (_, q) -> q.Type
61 | | NestedQuery ((_, q), _) -> q.Type
62 | | NestedQueryTransform ((_, _), resultSelector, _) -> resultSelector.Body.Type
63 | | Aggregate (seed, _, _) -> seed.Type
64 | | Sum (q) -> q.Type
65 | | Count (q) -> q.Type
66 | | Take (_, q) -> q.Type
67 | | TakeWhile(_,q) -> q.Type
68 | | SkipWhile(_,q) -> q.Type
69 | | Skip (_, q) -> q.Type
70 | | ForEach (_, _) -> typeof
71 | | GroupBy (_, _, t) -> t
72 | | OrderBy (_, q) -> q.Type
73 | | ToList q -> q.Type
74 | | ToArray q -> q.Type
75 | | RangeGenerator _ -> typeof
76 | | RepeatGenerator (objExpr,_) -> objExpr.Type
77 | | ZipWith (_,_,f) -> f.ReturnType
78 |
79 | override self.ToString() =
80 | let str (expr : Expression ) = expr.ToString()
81 | let rec toString (query : QueryExpr) : string =
82 | match query with
83 | | Source (expr, t, queryExprType) ->
84 | match queryExprType with
85 | | Sequential -> sprintf' "Source (%s, %s, Sequential)" <| str expr <| t.ToString()
86 | | Parallel -> sprintf' "Source (%s, %s, Parallel)" <| str expr <| t.ToString()
87 | | Gpu -> sprintf' "Source (%s, %s, Gpu)" <| str expr <| t.ToString()
88 | | Generate(expr1, expr2, expr3, expr4) ->
89 | sprintf' "Generate (%s, %s, %s, %s)" <| str expr1 <| str expr2 <| str expr3 <| str expr4
90 | | Transform (lambda, query) ->
91 | sprintf' "Transform [%s](%s, %s)" <| lambda.Body.Type.ToString() <| str lambda <| toString query
92 | | TransformIndexed (lambda, query) ->
93 | sprintf' "TransformIndexed [%s](%s, %s)" <| lambda.Body.Type.ToString() <| str lambda <| toString query
94 | | Filter (lambda, query) ->
95 | sprintf' "Filter (%s, %s)" <| str lambda <| toString query
96 | | FilterIndexed (lambda, query) ->
97 | sprintf' "FilterIndexed (%s, %s)" <| str lambda <| toString query
98 | | NestedQuery ((param, query), query') ->
99 | sprintf' "NestedQuery [%s](%s, %s, %s)" <| query.Type.ToString() <| str param <| toString query <| toString query'
100 | | NestedQueryTransform ((param, query), resultSelector, query') ->
101 | sprintf' "NestedQueryTransform [%s](%s, %s, %s, %s)" <| query.Type.ToString() <| str param <| toString query <| str resultSelector <| toString query'
102 | | Aggregate (seed, acc, query) ->
103 | sprintf' "Aggregate [%s](%s, %s, %s)" <| acc.Body.Type.ToString() <| str seed <| str acc <| toString query
104 | | Sum (query) ->
105 | sprintf' "Sum (%s, %s)" <| toString query <| query.Type.ToString()
106 | | Count (query) ->
107 | sprintf' "Count (%s)" <| toString query
108 | | Take (takeCount, query) ->
109 | sprintf' "Take (%s, %s)" <| str takeCount <| toString query
110 | | TakeWhile(lambda, query) ->
111 | sprintf' "TakeWhile (%s, %s)" <| str lambda <| toString query
112 | | SkipWhile(lambda, query) ->
113 | sprintf' "SkipWhile (%s, %s)" <| str lambda <| toString query
114 | | Skip (skipCount, query) ->
115 | sprintf' "Skip (%s, %s)" <| str skipCount <| toString query
116 | | ForEach (lambda, query) ->
117 | sprintf' "ForEach (%s, %s)" <| str lambda <| toString query
118 | | GroupBy (lambda, query, _) ->
119 | sprintf' "GroupBy (%s, %s)" <| str lambda <| toString query
120 | | OrderBy (lambdaOrderPairs, query) ->
121 | List.foldBack
122 | (fun (lambda, order) s ->
123 | match order with
124 | | Ascending -> sprintf' "OrderBy (%s, Asc, %s)" <| str lambda <| s
125 | | Descending -> sprintf' "OrderBy (%s, Desc, %s)" <| str lambda <| s)
126 | lambdaOrderPairs (toString query)
127 | | ToList query ->
128 | sprintf' "ToList [%s](%s)" <| query.Type.ToString() <| toString query
129 | | ToArray query ->
130 | sprintf' "ToArray [%s](%s)" <| query.Type.ToString() <| toString query
131 | | RangeGenerator (expr1, expr2) ->
132 | sprintf' "RangeGenerator (%s, %s)" <| str expr1 <| str expr2
133 | | RepeatGenerator (expr1, expr2) ->
134 | sprintf' "RepeatGenerator (%s, %s)" <| str expr1 <| str expr2
135 | | ZipWith(l,r,f) ->
136 | sprintf' "ZipWith (%s, %s, %s)" <| str l <| str r <| str f
137 |
138 | toString self
139 |
140 | static member AddOrderBy(keySelector : LambdaExpression, order : Order, queryExpr : QueryExpr) =
141 | match queryExpr with
142 | | OrderBy (keySelectorOrderPairs, queryExpr') ->
143 | OrderBy ((keySelector, order) :: keySelectorOrderPairs, queryExpr')
144 | | _ -> OrderBy ([keySelector, order], queryExpr)
145 |
146 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.Core/ReflectionHelpers.fs:
--------------------------------------------------------------------------------
1 | namespace Nessos.LinqOptimizer.Core
2 | open System
3 | open System.Linq.Expressions
4 | open System.Reflection
5 |
6 | []
7 | module ReflectionHelpers =
8 |
9 | let (|MethodName|_|) (methodName : string) (methodInfo : MethodInfo) =
10 | if methodInfo.Name = methodName then
11 | Some <| methodInfo.GetParameters()
12 | else None
13 |
14 |
15 | let (|ParameterName|_|) (parameterName : string) (parameterInfo : ParameterInfo) =
16 | if parameterInfo.Name = parameterName then
17 | Some parameterInfo
18 | else None
19 |
20 | let (|TypeCheck|_|) (paramTypeDef : Type) (typeDef : Type) =
21 | if paramTypeDef = typeDef then
22 | Some typeDef
23 | else None
24 |
25 | let (|ParamType|) (paramInfo : ParameterInfo) = paramInfo.ParameterType
26 |
27 | let (|Named|Array|Ptr|Param|) (t : System.Type) =
28 | if t.IsGenericType
29 | then Named(t.GetGenericTypeDefinition(), t.GetGenericArguments())
30 | elif t.IsGenericParameter
31 | then Param(t.GenericParameterPosition)
32 | elif not t.HasElementType
33 | then Named(t, [| |])
34 | elif t.IsArray
35 | then Array(t.GetElementType(), t.GetArrayRank())
36 | elif t.IsByRef
37 | then Ptr(true, t.GetElementType())
38 | elif t.IsPointer
39 | then Ptr(false, t.GetElementType())
40 | else failwith "MSDN says this can’t happen"
41 |
42 | let getIEnumerableType (ty : Type) =
43 | ty.GetInterface("IEnumerable`1").GetGenericArguments().[0]
44 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.Core/Session.fs:
--------------------------------------------------------------------------------
1 | namespace Nessos.LinqOptimizer.Core
2 |
3 | open System
4 | open System.Collections
5 | open System.Collections.Generic
6 | open System.Linq
7 | open System.Linq.Expressions
8 | open System.Reflection
9 | open System.Reflection.Emit
10 |
11 | type internal Session private () =
12 | static member private GetNewMethodName () = "LinqOptMethod"
13 | static member private GetNewTypeName () = "LinqOptTy_" + Guid.NewGuid().ToString("N")
14 | static member private GetNewAssemblyName () = "LinqOptAsm_" + Guid.NewGuid().ToString("N")
15 | static member private ModuleName = "Module"
16 |
17 |
18 | static member private ModuleBuilder
19 | with get () =
20 | lazy ( let asmBuilder = AssemblyBuilder.DefineDynamicAssembly(AssemblyName(Session.GetNewAssemblyName()), AssemblyBuilderAccess.Run)
21 | let moduleBuilder = asmBuilder.DefineDynamicModule(Session.ModuleName)
22 | moduleBuilder )
23 |
24 | static member internal Compile(expr : LambdaExpression) =
25 | let methodName = Session.GetNewMethodName()
26 | let typeBuilder = Session.ModuleBuilder.Value.DefineType(Session.GetNewTypeName(), TypeAttributes.Public)
27 | let methodBuilder = typeBuilder.DefineMethod(methodName, MethodAttributes.Public ||| MethodAttributes.Static)
28 | expr.Compile()
--------------------------------------------------------------------------------
/src/LinqOptimizer.Core/SortingHelpers.fs:
--------------------------------------------------------------------------------
1 | namespace Nessos.LinqOptimizer.Core
2 |
3 | open System
4 | open System.Collections
5 | open System.Collections.Generic
6 | open System.Linq
7 | open System.Threading.Tasks
8 | open System.Threading
9 |
10 |
11 | type private MergeArrayType =
12 | | FromArrayType
13 | | ToArrayType
14 |
15 |
16 |
17 | type Sort =
18 |
19 | static member SequentialSort(keys : 'Key[], values : 'Value[], orders : Order[]) =
20 | Array.Sort(keys, values)
21 | if orders.Length = 1 then
22 | match orders.[0] with
23 | | Descending ->
24 | Array.Reverse(values)
25 | | _ -> ()
26 |
27 |
28 |
29 | static member ParallelSort<'Key, 'Value when 'Key :> IComparable<'Key>>(keys : 'Key[], array : 'Value[], orders : Order[]) =
30 | // Taken from Carl Nolan's parallel inplace merge
31 | // The merge of the two array
32 | let merge (toArray: 'Value []) (toKeys : 'Key[]) (fromArray: 'Value []) (fromKeys : 'Key[]) (low1: int) (low2: int) (high1: int) (high2: int) =
33 | let mutable ptr1 = low1
34 | let mutable ptr2 = high1
35 |
36 | for ptr = low1 to high2 do
37 | if (ptr1 > low2) then
38 | toArray.[ptr] <- fromArray.[ptr2]
39 | toKeys.[ptr] <- fromKeys.[ptr2]
40 | ptr2 <- ptr2 + 1
41 | elif (ptr2 > high2) then
42 | toArray.[ptr] <- fromArray.[ptr1]
43 | toKeys.[ptr] <- fromKeys.[ptr1]
44 | ptr1 <- ptr1 + 1
45 | elif (fromKeys.[ptr1].CompareTo(fromKeys.[ptr2]) <= 0) then
46 | toArray.[ptr] <- fromArray.[ptr1]
47 | toKeys.[ptr] <- fromKeys.[ptr1]
48 | ptr1 <- ptr1 + 1
49 | else
50 | toArray.[ptr] <- fromArray.[ptr2]
51 | toKeys.[ptr] <- fromKeys.[ptr2]
52 | ptr2 <- ptr2 + 1
53 |
54 | // define the sort operation
55 | let parallelSort () =
56 |
57 | // control flow parameters
58 | let totalWorkers = int (2.0 ** float (int (Math.Log(float Environment.ProcessorCount, 2.0))))
59 | let auxArray : 'Value array = Array.zeroCreate array.Length
60 | let auxKeys : 'Key array = Array.zeroCreate array.Length
61 | let workers : Task array = Array.zeroCreate (totalWorkers - 1)
62 | let iterations = int (Math.Log((float totalWorkers), 2.0))
63 |
64 |
65 |
66 | // Number of elements for each array, if the elements number is not divisible by the workers
67 | // the remainders will be added to the first worker (the main thread)
68 | let partitionSize = ref (int (array.Length / totalWorkers))
69 | let remainder = array.Length % totalWorkers
70 |
71 | // Define the arrays references for processing as they are swapped during each iteration
72 | let swapped = ref false
73 |
74 | let inline getMergeArray (arrayType: MergeArrayType) =
75 | match (arrayType, !swapped) with
76 | | (FromArrayType, true) -> (auxArray, auxKeys)
77 | | (FromArrayType, false) -> (array, keys)
78 | | (ToArrayType, true) -> (array, keys)
79 | | (ToArrayType, false) -> (auxArray, auxKeys)
80 |
81 | use barrier = new Barrier(totalWorkers, fun (b) ->
82 | partitionSize := !partitionSize <<< 1
83 | swapped := not !swapped)
84 |
85 | // action to perform the sort an merge steps
86 | let action (index: int) =
87 |
88 | //calculate the partition boundary
89 | let low = index * !partitionSize + match index with | 0 -> 0 | _ -> remainder
90 | let high = (index + 1) * !partitionSize - 1 + remainder
91 |
92 | // Sort the specified range - could implement QuickSort here
93 | let sortLen = high - low + 1
94 | Array.Sort(keys, array, low, sortLen)
95 |
96 | barrier.SignalAndWait()
97 |
98 | let rec loopArray loopIdx actionIdx loopHigh =
99 | if loopIdx < iterations then
100 | if (actionIdx % 2 = 1) then
101 | barrier.RemoveParticipant()
102 | else
103 | let newHigh = loopHigh + !partitionSize / 2
104 | let toArray, toKeys = getMergeArray FromArrayType
105 | let fromArray, fromKeys = getMergeArray ToArrayType
106 | merge toArray toKeys fromArray fromKeys low loopHigh (loopHigh + 1) newHigh
107 | barrier.SignalAndWait()
108 | loopArray (loopIdx + 1) (actionIdx >>> 1) newHigh
109 | loopArray 0 index high
110 |
111 | for index in 1 .. workers.Length do
112 | workers.[index - 1] <- Task.Factory.StartNew(fun() -> action index)
113 |
114 | action 0
115 |
116 | // if odd iterations return auxArray otherwise array (swapped will be false)
117 | if not (iterations % 2 = 0) then
118 | Array.blit auxArray 0 array 0 array.Length
119 |
120 | parallelSort()
121 | if orders.Length = 1 then
122 | match orders.[0] with
123 | | Descending ->
124 | Array.Reverse(array)
125 | | _ -> ()
126 |
127 | // Composite keys for sorting, used only for code generation
128 | []
129 | []
130 | []
131 | type Keys<'T1, 'T2 when 'T1 :> IComparable<'T1> and 'T2 :> IComparable<'T2>>
132 | (t1 : 'T1, t2 : 'T2, o1 : Order, o2 : Order) =
133 | member self.T1 = t1
134 | member self.T2 = t2
135 |
136 | override self.Equals(_) =
137 | raise <| new NotImplementedException()
138 | override self.GetHashCode() =
139 | raise <| new NotImplementedException()
140 |
141 | interface IComparable> with
142 |
143 | member self.CompareTo(keys : Keys<'T1, 'T2>) =
144 | let cmpt1 = t1.CompareTo(keys.T1)
145 | if cmpt1 = 0 then
146 | if o2 = Order.Ascending then
147 | t2.CompareTo(keys.T2)
148 | else
149 | -t2.CompareTo(keys.T2)
150 | else
151 | if o1 = Order.Ascending then
152 | cmpt1
153 | else
154 | -cmpt1
155 |
156 | interface IComparable with
157 | member self.CompareTo(_) =
158 | raise <| new NotImplementedException()
159 |
160 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.Core/TypeCollectorVisitor.fs:
--------------------------------------------------------------------------------
1 | namespace Nessos.LinqOptimizer.Core
2 |
3 | open System
4 | open System.Collections
5 | open System.Collections.Generic
6 | open System.Linq
7 | open System.Linq.Expressions
8 | open System.Reflection
9 | open System.Collections.Concurrent
10 |
11 | type private TypeCollectorVisitor () =
12 | inherit ExpressionVisitor() with
13 |
14 | let types = List()
15 |
16 | member this.Types with get () = types
17 |
18 | override this.VisitNew(expr : NewExpression) =
19 | types.Add(expr.Type)
20 | expr.Update(this.Visit expr.Arguments) :> _
21 |
22 | override this.VisitParameter(expr : ParameterExpression) =
23 | types.Add(expr.Type)
24 | expr :> _
25 |
26 | override this.VisitMember(expr : MemberExpression) =
27 | match expr.Member with
28 | | :? PropertyInfo as pi ->
29 | if pi.GetGetMethod() = null then types.Add(expr.Member.DeclaringType)
30 | | :? FieldInfo as fi ->
31 | types.Add(expr.Member.DeclaringType)
32 | | _ -> ()
33 |
34 | expr.Update(this.Visit expr.Expression) :> _
35 |
36 | override this.VisitMethodCall(expr : MethodCallExpression) =
37 | types.Add(expr.Method.DeclaringType)
38 | expr.Update(this.Visit expr.Object, this.Visit expr.Arguments) :> _
39 |
40 | // cast
41 | override this.VisitUnary(expr : UnaryExpression) =
42 | types.Add(expr.Type)
43 | expr.Update(this.Visit expr.Operand) :> _
44 |
45 | module TypeCollector =
46 | let getTypes(exprs : seq) : seq =
47 | exprs
48 | |> Seq.collect (fun expr ->
49 | let tg = new TypeCollectorVisitor()
50 | tg.Visit(expr) |> ignore
51 | tg.Types)
52 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.Core/Utils.fs:
--------------------------------------------------------------------------------
1 | namespace Nessos.LinqOptimizer.Core
2 | open System
3 | open System.Collections.Concurrent
4 |
5 | // anton tayanovskyy's fast sprintf'
6 | type Cache<'T> private () =
7 | static let d = ConcurrentDictionary()
8 |
9 | static member Format(format: Printf.StringFormat<'T>) : 'T =
10 | let key = format.Value
11 | match d.TryGetValue(key) with
12 | | true, r -> r
13 | | _ ->
14 | let r = sprintf format
15 | d.TryAdd(key, r) |> ignore
16 | r
17 |
18 | module Utils =
19 |
20 |
21 | let sprintf' fmt =
22 | Cache<_>.Format(fmt)
23 |
24 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.FSharp/AssemblyInfo.fs:
--------------------------------------------------------------------------------
1 | // Auto-Generated by FAKE; do not edit
2 | namespace System
3 | open System.Reflection
4 |
5 | []
6 | []
7 | []
8 | []
9 | []
10 | do ()
11 |
12 | module internal AssemblyVersionInformation =
13 | let [] AssemblyTitle = "LinqOptimizer"
14 | let [] AssemblyProduct = "LinqOptimizer"
15 | let [] AssemblyCompany = "Nessos Information Technologies"
16 | let [] AssemblyVersion = "0.7.0"
17 | let [] AssemblyFileVersion = "0.7.0"
18 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.FSharp/LinqOptimizer.FSharp.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0;net45
5 | AnyCPU
6 | false
7 | true
8 | true
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/LinqOptimizer.FSharp/QueryBuilder.fs:
--------------------------------------------------------------------------------
1 | namespace Nessos.LinqOptimizer.FSharp
2 |
3 | open System
4 | open System.Collections
5 | open System.Collections.Generic
6 | open System.Linq
7 | open System.Linq.Expressions
8 | open System.Reflection
9 |
10 | open Microsoft.FSharp.Linq
11 | open Microsoft.FSharp.Quotations
12 | open Microsoft.FSharp.Quotations.Patterns
13 | open Microsoft.FSharp.Quotations.DerivedPatterns
14 |
15 | open Nessos.LinqOptimizer.Base
16 |
17 | type private QueryBuilder () =
18 |
19 | member __.Quote (expr : Expr<'T>) = expr
20 | member __.Source(source:IEnumerable<'T>) : IQueryExpr> = raise <| NotImplementedException()
21 |
22 | []
23 | member __.Select(source : QueryExpr>, [] projection:('T -> 'R)) : QueryExpr> = raise <| NotImplementedException()
24 |
25 | member __.For(source:QueryExpr>, body:('T -> QueryExpr<'R>)) : QueryExpr<'R> = raise <| NotImplementedException()
26 |
27 | []
28 | member __.Where(source:QueryExpr<'T>,[] selector: 'T -> bool) : QueryExpr<'T> = raise <| NotImplementedException()
29 |
30 | member __.Yield (x : 'T) : QueryExpr> = raise <| NotImplementedException()
31 |
32 | member __.Zero () : QueryExpr<_> = raise <| NotImplementedException()
33 |
34 | member __.Run (expr : Expr<'T>) = expr
35 |
36 | module private QueryBuilderCompiler =
37 |
38 | let toExpression (expr : Expr) : Expression =
39 | Linq.RuntimeHelpers.LeafExpressionConverter.QuotationToExpression(expr)
40 |
41 | let evalExpr<'T> (expr : Expr) =
42 | Linq.RuntimeHelpers.LeafExpressionConverter.EvaluateQuotation(expr) :?> 'T
43 |
44 | // let rec transform (expr : Expr>>) : Expr>> =
45 | // match expr with
46 | // | Call (o ,m, [seqExpr]) when m.Name = "Source" ->
47 | // let source = evalExpr> seqExpr
48 | // <@ ExtensionMethods.AsQueryExpr(source) @>
49 | // Expr.Call()
50 | // | Call (o ,m, [source; func]) when m.Name = "Select" ->
51 | // let source = source :?> Expr>>
52 | // let func = func :?> Expr>
53 | //
54 | // let func = toExpression func
55 | // let source = source :?> Expr
56 | // let source = toExpression (transform source)
57 | // <@ Query.map func source @>
58 | // | _ ->
59 | // expr
60 | //
61 | // []
62 | // module Extensions =
63 | // let query = new QueryBuilder ()
64 | //
65 | // let x =
66 | // (query { for x in 1..10 do select x })
67 | //
68 | // QueryBuilderCompiler.transform x
69 | //
70 | // match x with
71 | // | Call (_,m, [source; func]) when m.Name = "For" ->
72 | // func.Type :> obj
73 | // | Call (o ,m, [source; func]) when m.Name = "Select" ->
74 | // (source.Type.ToString(), func.Type.ToString()) :> obj
75 | //
76 | // |> ignore
77 | //
78 | // Call (Some (Value (MBI_0024+QueryBuilder)), For,
79 | // [),
80 | // Lambda (_arg1,
81 | // Let (x, _arg1,
82 | // Call (Some (Value (MBI_0024+QueryBuilder)), Yield, [x])))])
83 | //
84 | // Lambda (x, x)
85 | //
86 | //"MBI_0010+QueryExpr`1[System.Collections.Generic.IEnumerable`1[System.Int32]]",
87 | //"Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,System.Int32]"
88 | //
89 | //Call (Some (Value (MBI_0024+QueryBuilder)), Source, [Call (None, op_Range, [Value (1), Value (10)])]
90 | //
91 | //
92 | //let x =
93 | // Linq.RuntimeHelpers.LeafExpressionConverter.EvaluateQuotation <@@ (fun x -> x + 1) @@> :?> (int -> int)
94 | //
95 | //
96 | // module F =
97 | // open System.Linq
98 | //
99 | // let x = Enumerable.Repeat(1,10)
100 | // let ty = x.GetType()
101 | // ty.FullName = "System.Linq.Enumerable+d__b8"
102 | // ty.GetFields() |> Array.iter (fun m -> printfn "%A" m.Name)
103 | // ty.GetField("<>3__start").GetValue(x)
104 | // ty.GetField("<>3__count").GetValue(x)
105 | //
106 |
--------------------------------------------------------------------------------
/tests/LinqOptimizer.Tests.CSharp/LinqOptimizer.Tests.CSharp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.2;net461
5 | AnyCPU
6 | true
7 | true
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/tests/LinqOptimizer.Tests.CSharp/ParallelQueryTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using Nessos.LinqOptimizer.CSharp;
7 | using FsCheck;
8 |
9 | namespace Nessos.LinqOptimizer.Tests
10 | {
11 | [TestFixture]
12 | class ParallelQueryTests
13 | {
14 | [Test]
15 | public void Select()
16 | {
17 | Prop.ForAll(xs =>
18 | {
19 | var x = xs.AsParallelQueryExpr().Select(n => n * 2).Sum().Run();
20 | var y = xs.AsParallel().Select(n => n * 2).Sum();
21 | return x == y;
22 | }).QuickCheckThrowOnFailure();
23 | }
24 |
25 | [Test]
26 | public void Where()
27 | {
28 | Prop.ForAll(xs =>
29 | {
30 | var x = (from n in xs.AsParallelQueryExpr()
31 | where n % 2 == 0
32 | select n)
33 | .Sum()
34 | .Run();
35 | var y = (from n in xs.AsParallel()
36 | where n % 2 == 0
37 | select n)
38 | .Sum();
39 |
40 | return x == y;
41 | }).QuickCheckThrowOnFailure();
42 | }
43 |
44 | [Test]
45 | public void Pipelined()
46 | {
47 | Prop.ForAll(xs =>
48 | {
49 | var x = xs.AsParallelQueryExpr()
50 | .Where(n => n % 2 == 0)
51 | .Select(n => n * 2)
52 | .Select(n => n * n)
53 | .Sum()
54 | .Run();
55 |
56 | var y = xs
57 | .AsParallel()
58 | .Where(n => n % 2 == 0)
59 | .Select(n => n * 2)
60 | .Select(n => n * n)
61 | .Sum();
62 |
63 | return x == y;
64 | }).QuickCheckThrowOnFailure();
65 | }
66 |
67 | [Test]
68 | public void SumInt()
69 | {
70 | Prop.ForAll(xs =>
71 | {
72 | var x = (from n in xs.AsParallelQueryExpr()
73 | select n * 2).Sum().Run();
74 | var y = (from n in xs.AsParallel()
75 | select n * 2).Sum();
76 |
77 | return x == y;
78 | }).QuickCheckThrowOnFailure();
79 | }
80 |
81 | [Test]
82 | public void SumDouble()
83 | {
84 | Prop.ForAll(xs =>
85 | {
86 | var x = (from n in xs.AsParallelQueryExpr()
87 | select n * 2).Sum().Run();
88 | var y = (from n in xs.AsParallel()
89 | select n * 2).Sum();
90 |
91 | return (Double.IsNaN(x) && Double.IsNaN(y)) || Math.Ceiling(x) == Math.Ceiling(y);
92 | }).QuickCheckThrowOnFailure();
93 | }
94 |
95 | [Test]
96 | public void SumLong()
97 | {
98 | Prop.ForAll(xs =>
99 | {
100 | var x = (from n in xs.AsParallelQueryExpr()
101 | select n * 2).Sum().Run();
102 | var y = (from n in xs.AsParallel()
103 | select n * 2).Sum();
104 |
105 | return x == y;
106 | }).QuickCheckThrowOnFailure();
107 | }
108 |
109 | [Test]
110 | public void SelectMany()
111 | {
112 | Prop.ForAll(xs =>
113 | {
114 | var x = xs.AsParallelQueryExpr()
115 | .SelectMany(n => xs.Select(_n => n * _n))
116 | .Sum().Run();
117 | var y = xs.AsParallel()
118 | .SelectMany(n => xs.Select(_n => n * _n))
119 | .Sum();
120 |
121 | return x == y;
122 | }).QuickCheckThrowOnFailure();
123 | }
124 |
125 | [Test]
126 | public void SelectManyNested()
127 | {
128 | Prop.ForAll(xs =>
129 | {
130 | var x = xs.AsParallelQueryExpr()
131 | .SelectMany(num => xs.SelectMany(_num => new[] { num * _num }))
132 | .Sum().Run();
133 | var y = xs
134 | .AsParallel()
135 | .SelectMany(num => xs.SelectMany(_num => new[] { num * _num }))
136 | .Sum();
137 |
138 | return x == y;
139 | }).QuickCheckThrowOnFailure();
140 | }
141 |
142 | [Test]
143 | public void SelectManyPipeline()
144 | {
145 | Prop.ForAll(xs =>
146 | {
147 | var x = xs.AsParallelQueryExpr()
148 | .Where(num => num % 2 == 0)
149 | .SelectMany(num => xs.Select(_num => num * _num))
150 | .Select(i => i + 1)
151 | .Sum()
152 | .Run();
153 |
154 | var y = xs
155 | .AsParallel()
156 | .Where(num => num % 2 == 0)
157 | .SelectMany(num => xs.Select(_num => num * _num))
158 | .Select(i => i + 1)
159 | .Sum();
160 |
161 | return x == y;
162 | }).QuickCheckThrowOnFailure();
163 | }
164 |
165 | [Test]
166 | public void SelectManyCompehension()
167 | {
168 | Prop.ForAll(xs =>
169 | {
170 | var x = (from num in xs.AsParallelQueryExpr()
171 | from _num in xs
172 | select num + " " + _num).Run();
173 | var y = (from num in xs.AsParallel().AsOrdered()
174 | from _num in xs
175 | select num + " " + _num);
176 |
177 | return x.SequenceEqual(y);
178 | }).QuickCheckThrowOnFailure();
179 | }
180 |
181 | [Test]
182 | public void GroupBy()
183 | {
184 | Prop.ForAll(xs =>
185 | {
186 | #if MONO_BUILD
187 | var x = xs.AsParallelQueryExpr().GroupBy(num => num.ToString()).Select(g => g.Count()).Sum().Run();
188 | #else
189 | var x = (from num in xs.AsParallelQueryExpr()
190 | group num by num.ToString() into g
191 | select g.Count()).Sum()
192 | .Run();
193 | #endif
194 |
195 | var y = (from num in xs.AsParallel()
196 | group num by num.ToString() into g
197 | select g.Count()).Sum();
198 |
199 | return x == y;
200 | }).QuickCheckThrowOnFailure();
201 | }
202 |
203 | [Test]
204 | public void OrderBy()
205 | {
206 | Prop.ForAll(xs =>
207 | {
208 | var x = (from num in xs.AsParallelQueryExpr()
209 | orderby num
210 | select num * 2)
211 | .Run();
212 |
213 | var y = from num in xs.AsParallel()
214 | orderby num
215 | select num * 2;
216 |
217 | return x.SequenceEqual(y);
218 | }).QuickCheckThrowOnFailure();
219 | }
220 |
221 | [Test]
222 | public void OrderByDescending()
223 | {
224 | Prop.ForAll(xs =>
225 | {
226 | var x = (from num in xs.AsParallelQueryExpr()
227 | orderby num descending
228 | select num * 2)
229 | .Run();
230 |
231 | var y = from num in xs.AsParallel()
232 | orderby num descending
233 | select num * 2;
234 |
235 | return x.SequenceEqual(y);
236 | }).QuickCheckThrowOnFailure();
237 | }
238 |
239 |
240 | [Test]
241 | public void Count()
242 | {
243 | Prop.ForAll(xs =>
244 | {
245 | var x = xs.AsParallelQueryExpr()
246 | .Select(i => i)
247 | .Count()
248 | .Run();
249 |
250 | var y = xs.AsParallel()
251 | .Select(i => i)
252 | .Count();
253 |
254 | return x == y;
255 | }).QuickCheckThrowOnFailure();
256 | }
257 |
258 | [Test]
259 | public void ToList()
260 | {
261 | Prop.ForAll(xs =>
262 | {
263 | var x = xs.AsParallelQueryExpr()
264 | .ToList()
265 | .Run();
266 |
267 | var y = xs.AsParallel()
268 | .ToList();
269 |
270 | return x.OrderBy(i => i).SequenceEqual(y.OrderBy(i => i));
271 | }).QuickCheckThrowOnFailure();
272 | }
273 |
274 | [Test]
275 | public void ToArray()
276 | {
277 | Prop.ForAll>(xs =>
278 | {
279 | var x = xs.AsParallelQueryExpr()
280 | .ToArray()
281 | .Run();
282 |
283 | var y = xs.AsParallel()
284 | .ToArray();
285 |
286 | return x.OrderBy(i => i).SequenceEqual(y.OrderBy(i => i));
287 | }).QuickCheckThrowOnFailure();
288 | }
289 |
290 | [Test]
291 | public void LinqLetTest()
292 | {
293 | Prop.ForAll>(nums =>
294 | {
295 | var x =
296 | (from num in nums.AsParallelQueryExpr()
297 | let r1 = num * 2
298 | let r2 = r1 * 2
299 | select r2 * num * 2).Sum().Run();
300 |
301 | var y =
302 | (from num in nums.AsParallel()
303 | let r1 = num * 2
304 | let r2 = r1 * 2
305 | select r2 * num * 2).Sum();
306 |
307 | return x == y;
308 | }).QuickCheckThrowOnFailure();
309 | }
310 |
311 | [Test]
312 | public void ThenBy()
313 | {
314 | Prop.ForAll(ds =>
315 | {
316 | var x = ds.AsParallelQueryExpr()
317 | .OrderBy(d => d.Year)
318 | .ThenBy(d => d.Month)
319 | .ThenBy(d => d.Day)
320 | .ThenBy(d => d)
321 | .Run();
322 |
323 | var y = ds.AsParallel().AsOrdered()
324 | .OrderBy(d => d.Year)
325 | .ThenBy(d => d.Month)
326 | .ThenBy(d => d.Day)
327 | .ThenBy(d => d);
328 |
329 | return x.SequenceEqual(y);
330 | }).QuickCheckThrowOnFailure();
331 | }
332 |
333 | [Test]
334 | public void ThenByDescending()
335 | {
336 | Prop.ForAll(ds =>
337 | {
338 | var x = (ds.AsParallelQueryExpr()
339 | .OrderByDescending(d => d.Year)
340 | .ThenBy(d => d.Month)
341 | .ThenByDescending(d => d.Day)
342 | .Select(d => d.Year + ":" + d.Month + ":" + d.Day)).Run();
343 |
344 | var y = ds.AsParallel()
345 | .OrderByDescending(d => d.Year)
346 | .ThenBy(d => d.Month)
347 | .ThenByDescending(d => d.Day)
348 | .Select(d => d.Year + ":" + d.Month + ":" + d.Day);
349 |
350 | return x.SequenceEqual(y);
351 | }).QuickCheckThrowOnFailure();
352 | }
353 |
354 | [Test]
355 | public void PreCompileFunc()
356 | {
357 | Prop.ForAll(i =>
358 | {
359 | if (i < 1) return true;
360 |
361 | var t = ParallelExtensions.Compile(m =>
362 | Enumerable.Range(1, m).AsParallelQueryExpr().Sum());
363 |
364 | var x = t(i);
365 |
366 | var y = Enumerable.Range(1, i).AsParallel().Sum();
367 |
368 | return x == y;
369 | }).QuickCheckThrowOnFailure();
370 | }
371 |
372 | }
373 | }
374 |
--------------------------------------------------------------------------------
/tests/LinqOptimizer.Tests.CSharp/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/tests/LinqOptimizer.Tests.FSharp/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/tests/LinqOptimizer.Tests.FSharp/LinqOptimizer.Tests.FSharp.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.2;net461
5 | AnyCPU
6 | true
7 | true
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/tests/LinqOptimizer.Tests.FSharp/PQuery.fs:
--------------------------------------------------------------------------------
1 | namespace Nessos.LinqOptimizer.Tests
2 |
3 | open System
4 | open System.Collections.Generic
5 | open System.Linq
6 | open System.Text
7 | open FsCheck
8 | open NUnit.Framework
9 | open Nessos.LinqOptimizer.FSharp
10 |
11 |
12 | []
13 | type ``F# Parallel Query tests`` () =
14 |
15 | let equal x y = Enumerable.SequenceEqual(x,y)
16 |
17 | []
18 | member __.``map`` () =
19 | let test (xs : seq) =
20 | let x = xs |> PQuery.ofSeq |> PQuery.map (fun n -> 2 * n) |> PQuery.run
21 | let y = xs |> Seq.map (fun n -> 2 * n)
22 | Seq.sum x = Seq.sum y
23 | Check.QuickThrowOnFailure (TestInput.RunTest test)
24 |
25 | []
26 | member __.``filter`` () =
27 | let test (xs : seq) =
28 | let x = xs |> PQuery.ofSeq |> PQuery.filter (fun n -> n % 2 = 0) |> PQuery.run
29 | let y = xs |> Seq.filter (fun n -> n % 2 = 0)
30 | Seq.sum x = Seq.sum y
31 | Check.QuickThrowOnFailure (TestInput.RunTest test)
32 |
33 | []
34 | member __.``pipelined`` () =
35 | let test (xs : seq) =
36 | let x = xs |> PQuery.ofSeq
37 | |> PQuery.filter (fun n -> n % 2 = 0)
38 | |> PQuery.map (fun n -> n * 2)
39 | |> PQuery.run
40 |
41 | let y = xs |> Seq.filter (fun n -> n % 2 = 0)
42 | |> Seq.map (fun n -> n * 2)
43 | Seq.sum x = Seq.sum y
44 | Check.QuickThrowOnFailure (TestInput.RunTest test)
45 |
46 | []
47 | member __.``sum int`` () =
48 | let test (xs : seq) =
49 | let x = xs |> PQuery.ofSeq |> PQuery.map (fun x -> x * x) |> PQuery.sum |> PQuery.run
50 | let y = xs |> Seq.map (fun x -> x * x) |> Seq.sum
51 | x = y
52 | Check.QuickThrowOnFailure (TestInput.RunTest test)
53 |
54 | []
55 | member __.``sum double`` () =
56 | let test (xs : seq) =
57 | let x = xs |> PQuery.ofSeq |> PQuery.map (fun x -> x * x) |> PQuery.sum |> PQuery.run
58 | let y = xs |> Seq.map (fun x -> x * x) |> Seq.sum
59 | (Double.IsNaN x && Double.IsNaN y) || Math.Ceiling(x) = Math.Ceiling(y)
60 | Check.QuickThrowOnFailure (TestInput.RunTest test)
61 |
62 | []
63 | member __.``sum int64`` () =
64 | let test (xs : seq) =
65 | let x = xs |> PQuery.ofSeq |> PQuery.map (fun x -> x * x) |> PQuery.sum |> PQuery.run
66 | let y = xs |> Seq.map (fun x -> x * x) |> Seq.sum
67 | x = y
68 | Check.QuickThrowOnFailure (TestInput.RunTest test)
69 |
70 | []
71 | member __.``collect`` () =
72 | let test (xs : seq<'T>) =
73 | let x = xs |> PQuery.ofSeq
74 | |> PQuery.collect (fun n -> Seq.map (fun n' -> n' * n) xs)
75 | |> PQuery.run
76 | let y = xs |> Seq.collect (fun n -> Seq.map (fun n' -> n' * n) xs )
77 | Seq.sum x = Seq.sum y
78 | Check.QuickThrowOnFailure (TestInput.RunTest test)
79 | Check.QuickThrowOnFailure (TestInput.RunTest test)
80 |
81 | []
82 | member __.``collect (nested pipe)`` () =
83 | let test (xs : seq<'T>) =
84 | let x = xs |> PQuery.ofSeq
85 | |> PQuery.collect (fun n -> xs |> Seq.map (fun n' -> n' * n) )
86 | |> PQuery.run
87 | let y = xs |> Seq.collect (fun n -> Seq.map (fun n' -> n' * n) xs )
88 | Seq.sum x = Seq.sum y
89 | Check.QuickThrowOnFailure (TestInput.RunTest test)
90 | Check.QuickThrowOnFailure (TestInput.RunTest test)
91 |
92 | []
93 | member __.``collect (nested groupBy)`` () =
94 | let test (xs : seq) =
95 | let x = xs |> PQuery.ofSeq
96 | |> PQuery.collect (fun n -> xs |> Seq.groupBy (fun x -> x))
97 | |> PQuery.map (fun (a,x) -> x |> Seq.sum)
98 | |> PQuery.run
99 | let y = xs |> Seq.collect (fun n -> xs |> Seq.groupBy (fun x -> x))
100 | |> Seq.map (fun (a,x) -> x |> Seq.sum)
101 | Seq.sum x = Seq.sum y
102 | Check.QuickThrowOnFailure (TestInput.RunTest test)
103 |
104 | []
105 | member __.``groupBy`` () =
106 | let test (xs : seq) =
107 | let x = xs |> PQuery.ofSeq
108 | |> PQuery.groupBy (fun x -> string x)
109 | |> PQuery.map (fun (a,x) -> Seq.sum x)
110 | |> PQuery.run
111 | let y = xs |> Seq.groupBy (fun x -> string x)
112 | |> Seq.map (fun (a,x) -> Seq.sum x)
113 |
114 | Seq.sum x = Seq.sum y
115 | Check.QuickThrowOnFailure (TestInput.RunTest test)
116 |
117 | []
118 | member __.``sort`` () =
119 | let test (xs : seq) =
120 | let x = xs |> PQuery.ofSeq
121 | |> PQuery.sort
122 | |> PQuery.run
123 | let y = xs |> Seq.sort
124 | equal x y
125 | Check.QuickThrowOnFailure (TestInput.RunTest test)
126 |
127 | []
128 | member __.``sortBy`` () =
129 | let test (xs : seq) =
130 | let x = xs |> PQuery.ofSeq
131 | |> PQuery.sortBy (fun x -> -x)
132 | |> PQuery.run
133 | let y = xs |> Seq.sortBy (fun x -> -x)
134 | equal x y
135 | Check.QuickThrowOnFailure (TestInput.RunTest test)
136 |
137 | []
138 | member __.``toArray`` () =
139 | let test (xs : seq) =
140 | let x = xs |> PQuery.ofSeq
141 | |> PQuery.toArray
142 | |> PQuery.run
143 | let y = xs |> Seq.toArray
144 | equal (Seq.sort x) (Seq.sort y)
145 | Check.QuickThrowOnFailure (TestInput.RunTest test)
146 |
147 | []
148 | member __.``precompile function``() =
149 | let test (xs : seq) =
150 | let t = PQuery.compile,int>(PrecompileHelpers.``fun x -> PQuery.length (PQuery.ofSeq x)``)
151 | let x = t(xs)
152 | let y = xs |> Seq.length
153 | x = y
154 | Check.QuickThrowOnFailure (TestInput.RunTest test)
--------------------------------------------------------------------------------
/tests/LinqOptimizer.Tests.FSharp/PrecompileHelpers.fs:
--------------------------------------------------------------------------------
1 | namespace Nessos.LinqOptimizer.Tests
2 |
3 | module PrecompileHelpers =
4 |
5 | open System
6 | open System.Collections
7 | open System.Collections.Generic
8 | open System.Linq
9 | open System.Linq.Expressions
10 | open Nessos.LinqOptimizer.Base
11 | open Nessos.LinqOptimizer.Core
12 | open Nessos.LinqOptimizer.FSharp
13 | open Microsoft.FSharp.Linq
14 | open Microsoft.FSharp.Linq.RuntimeHelpers
15 | open Microsoft.FSharp.Quotations
16 |
17 | let private queryType = typeof
18 | let private ofSeq = queryType.GetMethod("ofSeq")
19 | let private length = queryType.GetMethod("length")
20 | let private iter = queryType.GetMethod("iter")
21 |
22 | let private pQueryType = typeof
23 | let private pOfSeq = pQueryType.GetMethod("ofSeq")
24 | let private pLength = pQueryType.GetMethod("length")
25 |
26 | let ``fun x -> Query.length(Query.ofSeq x))`` =
27 | let x = Expression.Parameter(typeof>, "x")
28 | let ofSeq = Expression.Call(ofSeq.MakeGenericMethod [| typeof |], x)
29 | let length = Expression.Call(length.MakeGenericMethod [| typeof |], ofSeq)
30 | Expression.Lambda, Nessos.LinqOptimizer.Base.IQueryExpr>>(length, x)
31 |
32 |
33 | let ``fun x -> PQuery.length (PQuery.ofSeq x)`` =
34 | let x = Expression.Parameter(typeof>, "x")
35 | let ofSeq = Expression.Call(pOfSeq.MakeGenericMethod [| typeof |], x)
36 | let length = Expression.Call(pLength.MakeGenericMethod [| typeof |], ofSeq)
37 | Expression.Lambda, Nessos.LinqOptimizer.Base.IParallelQueryExpr>>(length, x)
38 |
39 | // let ``fun x -> Query.iter (fun m -> a.Add(m)) (Query.ofSeq x)`` =
40 | // let a = Var("a", typeof>)
41 | // let m = Var("m", typeof)
42 | // let x = Var("x", typeof>)
43 | // let add = typeof>.GetMethod("Add") //.MakeGenericMethod [| typeof |]
44 | // let ofSeq = Expr.Call(ofSeq.MakeGenericMethod [| typeof |], [Expr.Var x])
45 | // let action =
46 | // let action = Expr.Lambda(m, Expr.Call(Expr.Var a, add, [Expr.Var m]))
47 | // <@ LeafExpressionConverter.QuotationToLambdaExpression %%action @>
48 | //
49 | // let iter = iter.MakeGenericMethod [| typeof |]
50 | // let iter1 = Expr.Call(iter, [action])
51 | //
52 | // ()
--------------------------------------------------------------------------------