From a1d929f561641646f433947a912040347f4bcb8e Mon Sep 17 00:00:00 2001 From: Willy <63505597+willysoft@users.noreply.github.com> Date: Fri, 18 Apr 2025 17:01:10 +0800 Subject: [PATCH] chore: Add benchmarking project and performance comparison - Introduced `AutoQuery.Benchmark` project for performance testing. - Added `BenchmarkDotNet` for benchmarking setup. - Implemented `QueryPerformance` benchmarks comparing AutoQuery and Dynamic LINQ. - Included benchmark configuration and test data generation. - Updated README with benchmark results and image. --- AutoQuery.sln | 9 ++ README.md | 6 ++ imgs/benchmarks.jpg | Bin 0 -> 28378 bytes .../AutoQuery.Benchmark.csproj | 20 +++++ .../AutoQuery.Benchmark/BenchmarkConfig.cs | 18 ++++ .../Benchmarks/QueryPerformance.cs | 80 ++++++++++++++++++ sandbox/AutoQuery.Benchmark/Program.cs | 4 + 7 files changed, 137 insertions(+) create mode 100644 imgs/benchmarks.jpg create mode 100644 sandbox/AutoQuery.Benchmark/AutoQuery.Benchmark.csproj create mode 100644 sandbox/AutoQuery.Benchmark/BenchmarkConfig.cs create mode 100644 sandbox/AutoQuery.Benchmark/Benchmarks/QueryPerformance.cs create mode 100644 sandbox/AutoQuery.Benchmark/Program.cs diff --git a/AutoQuery.sln b/AutoQuery.sln index 73d8502..3586283 100644 --- a/AutoQuery.sln +++ b/AutoQuery.sln @@ -26,6 +26,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoQueryApiDemo", "sample\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoQuery.AspNetCore.Tests", "test\AutoQuery.AspNetCore.Tests\AutoQuery.AspNetCore.Tests.csproj", "{A9125D44-3F97-4251-85F7-B28D07FA7B2D}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sandbox", "sandbox", "{75B3287A-5C54-494A-B646-72CFBF9E2FB2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoQuery.Benchmark", "sandbox\AutoQuery.Benchmark\AutoQuery.Benchmark.csproj", "{6F332D4E-4C59-40C7-836F-C7344FD9ACD5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -56,6 +60,10 @@ Global {A9125D44-3F97-4251-85F7-B28D07FA7B2D}.Debug|Any CPU.Build.0 = Debug|Any CPU {A9125D44-3F97-4251-85F7-B28D07FA7B2D}.Release|Any CPU.ActiveCfg = Release|Any CPU {A9125D44-3F97-4251-85F7-B28D07FA7B2D}.Release|Any CPU.Build.0 = Release|Any CPU + {6F332D4E-4C59-40C7-836F-C7344FD9ACD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6F332D4E-4C59-40C7-836F-C7344FD9ACD5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6F332D4E-4C59-40C7-836F-C7344FD9ACD5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6F332D4E-4C59-40C7-836F-C7344FD9ACD5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -67,6 +75,7 @@ Global {AEAB6CC3-1A1D-450D-A368-6A3BAF82A4DE} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {BF0093CA-57E3-4D5F-91B9-46898DFDD0C1} = {E97A2640-E7C7-4C4B-B3E2-311C1B9FDAA4} {A9125D44-3F97-4251-85F7-B28D07FA7B2D} = {5730CC15-795B-43C9-802C-18B3E19B7E22} + {6F332D4E-4C59-40C7-836F-C7344FD9ACD5} = {75B3287A-5C54-494A-B646-72CFBF9E2FB2} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8FA10855-B537-4BAA-BB5B-6C0FB0BA6F42} diff --git a/README.md b/README.md index 18701b0..7008b71 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,12 @@ - **Pagination and Sorting**: Built-in support for pagination and sorting. - **ASP.NET Core Integration**: Middleware support for easy integration into ASP.NET Core projects. +## Benchmark + +The benchmark results provide an overview of `AutoQuery`'s performance in handling dynamic queries and filtering. These results can help evaluate its suitability for various application scenarios. + +![](imgs/benchmarks.jpg) + ## Installation ### AutoQuery diff --git a/imgs/benchmarks.jpg b/imgs/benchmarks.jpg new file mode 100644 index 0000000000000000000000000000000000000000..80d16648ebe16ca404118b6c0c9e98b6a3f45baa GIT binary patch literal 28378 zcmbrlcT`i~_B|R!iXv4&Iw&Y2O*#mIjV?rb2azVk5b2>t=^X(9rAn_MQbKRiJ0d;Q z5IO=0H9&x0yze)@Z~WdE_jBL7?~wBshp^{abImpP-fLe@T`mG>)s@wh0avbE0lXpp z04`?$F90N0um1fIuWQ5)DFrF%wQHm|$jQklZr-?glkx^7B^5P2EfqB#H6 z21Z84n>4qXnHZSq85kMvyisAqBb=eA_qX6iVu#;T53%E*m zg@o?PWe0!@0JuU*9PQr$|L^O{RpJ=Q$SJPhpd>zkqy=2PLPB!&8p+>L6QA}Y{vL3R zj+Fk6=rgig+E(Ouof*VF#br_OJTL#rs564)6@TjzaQz0;ZDtnMd-wSs@IRE0l#-T_ zef;93lCp}bn!2u@zJZ~Uv5B>ft)2Zl2S-;ocMngfmv>-La7buactm`{m&ByxuisL# zb8_?Y3kr*hD=Mpy)it$s^=<7Pon75My?wt&$Hpfnr>0TpCCu{5>e~9oCT?&4;PB`e ze{%X4*A)QC{{`!Rko`Y!(GhW7y>^Y{8u?#bSFU;zZxXs|q<2Kg=$~nmTRGplEB1+k z;dxwE`OoV-;yPHyw=N?$n0O`7_i%rq{R`RuJ759-6|(;U_J89-0dA69AqJ0x4xk7i zV6c#&U4r`(T%`T#ko6 zfB5%G>r4@RvF8Iq74N`E7P=qQ%tmIkAN)G~NSA~(+FNcnU*Ae04{*AQa%|=qwQ%4K z{`CUp2i<;X_3@rH75(xD4=l*h%`aCLQC9EG&OFeI7QGQ!06nWoJTM!CyX~D2Vng2{ z@|$`5hd8felS7gHTjm=2HOVhh6s5OO)v3})Y0qEBhusYxh*P+3OHZ=?RJJoVHqKY1 z2JeGCpVY!h7`j4jsy}0g9dGZ-yzo40tecDOq`+{y4)TS5(OvcUflk#adGJu%MF;Hd z0P8<(4jp`)6j}JL?bjyLZG*deb58K+0U@q*N_wW3vt-M>?2i@~#~+^_8)9z8ytq1;bn{WJHOXYG1Xi)l@+x1T@2z6!=^kW>O@%GNryWH1(ENTXTkNSzBh1|yo_i=v`yaqZn^l;9wyFR4O z<1;?Mj|3RozQka^m+V}dFxd0+nr|RE4?>pxc-w*X1#ZhSMpk}mVp2;#!D+75$aE1? z?P#mkCY^K%z^)#+QoI#-=!T5cTo~zwwCMIc$c|_|yc+S~`+t4c1FsVl$9*00p%Iq= zE!y+UXXTHB*HZ6~u8>jgMY*_m8%kTC&*UxvJ>j^Vi%xqY@k&f|BfgXl3EDcm&fa|C zgXqjV&EQgEJc#{MgK%J_X}bhOv|v9Efg2V5Pwi3*UzLBm`Ff}(?i=8>QKBHcb+}(i z#z)Pi=1tdIPNvA>oPnLhCKF4B0?GNd5~Q< zY(|V7;GqwjtR&l(kE-+P@ZFBo7_wDvnz1p)612z(YZc`5JoFNvEF@Vc=35SCl6PNh zQb8|7x{FXd&Kn-ZWO7G9?i}ZDXVuEEEJm~;OwLPNmX{ixF9DX3mw@c}I<6qgJp*~h zOMq2_$T??XZg!#y=`1G8LbvUCV~sNqTh}{csdq8T#o!JVX|&HZ@wEcmu}Es9@;5%L zHyiQG;F4^AY4@(}$1BnvC+9e$`MY=^*0MbgZ?L8_K`_lVg6625`Q>%Yzgp^S{%~_i zrsi=xEjY@?H6h9%KBq+6#~YR^Q)FjYaD6;~QmU8#N#bdj$d1<0uaoA8Q?hn+Y@4ZK zNHaMuqy@IruVL=!AaT}F9Y04X!c6g|!`xKEIM(b3mKk@bSfziqgrGc<(IvyQpl0r| zw>T$+yLC1&yOTk+dc^EJ-RZuuUioZ?R4c=$jDx1Ah8)9hBZkMe4W>@xJIQ&e3jW#6 z!N2@Tpi)Cz5mNqqEt85>1RD2djcCy<+F6uWLb+;t3RqhK|u0D-5T=$FqG?E=G|8kbUU<)jz z30nO&Fg4vhsny(<;iwzl}h*Y4}kWz#wnXx9f2EQi~4^! z1#yNQ!(sEc@gKP-edTTk6Jb^5&NJMfa}tDSB`UAOk04JK^l7^J3S~;772fSLFNrpH zRpiF6&p6FA2)x@l@M{8oeTOJ+)IYlqwQl#;LKO6uTAD&bHAhockQ#7ZEv8#$f>tQf zYMP_OYb!4vZHfnornS%zNbs(Ojx98e(mqAd6h(Sy_wzvAfOo93D3kuXJ^mkbUHRYj zw90Z8$XkwY^4Ynn$hP&dRuJn8}%CH^>L zp#J0uCPC%9)!og5ZES)=y3gM>7P_D&Ycd5^{BE0pnG}dy$PR&9!IQh%_@RoPdEwUmw>m`--uZj?Z8|>E<7PqZ1BaI zVM8c44{u5srpc~q6wiMFhbE|vmfd^w%+r&qF+R$cahBf+y2w5`$@y?xfqT=Ew$XOc z@|xoX8}FyW=J#4!*fk_s3g*v|y;rI-={`!tuILD!73+ zL8Ef_5^xPa5pkN_)>458M#zhxLnn~t&u8=|-FlEbMcqXkzl@Epe`!=d+N;fYB|O*p zLBUBpFvh;Uh5Ulk7^>1LeId|{V@OGkfb2?)n=@&+UV~8ewcYE__aJP=^~(0}Fc+UT zo)V>Ge82!4k~rq{XFGcjatQ##M&++YoWhR4Wqu4T&{($4mGZF>lKB@f!mrh0fV-7cGVWhEcb-1$Hg1f*$MfJRu5`%%t2Sc?b zNa!gds8JE!WJh=|NO3; zQ{3ClxN-?FdTdvm$fsWw<&0l{bBATPr21%Q<}7K>BDBX9O4Bhh&jzF&M9-NBU1=34 zZi>nt3z&IEpE%R+Jc(d1B0hl14^*hLB|fnKvgW4U82pc|&%3s$a)+zi%p5LIgbJ1# z;pcNEM=+|YZK}=qJ?3zg;$GsnyMr_KZxe!tkzAEK2w#+aN4muPEQdT)TK9{I7kq`h z!`fp2BeNSd{y?d=Y>P0k7E!{i?_n4ezg4LX^Dl@VvG|5fK<|b&63YNHw4^gRC5n>^ zJ8LixZvK30+whi|YA=0>%)^|A9r0A2kTY4l9gNZ8_y@>4my@IpTbdylua+HsNbx-? z82aH+@G)b%HTwZZQZ$F3cLZ+M7R=Ijt2x8`eR|XTQ3YsrlkNo_A~Zu7C;d0GuAzuN zDQRaryZN^hY2|FyQ?j{hg;xWk7rZFg)c8m;x|cj#VK)eJBfg4DC0~6tK8GKkIP}4VzgCA%~tfqXaHupZ*uTGaA&L~9WT>?No z{#?fRY$#k%at6l0A@&oVA`9-y{LvM<_KewOM%smq?T5FsiT}g%1bI<(6QAMe%7h5K z@kRc|pbIK*qNsrKdKuHB?W;57XX*hiSzp~*elicm(g@o#Z&|0XAh~R{^7Jsn6=%qU zajz4uM-GplT(lJaEZ9ggWQ`_lR|}Y`rWo7E3^D4_YhBsU7uZ?|xCGoJFk&Y$T z_m0g|yA3CPY<8)3C{CAq{b-<2#|#KE&^5B#vs=rY+^9^KrpQ0O1cW@sPW3Dmt3R^X zR4bU@(b)O5p}<9STK@O&AJ3=Inh5(OqY9b>si5s_ynh!c*QpNfnQ7ZmtPw7HMIXQbBXZcc`0H06(H zV;^ND12Ipus6bt;J6VpM@Uq>t+VHP$zId^GiJJ)!C_hgHFV*95i|_2B?}s0%2CCc* zF<@DLR%W>*0lM8Gax446OLLv|hlQ;qgHEId&4b>b)BGFmfxQgpJVe?r0qhF|X`DVH zOri(8)>sn(jh92(MTJ{IP45_MJ@4so9iwg%c+sxNBsok0mDa(oeicI3G`AqfG*>6i zUWz}R$q}vI&dKo79Ys1<>G7+}iRZiV>v^U@Y8H2zSBKSb3*D5NVJ7Cwk7I-6`M|{% zk9(cVj5Y?T{=_hgi(1`p9Z(r~9<_Y>d&<{i><;!xa`2%D6%^7Th4K}fnVR360NxwV zR@IMg;IgVgah6 z8LCkpot@AZ|9~(Ef3Oh0(DXj>p$xnCG?s7sdKvU~d%b`3dGvSCQHuGf#YQxI>f7?@ z>RXl+CPOpZkMG%|q9&6^o)<|LTM068f|6bv5Y_w=Abngp<_1CWbVy0D4kjV;-}wt8 z#}03gXNb@mvjvB2cX1lERc!M*2PR?)%=TdP3fzvC42`yaOJEB*D3Yx+*w*wVKiAHZ z>L)<_GXZA4j9d&!#A;#I*q6jq;S8o={xj8Qe3t;K3ss_z9N4_^Ap7HgeG_d#;D!8e zc`meNxYXmc3Ki$FcbZl<^kP5rgsHD{kdfAaSR7xnR3HKV{Z$f6t4=9A2a@4T@Wi`0}GIX@Is2*e7MlZ#GDmU4^@i6La6T zLrAV!-*2ps7)1@&^&YZI&MccOtoh7e0(_o8FMg-wdQ+7}JD^2wY@qxyipUTS9PqUM zBcHHzQQuR(v4Rujd?E7+nKd(NVPpx)@;ANk0*}h_KvZ)4k zrlg%Qx4qHl{MqZ8bEgha*;=;S$kNne?!B8Lm# z$8q__q|R!$q8Bj_y3sR+1xwyF;fn7xhHv>k3H|vSFWu(hZCh&%7RpQ8 zl5D7!?g<*ulLIer?>KT@_5YQ!_Q_YSV0~cF51YC}YQT`mxUX#px$B<8kvn>Y>q{Q3n;xfYo-85Op<~Ngn zQ-HVIiy_uCf={`^8Pf9P_=-Mcy`UP@5lA|1Nj|wNLNl*Yu!RDTb%edCi!tQ3**3U# z5zs&r+x%!$f!p^DPBF~QixVt%v8+bY6U?+w3d3GJ&#j>V&v znC?{9_c4CKaPLuIB6buVQ{RbZ?35V=bV_kGa@@$*tx3|S=lw*dmBibx|4|~%YKa7FY)N`FiHZ8c%zq^Yt zttXg(GMKQ{h~j>SBPkr!P}%}Y?)f!CF~Lt7#h*eR0e4FR7heiWwJ|9TE}%MKdmsF`c#qx{meyg*heB%A=lOn zt{)>^vER3j>cK=whW~F#rj!umaNY+@$+EB@Db z7=Oi#CHyWL(OKi?O_eMN&C_xzqL^-Ki+BhUkVUff2npqg%>wUsvcLIg{B@DqZ}3?& zs)CiMkSNOT6qODU#&^jmi3&{!D9hML-6krFz9UHH=cf34E$lW_#Q@6}hH&HX_%-D? z@rvHZ^=@;U-lLN*iSo42CtKr+AWBt1f-^d|CPap=Q6#_4=+^^5Ygu_uc)TOMMP|{O zmfKo*&OP{8>FDqI-39;92t(wHGec;zv@!a@0y)o}Vx^DvsNXjjnGdN*z*@tzg`TmXskcQED8 zC*G56N{SAAV`1nAN^>gk3lGpC%xf!y?mQQ1Nsg~E!O1HyVw*0SiXEjNCssHg<5Txbb;p`C*Vn4~7+#v$bcT7mWjc_h1h1kPiA z*X}aoZ<}GsvOuhpH?;7x&~M4i;jVKARGF@It(=yBQMR z9c%L31(=jg4>NT$rbi!# zE>#A!p~gADn20Grcvaoo0`d=YL#rDpi|eIhDN{S?5A-}!nzwdm4}dp(oyL9nO7O)S z5?~tLA2{*dF$u8x_)5jwdt&+H)XeYSdJtxv)8bO#Xh`H~a-gqk5j6SLZwaGh?EsJl zoCSV;bh+jW0DIz7`D4+!p~eLLp!g_$)I7?9K5(_s|DOHYxzWKk4`wy*`xa_&J2a$G z9FhlO8bK{U-QP2Bj25%!Fb|~GkPI!J45~?Qb1Y5Z#*@PrHN^&|iEniJkpr#Cl>&GY zh17)_-vbwy>%%Qq8>=JyCw$Ce3(=|IpK>L>)r6|%wIZdnre zp0vG?yeZYs^W9?C+|o@91Rqg9GGvQZS^~!2ZoZ2poeT^O_7%@tub2~B7AQce$D0g@ zCP$|joP2wxW9f`iy#^3n)zjYQL$+@k`E;4p!)w-eTBN)U!$??a!|lpK_(89Uo-kEmjIG}Ym~hBnLa+X-4YvXMn&HKIH^3V zH7Ut^{5-H}z2>$4TQR}Fl^dNdfcLkC`4mKyAW%qa{aR$zxzn-g4^Y23)7bn%5AVx8 zD-P3}&mh^$XF4wF8hk~sb3)tpwfL2fS{4eH4i4qGrYDJ#(ViI=Te9WSOBFW?5voyQ zacx%pruW7t?q8PbuE{eWoQvwtofx7#mmiYNJS1bGm7=DC=IJ5+VD`^%Zq%MT6>FF( zUjj}fV>Qpe!IoQ~w=UY^V2@+YmMp_BSO_|}#I@kV@B^RJbmdJJn~(g$89wYBt;dXU zSV+4f_M5LRwk!PTEM0tia-{vJUus4|R?&;b+9>1EXiGW46h7s(XLg=+3YFi8vCym# z+p*qsPS8}>&WF#2>lEnTzV`&em}4$Hw6t}UhR^cxv-MJpg5=g!*n_-obbsyVH{e;> z$&HW{LAfr4lvQteVc0e%=H2ST#Jcx-LLbhGyat6}GlLE-+3^wG&qsv(XNngTlOd2* zUZ0t37}w7^AHCzu?IWA7;1^o!Z=Mbz^G!@0elZ&JMjBM6f3mxwpt@owANT8Zo{3u3 zT(eDIjJcL`!GoU~*;SA|i@Z70zk@2dxzF&vDo2O7#xNDsofrEH%$xRK zo0n{fXL+4dr20cIithM{KujYZR^)KOh&>K-x?d6bYVv*xrRUl6}Vha-;{<^My&A+h~EEs$`c_6z9ZB0xTqUrZ7*a6dWg%L<u;e<5eLU1jxS*FJxwqp&jEN?pf zcSJwapp_=KWpU4sPW$#?%nNF+HbWD8jIp9gI;AT8b83ve&^7dKm_O^*^*nrnrk~i6 zY7nmEv1)~~+LiHNiB?2+dr;;Ps*~*j4tn(UVVrKT400s5`iY{>d4a+M$1SIt4AJIv z@S4WgKtY&bmzN+Gu(2lTdLszNE0(3A6ZfWwnPLBs6hxb=tCj7H#;scgJHPHVO@|ZSja~x&(#6yqSr7%u0@a~bT}rry?*Nz- zMa&)$N@V?M<_57{`&|>wcYZ;@f-mpyzN)EBM8F%TeXFQ5EPko2?H(|;{#rATYRsJ1 zmzN@|krt9^kW+MrG}m)xLe8{&Un9rLc9(F`dlu%UAo(oo{-A5ziX!0d{qKLN!)9Rq z3g30My>+Gf)^_Y&v7Zs=v)0^PZH;YJjne2QVCU&deryiv$wh(38Fx+8$Dt`c5?<<$ zV`u8IO7|TEIjQ;k^sO8oLni-*Lh4%ZTo_BTW2k@FFW{W{NoFy<)lSogTZbhBJIsUQ>z0 z(;7Uc6i#Qn>069BH06lNu-+%L>TtkkFdBN0_M_kace39wxfS1Lejtj|E#_}aa zip>jp^9P#~cE22xL^nNPjVk-#WkGu2mo%G_4n1xMb!o-j`#n!dWBVs>C_WVW7k8NI6XysPqRkrp$Y}d7f?gj!29d99 z*Iu|s+haXDCb@{LrIBZ<=H#wdihEnv7^YnO>!iy zQ_aYQx#@PH4qRKGM>p4k@7gsQ>w|(p!v?V<7T>ayI~6`c8N;K%*6qJopCN6>hNB?O z^FP?E6sGog1epaQ^{4YDNn=l+xco-+nYQ-Ms;b+IdgxN zf=$c6N@xzA*zb}ag*R@v(%QzZ#8KvNXE-xj+qh1Nqaw~qan*7dJi^r1Q~g*`3L+o5eqI=O) zuLGpmG_rx8JxY^~7sC8ROm0GxI`8FS0>$DFo!AC-A#ahrCyHu+U)nnletT;S+ZM>i z&NkXW{_22ntiasY%&us~zlAUlIb#CtI%R4|FSCDGPS~TpV!e0R7TZ_Zcnizb4Wo0@ zErG(G#vg0#zq&4;Oxt?zv%678O#X{Mb!!pm-BKrs{;f#4THfl0nz*bK7QK?LO;y`B z-n7rI`RKZqFr+#yi1CEB>ZN5dKkIdZvk@)%sa!AE$tgHCJmJjz_dy>!Rzw0jefquLq;}Qpy?Dsr&d+os zE=9 z1t0EnUgA>#vwj!tvy5sUuW$df(!1HX#;{?T2~_M!^N#wltu}3;c&fvX7r}JLHgl}E zOe?VCBqtIJyrQM2(Ru<+saRKIX@0_(eTi$n2!d>0r;oku-A-qT$%&)B zsMbWTra0HnH0nH$%SIyK!b5*PdyGUg`pszIAOq8i#Ka&QPu3+?m^1HIaL}1`Pt=dM zEuPFhEu4RiA`zdO zWwEIH3WD) zposCY+v22hJb}jG-#J76Bt;}&r@2_y{Lo}q@~;`wt{Q8t_>-%2#9ZUQ3awZk{o0(V(7vo0@Pn z?x3gFyL?=Sz59Nnt84JV_Kb-$mecu#k)$)quLqgGkh&Bk3O@SaZCAJ1lx5=AVm7+n z*cCcd8>1+yu($HD=a{8v6}(l4sEC$9a`1pmHALDkuKXc7Oqyu-7UHrtQab7|mc~(R z!#mB>lzxeplJwo{N6U0QW%mD2z$-2uLxC5xmXQ|se!;4o4k15gDK2Jw&+?O(b(5Y9 z%Mb*D;PyI>E^0Fd&Q>349lv_(d|`kyPgfy8UK#&o4l{C26znTDuK!OG$8CE2Q+%~I z{-y{Ob*JDTqN4@Bj~Xa1?*3>TDZ1iQc?&=J)zFZut%ac%ole;ykS98V>|gk^t*|v5 zdCSa6ppvbkvBag7nnq$=9bq@H7S!5~DIoP67qV!Hxdep&cKAn!t{=@8C4BL~_xY$K z4JR~vb&dlQf-%{XpB7ruQN2TZ)j4B!e?C2ja0^!uCht{<7i(AAClZWY+Q8B=*te;) zc%f04c5hS0I$l{IJcUJQ6`H3UqxZHnhQgL7fa1@)FU6+wO^w}JF?79Wq>KU(284a( zB|tc8;Xg2>N%!azuB?W*IenCm%P2>0E1Qex#%#Scdw%5&{013&fGi`1m9b#va8O}p z;GOSQ7-s+0uCUR~Sm%^2|Njth{vt`unfi0JbQujs=D~&8_)61~t_w>MaovBQ(Lb1c z)Xk9`(lmL$@Sq4AL5iQpp z=0xrv%9TnjbUJ$`s(1{CG+vhQPcYo?UQkgx- zH@Xhm) zlH@%x!!%`e+3VQhysBX*VPQ!&)Av2KvkkV1Ko*Xo3`RT^2Ep22d_OkpyvS`Pzq~$u zXEB5!jh(rpY%^wIC*db~ZLwB!Qp=M4gB4KfH{DTKDDLD~;y?JvVSK}qeb~iA#}sRP zcgOA98&1A?bt#h8*WR~#-S}>w@4Ue1`2v3nGfW4q(Zvpih6>-CIOx)AM8>Nw?NPD| zuX#|0V8~@;nFpOw%?a+`Hz(_D4=j|nzwCnLO#|P@F_#EG<6aExktzHamt!LIepviI z(DVAe0m_}o6nO6d-JDb_$JynP1;aI#gznjJDi_I9QO;id9#U8~RYtRYkqVM2pO2Tr2M)BP|86R)aw6fpv;}GQTD0Wlxzw;U1)LB^aC9>qjBb?G? zNZH+yB+_~>`KhQPO1HQys?~?C#}OFzUj+t+m9Q|qcH09lebr~?8}5vVZpU?sqVv!;)XoC$wx%CA2(yx`Ah7F>M z(O$ux#O-5B%y1Y=X55YH3PR#2F)PN)$*Sg-%w~DG`Kute6W3vmcTtz`sV+0qikBoQ4ONt%1JvmBQqM07itS4>^0;3xAC$sK|Vw6T`OL z*kCl_^}IWog5s&a#Z2s)_PSyvtOH2zus=V&PLYH)@3-b}91{W?+ee{)l~$D4a5)Z4 zG*jRgp5gK`gUZ~l=i(S=DP5_e)5^`ol{?Utx5Pf}CG;}0XHXP3xgiisHaUrXTKu{Y zM0>^VI_U2_I?%DdD3f`?uj!3cn_u~|vXQ7|LzE2(-heNrSsXgKWE+jljF5lkT54*l zwx-0s`H=_1!lvgT3GB$cDX)!IWHiNtbbj-id!V(m%d6vrZTvcdN&4*8xN&3Fd2-#E zl#M`a7wAEwW?uS3P=|d8Uxu{rO?#Id`p~%cH~ZTB`jrhHn_#VstKxwL%&v+d6HSdR zi?sMH;ID&i^{yu*9pcdV@lq>?vinFM*PB!AZHzIhW;f}&<=qgk@pI1iGTUIET-#P< zvwsVlv%4GIW+C54P$Jausf(F-Wq&S$)Hn47!}eOuldM=#V2BI~$Gk zt*p2x3pPp9SHGdtpt(CamkheQJ-n@pv)G$Ax{resKKBK4s4E5JzKN~dcvXI0r0{Sf zN$SX>S)w@vH7^`_updF;j2mY#!izQ4iEn#xO$ zje@}8ao>_8jj)uWcJc_1w=hyXkC(_}-DHr_x7sVr9Y!k!Z)rH+%7kWs4@^# zv=|dKZZ7?*aRN!{!$xV0fH<+MP&L)xj#qwiwQxBl(?RE`LE1>3A!n$lYhu11vRA%7 z`Pb7@&TI+v60H614JOE$_87=_8LO9biMaD>7yWA@>Rw|y%)Fpgy`MMmEJ-oxrK|zk zJJ7hkrZtm&-T&S%f+8%74Qy`$PLhJSlbx_}TQ&PCdvU=aCZXM#q`sEeVPME{hlzVr zVq>LW`YV%Yc)!z!^qIjY)yEex#G&T-+BnDEe6Pzsi;ke98f7!y?E7BJBnBYkC>aNk7%%TiIwKk zegLY64DI`;~%FIU%pnlQ=_?vr;ac-Yte`57R9-?dE36lTJJ-Cp8w7Y;WaO@%cc zBcf05=adn6vE*TytrPwnV;NvQM{DI@oe_fXCYE>}=rc15K7$x^+Clr`s};d=ip_fzVk_&EAOtf#4=f zs>{0L#>PE7QxhJsnBiMcPmS79Hw2v{$}atbTFTJSsgYGO54no~V4B@UKus?ad~85y z=~*fWLTIJ&xI@)?As_{?f*H!23#f);GZ}BOTOiwH<@-pGM)Xscc-p($G_%EQQodzW z{}Dz~KEP`| zyeUr>`6?F`{;cDID#nIrgu@QORQI|2>cI!q=$>^`>zfx6yEKHSIA9P8T?M3fJQX*S zI*G}I36dEcf}_Ozk=bQsX^98rz4H0K@dfJY{Qarpa_h&aQ%LziXnPZzY67vXih22W zGv~}^y+N|`(Kg8$NO@nlBg4C3y8p9zq7>lmum{y~w97mTzVw2747pJ5%WIlRU*#+l` zA6gcOs-#zYq9&RC9gpeAV63mJAMt%w{O(wqPkhkIhnI$bdF7iC)28=nO6L2jnsvvD z=?mP@V1uc#TlLXa-tL0#2AF)&BM{vZe(|0aJQ-${`ZJl6cSK%f=|MLtxUYI@yC95R zhD+{BY^nn@Mq=>AGt*C4*G_SQ;%|wCTbs$p9`!N7oMB88K1y-cx0V=V{LII6`-Axq}GJesL~??_OdvjH0{ z>Dsf3YLyO`{ZM#W>~GQqzB=Q#T|JpK<<7Es9D&xIZ|s9ql_$D;yi?lbfsY zLt1!SB4-pL#(hOZx;5o2n{g^Z$ORJUvC4?Ws-Hu4{1d5DIq384bQ_{6(IHO$` z2mJ#$_>Aw&?SxMOA7qvf$`%&60;-~U&?;e)O}bA_TZm#7Sum^QhYo}{rPqEkcXhOh z?VtbCJ|KJcb%)9qgI-_+S{i2ou6c z5Es4UkqLZcu=B8qP(Cbuw!`M?e?ObinoX)&PL!E-Twk@#L&xD{J-%|!;}Y=Ek0T%I z*_O>q_&-_#R9fa-^!rv7C7Z1>b%k|?-+PtTXEdHrFpMJPqD@W!?&t{cx9SvDgJZ! zQ%n`ddRJW1?6q>s^4QF4+;{TU#}5zf$GE86HDP+^B?RV_;XtD6qExXja->sH$jqja zpSyMQo7f|g0q*<>`vy@L$>j+JR(O#`)~41sjaIIxB(@kz($7)PW<5Qw%&Hs+Z%gK}^@qncheeb|y?H@oH&V zuFA0?&&IYH*PrkbNB>*6E+jf~n0mUqaN*r?Ec%>yF6Bs)l zV>s!1J8zu|vdE#!YF-_Co(Xmf50S$FoaWN=>KCt|3PyY-^L!1*6v#mk^f_1izT<6F z>K5131PBbdhfL@cUX6y5+a2;XtNd>E8S}pfZ854(+PnmGn#^qvR3of;Ct0I@%gwYb z1eL%4Iz!a6PQ6N8a+t)73|vTZP<`Sz^J<99orX9%o>Wd3Qt~U7jyohFtwYXg1_qUe zmRN0soq2 z9hJw?J-o`VpZ>h5{CJY3&{wJ!J0Ff4*3bMo(s`a*it5chuYAQu&)6K7OLw(pv$3h6 zn$SQ&9%v~yy=4-UY3*eTg1Lk3y(+h*umip9XKXHz*B@p~<1#|yO#hY5K2!sSoR22g zB32jvxaQWU{i*mFAfHd#Y|}2?2R(^}Q&-uRCbeJ%AFLn?%hdHn*x<)CV#yjp<`XmG z>kHAG(v$Mfu$isWCcsZ_LI&-6*gxfF7N&Twd#pa8ZtgYimb2hZz`8QX9(@V; zMyu)qufGS2tVEFCeDpKUh`(nmZcwVdJLCTE1KBE55jw{SZ~2*^6u+i}BB!(FqxE*M z>~0)zi!wDugLp(Gz-})kE_;16UVXR4bb7ZDQt)D4;Vxdq94Ae5SApcMQZ=ZuRC|uW zv|C0mjg)+aht~XZD2%q>bQiK5m&G2%VWTXhs&NxJ=yY0;NeXmBh;vJ;YQI2_)tmhJ zCgrvLKpT6urmaqYa)Mnl+9IoX#zLyd6+QR1&s3nnTzq?4|#DqQ@Kh$MoWZ zE+CB1i}v~$Bv-IxI!JY7H!n^;%X_3>f-R;qv;Se)3{c*hck%V+&+f?s1Byjwy3*2v zV3glU-oS3z<{WbN^!Fq>Ii#v3lIylpE_N31>f z4>CH-Y5PVOjHB|RaR1*5w`zVj(lK_1CTeL-*q)d5gt&dta+@-};mQoM{Jo!ikc!L3f@QzT#Qrf$5(Fx{Fw(&Fmg$ zbb@elZKswoRk75>Nl{2(o{911E<|#%T=nZlh4RRihmYxZa&z0>wi3RQ+yn_MCAWiM z0k9ho33IK!As9V~lQ&&RB=?hrRX(ch*E)W{-zKI2u$MzF|7kTvC9Gm3AL$pmVFu$B*LQZT(Fmx4GIC z>9|J@6t_Vd+jQT@?DwV4Bd8X?IWMH$^qCk-FMwJ0?BlTRk9GtaeVc#Fqv~VH7oef0 zssyS$3)O0W?v|S9KOaf&r9zFSa2b>yWfpJ;6Xa2F;WJqNyqUxC#C9`ViLnX4HALT* zJMf@%>CaGK+-jtnz(I?DX84gx$YDQQCQ6_a(RNN_Y6-x5E;v zYASW}=LU6Qw8~7+XTH3N`?&+ko6w2WQ3(bL6+JI?)XnG>uAi2)AIYEF1r_*k=A8lY zFuf|v(A&BjHJ@i*ET-Q@8Rvc)=lP;M9UabmQ?cF)!8I-~Uh=Nq{~N5(p~k?#Bn>R| zWxqpPm`;oG&XcDy8=Fr+8Xa7>8m*TG75}iVMM@6Zbwd;WphEH_a84X-pmG)#`3a?? zh9bu#52@t(;PJV<23=in`SXyGVT3diuYp1P__Lo$ZoS+<&;0L;?CKhMgM3>?d zd>ll03A2GN)s7&=J({JGHebe103RuxXEN_o@W{D{HWV-}N=|Fya+at%b5Q-5mRlO2 zyUZE4CxmZ^(FNpbEJis;U>x+m*g9#WAB<-gYzvh@fa2@1HwaS1<>g4?BJ>r6psz&o zh}q_`TFIP}4F_*D#JaW4ul6C&-0_gqS~VyStCfr%r0nFPmzZfG7kpep;1$0Yljnz4 zlZ;lFpOs_$`1I>u@6A#cJwhII!&3U)#bcaEXw};20fKh3-Zm({bQomsBd9I$F@U}J z2k9q3;N&M0;>i?dc^`XMFK6=TMitUdOlh)McZr$6(9f%6E?k1oiwpTGKK(Mlm5|y) zHYem!b^dn?B)kqhiEVwZp!&U;tUTOSyLW8Rg#gIgqlh{d;t|3px^B>h`o;4yD%GlW zuU9AU%SX>!ld|#!K`qOUBQ0A;T<6S4Vw=tD`f-u_4NY|^dpmXuyvNk$b4|vvyyuR7 zHyq%^4J1=&<5}1a&%31|=^t;NU+_h&f@RG)JNWz3Nwu`L1w7Wh?f;Jxv5CvbIfC1S zWHfAPGTfbp)xSv7-6MJkv72WsnIon`ry-aP^WhXd9T9FUa#Qi|<<4*m(90Z_Kjnp`bk$&`E{CLf?V#z-Oq;@-9seKglF> zHe-Ic!+w`b!ZVkE*Zmhmt8c^e-TK!s^Pp3gUVqVbXQ}dG$uZFx!C04}68etoE*)3~YS%Pt#+}UDjiT_Gi{VUi z6O7szkJM`r*`MC`n4URb^>1PdW--d%lfl#Tz=FA-?jZT3$=8qj8GhBNM31c)8@W($ z(<-^0))31U2<3L_UqGx|n^L#Rrom2!#w!ue($I-%0b=APufJHc@QpzuBpvnhWK2dp zJr6*KqWRb%I0U^ex#2cbcwiciLJ1s&h1n!3tCOv(a`l`VPAZb^NGNiEENL1+h3_)> zN7k?&aS)Ja@eoh!3sWDX>6i1azAImEogvxF4`xBjERu8TU{0ZXhm-}sv%U;-L~3+6ZE8Ue zh}Ozt`1f3mH_H&%CWZZtSZZkC>xoA4EE5Y~zW-I-cmA{ehHYz=Qbp}eTdS>AGq#@& zilRlSn5|8NnlV#tQM)KwN@`OhMIuH}VyhVhv8f#rGbHrMeZP7>pXV=lUgT|FUEk|E z&SRYp{3N4J;ahDj^_c?Sp8g6+XPCRz6~*)}(yS-7_xD;u%fqGJ^hGL$lNdAa5WY=Z z_@H!dT$PU;EBSoysY-5jpFl`rR6M?4enreGjRfhLmn7s?9yf_lIWGPg|E8A#9Gh z>{{G-V*2jfP@)Pq;@W3WDHoUUut`1c;!-NL=fW1!d$I&2bHRttIla)^W09I(Gbf)k zjSGgCg;>e-yE5!U42}nY(_i=41RL=U{>c;akmj^QKm|R~qIBAbShF^9a#7<}grmGB zwl@10coK2-Nx^S}VTPTX!Td&3x6#)lklwicqkiN__$3>E(t2l>y@)Zr&kfIXcaNkd z=d4=g+Qrq^w{dz5LO;tUEcOEYU3vMO1Z`csB=X1_kgU>KD+KkphW(h|_VqxTyC0j0 zSpoXk?sx8;yvX?>d6oh^lUp3j%OQGoKv)l@+E_?6Fp6Y|8gl%wD=DB(Fza$~*f*%K zB^v8(K;x7~bK1%rY=#)~hBpK( z#!{ZK#vr&~>ZWX5?H!|?_HKQ?-cv<=EJBB6PHQNmY;H44PT&E?dT3NlkJnlJiN=#H zb^gc?%f=2J$bU56qslgO!Y3Z4@F`nOc93fC>_lf$G$-Z zkLA9kXW%K5hU6pd9s+ArfCvM44AZGD61@i(@}%aaLSssKqSOAaHoyPt@}xa_As~KC zZw%DiyPjNta$9kDf2bPCOLFQ~Y2|Dt$kvs60xYxmjsMPKa*7^X@<^=+`4*Vcng)W;)l6J3$Z+g;7rz@&%3rWaFnTL4qv zr3hLzN#SurxC;+wTlLaIS(qWpv%i?Ub&NGW0`4AR{tUo;NmRfh23d?fkE(7vEI?_u zNG^M~z^Kg&7Xm=fCXr*;T&Ge~;hc~)Z*qXEZQTmV7hL%R&nLGX5X$>W%_?7fwC!BN zXe|W6`TS*G)GmbPf_K~MgkL}9=*zeh!7Wxca|^e#fiVtI^7G+7uDPCOja_rRobok2 z`_S~d7Y~mJy=F+YJ=GkBJ(BV79usnSP?r|w_R^kB+Dks}QQpfC5vJeL`XiL3e2N7T z7JWE%h!^NOJ5Dd{HZU?0G6BCfGZ6b|N#mRr8mKIk(h1|)%x>AOa9ghO##pqpH?@9v z{!J1p$V{d3oMyAPlU}xqrQ~%wN-ay?HLjXK0wu#Vyn>rO_Pm6KdrYH)W>~uhd|1E_ z4NwLM0S%)sBf$a*32J{oDcYo`y)0kP>%nAi=s4%_!BalX%X`4L%MpW;=+F-{Q|;vD zHiVR3Vk*I~4^i~&zuxr^V<CIqUWpn~qnwiw_}sxY+Ga^fNi{X)>~b#iT-j1v0>bl^J->fjv)6XT0oLsV zYPPA(6vOPW3e0EXC2vur;PU@}F=-M*6Ym(Jz7 z{?RZX_5>D4mrY|M#p)(+)u$*tEiY#bhQ~X7T@T&wdbdtk={QQ^2gh~-i}>>@s<%S( zihbbdkdP2Dj6VEtP_w+~)zl9rM3H2a!3tee;NA_-rkVDO@<6F+?< zlG1vRndbmhz6H>-vgto=4sU9Ck&D3X){93p6PW|JL(*-bK+a%BptD+vdIi#upffYSLdTVfXt|9B}Sn zd4_KtFe&|i^Z54ZWS#Aqli0|c$_uedI;1Dc9Hn;MZzzM%X7{fe>*uNIB&!??gNt1$ z?4i0xd!?QAp{Gu){AOF793^Y-GGxk3Zrv8v2|c}&cb6LTufkp=d(NFhkD6JTDISEN z((*Zdr_z}%`2nrY9!CBTeUf91!@~BuW#IKrE6cIUW6TzSz3aAMMTKD}+m0?(HEc5a z^R??d)U5t{!hxF$S$^Y~{~t|H%2kiE+>F-3S6VpfdgNQ_nkSk_RToEHKH9245nj_I zxG_D>l$eF~VIs-)n0|ZC#d=fo084hW7CA}Kv?tzH$@4;3XZ`!j3!MM9S5TyUJN%| zi>9$(IPnSg0t~v}CKbYXvsPe<%246(TO^|@uakYx(4rMfYgtzsMY_hv_U~9|XbC^< z6-Wb%KI{~&Ujzgcolqqo&<1b)7BGK1q;*j2FH@b|XU+Dn*!Ny?#w6s@=>_j*Bs)>= zeN3}TGFJcTc>aX&=#PV5SC;rhG4wlq(Go5H%3^`OyY6FqV?A8=l)rdb^o1G zv7On#PdNB|+XP}ehNBG#BJH7HF79VN4a>nHK;AkmsI3@XqXCGpel@98i^+ljTF5HHC23XPR%xHmJp8OG_PtdUmkxJMnhl^< z4ulIV>M($FfWHt#$&Zv*KevF&2yDz=_(N~H_&@tl6{b%K3SHV0zpXT#Lzf#Nv=lHQ z>*FCSmq(TZ>n=ifS;ZOkg%myVRTCrjJh7wa-u>o-%x+`fRz|AUrKFK$b-}JfrxRCI zZ4I2@uuK~=`j5%^s)gg}^&h(qODlZkW~IVb0+Bjc3WtGN_hiz`3BUSGqjWpiB)POO zvxQ*scF^!rJ;`-N|cxpJe;mm|GLy$vCUhQ_wCVPyp#VRK9; z(oJi@;aV)oeVn*dz!SHF`$gq$q8_VGtzLgxmBJs5Y2O78t#<-_V#Nsfu5Be`O<$Qn zjZ)?GJY$l|bw@{*iAL&ImnT9>SR%31EM`J@(WULB)om=W+j{e6(boJpS0)gzj*8+i zLh_#F7c+aCZ1cjT^+L;{qLVfIwXDCV%7xSOlSuOk?OWE?bKzP|weYPyG*6F z0kfT~V+kTN90sj~R%(SIwEaZAHNSAGBo|cXaYi|d+z`F;sJ1)4|F0hX()<6a)XEL! zL(h*X)qZ-(uk@fYS0;*K0UC(p&uKiiV``S-_cs=(Y4I?FJ zl%i3*Z7&RMAG0Ir9Xz|h)M0*1mee=a#=1rKlfT%23B@5KFD}4;KcHvF$gl99KqC;CtFLDjFUN(V+jrbr8oHN3|^UitK@6J~>)*(ey3Q)h7T%zt#!u6H8iVZfrfM?5+lqucGzO4>GCQtA~b8pyRabS25u*=UqWck zg>U#((z}zTRfPY)5b zolK{mut**fG#`vbFR+S@?Y;3XZEB7}V83}Mrq<%^Mk9AFuO51%{z-k(T+rR=$C@es-LYmxxTb74ggb#q+ar9R6y~#- zWMf6B`df51d-gjcP$2Hd%R-~DH%(=BG^$s|rw`-?~EKHnnRHw=+U-g5Y#mmUvHM}$&ai@*F0Yecr65hODO=QV+0P69^Fk$Nx#f}y;#CWR*bMAb zwX(G`lbYz{%oRjZmS=}m-zy3FNN>fw_l?l&6uVK%Ric6%8_B7|6|LuGAYBdDXffW6 z92NZ3ck`hBuI+nnU|*4qjunVnRy9$<7sDSR^qLxl;Xo3o+{SY< zQj-At^m+mXA6J6i4aa#|1EfDcu@zJlrNw6-ROzf-jnR}V-^NhY@dNkcrnXq0fV-fz zN#JV5ERE**;EkpKL?q}TsC&ccwi)Jd`UO@12}0J@6qoq9Q*hExe%bO#Kk*dwcc&YY z5R72URdznp(cV66eDP~)`d(ToBvhqtwfRIGR3ARutb`ypC2_Mdher}v%0ntP)d5j~ z5v&%NMUVsf%nwbAv+IAQ{pQkyf!-zLWfmPl0@8uj8xA2^m%bb~`QFNSmzn`A^Z}$B z0kSS%Y5=iZidayg+3+&i?nzjwV$q2eVMejE3L__d*uxrh;QYs17f;&d%QOFrdLHRI zL$m(VbT}X;N=u+2$bu#QxOn+?3 zR2dZ4r;8aLZ(sX^9y+*IfqxU-J}h_;l|vvOzfD-S`uM7KOc}2m*eA1=`^H;S$G84* z;q`9$P{yYj_6jt8U1pPQ?sE8)xJX5!&oL`ig=ggcARBwEQ}0dICO^TW<}MrtQ{;kX zSsI2zw^0DHz3z`WQs|ijqn&9)vB&6U%$>l^S2f^FQQ_5kwlDhkOeK9+KVNFUe9QvG zA233^^x~g4=cbbda4$xI|7dg+pwSSQDveHIwoyb5jK3cGwrrENDk3T1!<}S?T{A{) zh^k9eDg!THpP-X+F`;yc1-dvqqX1zVToG{BG%og}$C@N>{|Bf?w&%aLf2?@tsZ@a2 z;-RLcCImrOoRpLs16`s={Hhz3Jv0JCEff@blxMB3b)oXp-rt1c-GcNDBQwZ`U~la5 zd@+%oymW}2q0GeMKzz!RaWYx`FDbRHy6gc?4N)gG$%+=aSC+9x!KdF$)QX=>Bw{=b zEghQYFSfA7u!xI;4hST9w8rR)(g)MLmxw>4%YYQT|YkhXf$+0me(Z;7KzSzdQm7Scv$hSBW zs6`*azV{>|aBlS==OiuekF|&dkGsA6_sQqSZ|f_F7j5Q9HbEErfI>0&U;DrHKrNra zu$@zo)4(B;5Da-&Trv0;xYasg6aL9kxC>l|1uB1>2PIKE&RP}*gz0b zEKQ)S|HchKnr1C;g)?n#LyuZDw3Dg}Yl>Fcfwhm(OMkm_%1917zSAaW7AiH(LSQ^ad_9*DzZIoWoOIjb0bUeXNDfZ{m@)yn`4*JfC?DfM$htfa}pBWq&!qr zwn=>tL)>3~7?cp}oGR zCg8t+Mz*R3KU`jmBU)LI;(kKu^^3su2kqH)Tk5FL#I!Co8{?v)E0_4*$VR$uMEC={ zo~PG3K%o@c_my@0H`c!77{MB6G&T{Znc4V}Q(z1FIQSn;D#wccdHgW-?y;m&b-|0l zafem3?F(F02m#WYU1d2I1me42{q_CR_#nCRF8|S>yjDo+j;p*M8o?Fe^pB=kw6_9B zoz)^lO~-!_;rpBFGHW#+uddhg%-@auGbY3O{YqYlPpUQcqfEE%3G4<;m*hlzfNocp zi&GNvLBAzI(PsoCz_RU4n1SUJp=?P53zhqJwfFsdmdlm-N(Oq&%dJ@Nm|KN{wqO$h zn}HE2VL2W40y!&r=~wdDN3*v~8TtaoW~OTh2cEZV6Iy;a&LnevckxfAS8#14T#eOy zzOwgm#7kR*a4;C4ER}1y-A?>CCRd1zD~T6(77*&me2CZdZXW_4Tp}B{bJuuX*JegI z(6|2>TpInMb(o0vl1>o&IJ>B_zfl@AFOO+?)CR951yTP64Cwd_4tq?%?_jrI1%^_K zwZ+Puz8Hw!qm(ENyi*+x#UFoZI>B#*50BT(_?1AVjOOi%vTRC&;07=)mbR6vQP>wG zxrk*UQ*&`ea?15cF#=aQyFmziKY7Cx@`IdCjPl=Yu1MjR`xUT1OvQ zg^u>;G)tfPO|!g+;h+_z)rxpm4IEMkPN14q!Wtvg$36BZ5+<5KyA^>GyL)wE(AV1z zR}T}aY&wM*Q#$N<5rj@|EB?sbijIWy+NS81pTp||H#Pbd>HG5-dYp#Q%DXeAcrCia z(E6IIA3!@4NQDPv8jvS$b9xQ#!VO3T9WOaMKLdELRI9IdcvcLtdwuS?i(YouGh+LD zQZ>I9p<#~`9l>j}M^qS-Jc7?FkQW>p)0M{&IXX_$exJRmS?77T$er*O`Me`6>EW@f z!wpuxIeSvf8ty4!zWVH;N=~6sncdU=Kbq((Z>~vjhIf7H6Gf`cGm?l!Bp2VPt zsNW;U}U`}jMR$&0Xx za8|+zI!~I!N;opttJRiFd6sUO_7)`aEy9ia$1mH*Qfvo5i#Biunzv}I`d@6@GoAnN z4(E&%6ad|FrTPmD_CEGwRqY-Kl>SLgty?`cu%*l{t|VGL=F^io_(uaQ?EO=Qtuhft z2kf=fC&CZC2iEgvftBfK4|raB&O2c4Dl_B zjq2Vyty{*vr-Hb+JaNL>T#d)c*kcwGf0p1!{=3npEA~+<4l@bB$;zWUD0%Mu#pYa7 z+lEP)_u)BESGsHT`R&>GSkHTBpAClSeA~FR#dNx4LeapcTCz%6HVBb>K4842d`Of) z`!3u;D}3x-7YIG`09yPN&~IBm7fq4NPb_|6Qajouv*#t2mDzfLX)R&>zpVxvZHO#t zbRt=2n+Q2Hv&sQ`E^9cjZJPgCRxNCfo5K~9`5I6vn$C-k&NaG31W|TBHvy@h{DBi= zD8PM{)zok}-_!gXX;k33(p(6{h5XdsCnqTfkc6x2 zmd`$euB^pUXBJj)>-N%FIAA4-!H1BpKNE|EN1JI`RJQ8v9})&KIawGcIKTGc3M?9M}X+>;>iFl>NJPaoq7{Ai@R5mCdQ#9AIJvoGhQSs%W=(1ETzlTTHGn!(#^Bl zty(louSKPdc!L>lnbudg!=HtBbjg2)6$?LgVhq37Cmq>U=56oI1!?GfaOO?azU}*@MPjcw?_n5y-Xp7UnZ4J&d ziZC@Em&wm#ViFNkN8N9xdpV=$$P2j^g-ekixlkbm8B27t@`h`}iH|R!VQ)h~5p)MH z>-dTetR-LQamZ3uRDWyex4gVibU#BTX+BGG;fW!7~=y!$o<;I`NJg$4A3wG=$1*f1EJV?fWN?U>=M}S5Lvc_lc zZ`2$3fR;e&Po!nKx5G5ZSc?Uo5*0-`O?qneiQEjwW6YsVO01LVFT^h%`)gy>)pV6o zBDsp)U8@m4yo0n}?uU(2=^Td}J4eGew(6U@P(1wj4%_)ck4cQEf^6?Nqf_`jxqg}* zt&pE>{E>hy2!APIwJE7GsPqX&wAM#jpM3gG_|1Zt2VCviqwt?jwB_aHX5R}=10wU> z^jMja@-3kV&*w%~RBkFmCVMvzxm~_?v@3$>p`=9e{iCU|c}m^vp!Ywp)?rR^BgTd_50{yUOy zG(3bTfcD)8dW-@PM)o|aMu%||GP%`y`s^2nN$4y7W35(*#;0T}g>8I;oe#MJTo@Lh zXS3^n+?RjuqqMROHp9HY+jY~DCwWW`R^beU-Hf*cOo!EdcY9`zxh?Ds&l6$gdS)5Q zt8~xJozvMDfV&o_$d&n4KgFdyYCM=9*0MJe6@BAwlV&KAuq*ug*uKF2CbPGRzH?lv|cUwhHiF?P%TuS%Nlv$d5 zf992__o{EMpSHPg%W>IkALpqPoVCSkCX`FCPRW?(Y3=w@_srY*6E=Qt9`lOUs0|wn zaU2(5K&@g9q&6!8b9K3IebBMGlKcYiP`aSu3v0gR^svR0@9%U41FL2?;b}pqp?S8x zQ)MRpxd0iJHDFuN%>15PSaI=5BNc~sh%S|o$fZEjB`t{stMvAYu!v#-vw$kam-n+4 z{Mpmk>CAGX7I{Q~OFACL5*-+{HUo-ZC$g&dnJ4Z4>Hsx|@Zu+tk`xAw}y=v`4#*P#{5`ud3 zGHlFq682)OIxYk;-YzJc;MTC|;=KQ8H4OVvj*c`}SIpHv`=*(mVR|vF(Ovp%hJFdP zh9_Qd*m2HWg^cB3a~{ZkiHN&+@{gvSl}u{~*?bI+2;J+JRm6Z7WB@R@xE_)pC=VZfOHKrZRYWe20%F|S_q=jne_gkQy4SZT8(9?--JtG z=u#|S_f-+g4IQECH<7A#;ZG+5z8)Ey7`QdYOsb*27JM5i0_9dU=rR1>3QEl0Jx+zc zpzbDV8`HL4+*Z$UUYwbzKvcqZi`@`NGX%T_fPY-(juhcTR0z>N#RLoj45mw>$MB-{ z$<|K1nGecnW}RgfH-2hZNfOYuDY`@u3Rd7Ax>I)-`Cf$WzRjJYaJo$6q4MbUyHTP=`IeYgF;Xr!0LoD&6h3q~u5*d)pYX1VeBE0-7QrkU<8UkW;L^X|~ z1kZLt*HgL67w>SrSw^y{yl(s-_Gao{)q$h)Vf&isd2!*$(J-A(3`*3ikOb+#2aR|D zcxvsG+nXMwf0MW=nBkO2b4iQUu$$=9ZOnbJbiV$9a%*vdP?A+890pRQ=7OWJL^m)|H7q?!J^zZQBZ0WtEvz9c|8Bx3g zyQa-D(=h9K=>wu(p3=Gg13*{s&1g0umwi&8GmC&5pJ=NWzu8CgwF{)y(~jiZl?5Ak zH;6{V2Eq~~>4xb)(W2_Mkt`#wV;B4{?KXrjQP)#xO8LAkn;rl$5Sdr;f{kI0KMemE zmhc9%4s#LCyHNFYeOHJmMZGRzHO>kt+V>1v3|bX)l{)S)Mpn`T7{VcD%ya%Gp(NMq zm0S4CK^I2|T{ghoQe>~@jrSoRjEl;fzg@NqGn?4$V5)ksX4Cl5>6);<^c0^epTtji zzF4Dmvh_o@2mZ(9MPx+F{G?BzmG>O$t8;3<%IYs_6xR{-@E?uA%>TY$cGUN4NiLoh zQn`RGna;g;#-@x0+3hjW*`B3$g>bn@k{e)vGokT + + + Exe + net8.0 + enable + enable + false + + + + + + + + + + + + diff --git a/sandbox/AutoQuery.Benchmark/BenchmarkConfig.cs b/sandbox/AutoQuery.Benchmark/BenchmarkConfig.cs new file mode 100644 index 0000000..eecfcad --- /dev/null +++ b/sandbox/AutoQuery.Benchmark/BenchmarkConfig.cs @@ -0,0 +1,18 @@ +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Jobs; + +namespace AutoQuery.Benchmark; + +internal class BenchmarkConfig : ManualConfig +{ + public BenchmarkConfig() + { + AddDiagnoser(MemoryDiagnoser.Default); + AddJob(Job.ShortRun + .WithWarmupCount(1) + .WithIterationCount(1) + .WithRuntime(CoreRuntime.Core80)); + } +} diff --git a/sandbox/AutoQuery.Benchmark/Benchmarks/QueryPerformance.cs b/sandbox/AutoQuery.Benchmark/Benchmarks/QueryPerformance.cs new file mode 100644 index 0000000..68ee312 --- /dev/null +++ b/sandbox/AutoQuery.Benchmark/Benchmarks/QueryPerformance.cs @@ -0,0 +1,80 @@ +using AutoQuery.Abstractions; +using AutoQuery.Extensions; +using BenchmarkDotNet.Attributes; +using System.Linq.Dynamic.Core; +using System.Reflection; + +namespace AutoQuery.Benchmark.Benchmarks; + +[Config(typeof(BenchmarkConfig))] +public class QueryPerformance +{ + private List _autoQueryTestData = null!; + private List _dynamicLinqTestData = null!; + private QueryProcessor _queryProcessor = null!; + + [GlobalSetup] + public void Setup() + { + _queryProcessor = new QueryProcessor(); + _queryProcessor.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); + _autoQueryTestData = Enumerable.Range(1, 10000) + .Select(i => new TestData { Id = i, Name = $"Name{i}", Age = i % 100 }) + .ToList(); + _dynamicLinqTestData = Enumerable.Range(1, 10000) + .Select(i => new TestData { Id = i, Name = $"Name{i}", Age = i % 100 }) + .ToList(); + } + + [Benchmark] + public void FilterSortSelectPageWithAutoQuery() + { + var queryOptions = new TestQueryOptions + { + Name = "Name5000", + Sort = "Age", + Fields = "Id,Name", + Page = 1, + PageSize = 10 + }; + + _autoQueryTestData.AsQueryable().ApplyQueryPaged(_queryProcessor, queryOptions); + } + + [Benchmark] + public void FilterSortSelectPageWithDynamicLinq() + { + _dynamicLinqTestData.AsQueryable() + .Where("Name == @0", "Name5000") + .OrderBy("Age") + .Select("new (Id, Name)") + .Skip(0) + .Take(10) + .ToDynamicList(); + } + + public class UserQueryConfiguration : IFilterQueryConfiguration + { + public void Configure(FilterQueryBuilder builder) + { + builder.Property(q => q.Name, d => d.Name) + .HasEqual(); + } + } + + public class TestData + { + public int Id { get; set; } + public string Name { get; set; } = null!; + public int Age { get; set; } + } + + public class TestQueryOptions : IQueryPagedOptions + { + public string? Name { get; set; } + public string? Fields { get; set; } + public string? Sort { get; set; } + public int? Page { get; set; } + public int? PageSize { get; set; } + } +} diff --git a/sandbox/AutoQuery.Benchmark/Program.cs b/sandbox/AutoQuery.Benchmark/Program.cs new file mode 100644 index 0000000..7f18f99 --- /dev/null +++ b/sandbox/AutoQuery.Benchmark/Program.cs @@ -0,0 +1,4 @@ +using BenchmarkDotNet.Running; +using System.Reflection; + +BenchmarkSwitcher.FromAssembly(Assembly.GetEntryAssembly()!).Run(args); \ No newline at end of file