SourceGeneratorBenchmarks.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. using Apq.Cfg.DependencyInjection;
  2. using BenchmarkDotNet.Attributes;
  3. namespace Apq.Cfg.Benchmarks;
  4. /// <summary>
  5. /// 源生成器绑定性能测试
  6. /// 对比源生成器(零反射)与 ObjectBinder(反射)的性能差异
  7. /// </summary>
  8. [MemoryDiagnoser]
  9. [RankColumn]
  10. public class SourceGeneratorBenchmarks
  11. {
  12. private string _testDir = null!;
  13. private ICfgRoot _cfg = null!;
  14. private ICfgSection _simpleSection = null!;
  15. private ICfgSection _nestedSection = null!;
  16. private ICfgSection _arraySection = null!;
  17. private ICfgSection _dictionarySection = null!;
  18. private ICfgSection _complexSection = null!;
  19. [GlobalSetup]
  20. public void Setup()
  21. {
  22. _testDir = Path.Combine(Path.GetTempPath(), $"ApqCfgSgBench_{Guid.NewGuid():N}");
  23. Directory.CreateDirectory(_testDir);
  24. var jsonPath = Path.Combine(_testDir, "config.json");
  25. File.WriteAllText(jsonPath, """
  26. {
  27. "Simple": {
  28. "Name": "TestApp",
  29. "Port": 8080,
  30. "Enabled": true,
  31. "Timeout": 30.5,
  32. "Id": "550e8400-e29b-41d4-a716-446655440000"
  33. },
  34. "Nested": {
  35. "Name": "ParentApp",
  36. "Database": {
  37. "Host": "localhost",
  38. "Port": 5432,
  39. "Name": "testdb"
  40. },
  41. "Cache": {
  42. "Host": "redis.local",
  43. "Port": 6379
  44. }
  45. },
  46. "Array": {
  47. "Tags": {
  48. "0": "tag1",
  49. "1": "tag2",
  50. "2": "tag3",
  51. "3": "tag4",
  52. "4": "tag5"
  53. },
  54. "Ports": {
  55. "0": 80,
  56. "1": 443,
  57. "2": 8080,
  58. "3": 8443
  59. }
  60. },
  61. "Dictionary": {
  62. "Settings": {
  63. "Key1": "Value1",
  64. "Key2": "Value2",
  65. "Key3": "Value3",
  66. "Key4": "Value4",
  67. "Key5": "Value5"
  68. }
  69. },
  70. "Complex": {
  71. "Name": "ComplexApp",
  72. "Endpoints": {
  73. "0": {
  74. "Host": "api1.local",
  75. "Port": 8001
  76. },
  77. "1": {
  78. "Host": "api2.local",
  79. "Port": 8002
  80. },
  81. "2": {
  82. "Host": "api3.local",
  83. "Port": 8003
  84. }
  85. },
  86. "Metadata": {
  87. "version": "1.0.0",
  88. "author": "test",
  89. "description": "A complex configuration"
  90. }
  91. }
  92. }
  93. """);
  94. _cfg = new CfgBuilder()
  95. .AddJson(jsonPath, level: 0, writeable: false)
  96. .Build();
  97. _simpleSection = _cfg.GetSection("Simple");
  98. _nestedSection = _cfg.GetSection("Nested");
  99. _arraySection = _cfg.GetSection("Array");
  100. _dictionarySection = _cfg.GetSection("Dictionary");
  101. _complexSection = _cfg.GetSection("Complex");
  102. }
  103. [GlobalCleanup]
  104. public void Cleanup()
  105. {
  106. (_cfg as IDisposable)?.Dispose();
  107. if (Directory.Exists(_testDir))
  108. {
  109. try { Directory.Delete(_testDir, true); }
  110. catch { }
  111. }
  112. }
  113. // ========== 简单类型绑定对比 ==========
  114. [Benchmark(Description = "SourceGen_SimpleTypes")]
  115. [BenchmarkCategory("Simple")]
  116. public SgSimpleOptions SourceGen_SimpleTypes()
  117. {
  118. return SgSimpleOptions.BindFrom(_simpleSection);
  119. }
  120. [Benchmark(Description = "Reflection_SimpleTypes")]
  121. [BenchmarkCategory("Simple")]
  122. public ReflectionSimpleOptions Reflection_SimpleTypes()
  123. {
  124. var options = new ReflectionSimpleOptions();
  125. ObjectBinder.BindSection(_simpleSection, options);
  126. return options;
  127. }
  128. [Benchmark(Description = "SourceGen_SimpleTypes_100")]
  129. [BenchmarkCategory("Simple")]
  130. public SgSimpleOptions SourceGen_SimpleTypes_100()
  131. {
  132. SgSimpleOptions options = null!;
  133. for (int i = 0; i < 100; i++)
  134. {
  135. options = SgSimpleOptions.BindFrom(_simpleSection);
  136. }
  137. return options;
  138. }
  139. [Benchmark(Description = "Reflection_SimpleTypes_100")]
  140. [BenchmarkCategory("Simple")]
  141. public ReflectionSimpleOptions Reflection_SimpleTypes_100()
  142. {
  143. ReflectionSimpleOptions options = null!;
  144. for (int i = 0; i < 100; i++)
  145. {
  146. options = new ReflectionSimpleOptions();
  147. ObjectBinder.BindSection(_simpleSection, options);
  148. }
  149. return options;
  150. }
  151. // ========== 嵌套对象绑定对比 ==========
  152. [Benchmark(Description = "SourceGen_NestedObject")]
  153. [BenchmarkCategory("Nested")]
  154. public SgNestedOptions SourceGen_NestedObject()
  155. {
  156. return SgNestedOptions.BindFrom(_nestedSection);
  157. }
  158. [Benchmark(Description = "Reflection_NestedObject")]
  159. [BenchmarkCategory("Nested")]
  160. public ReflectionNestedOptions Reflection_NestedObject()
  161. {
  162. var options = new ReflectionNestedOptions();
  163. ObjectBinder.BindSection(_nestedSection, options);
  164. return options;
  165. }
  166. [Benchmark(Description = "SourceGen_NestedObject_100")]
  167. [BenchmarkCategory("Nested")]
  168. public SgNestedOptions SourceGen_NestedObject_100()
  169. {
  170. SgNestedOptions options = null!;
  171. for (int i = 0; i < 100; i++)
  172. {
  173. options = SgNestedOptions.BindFrom(_nestedSection);
  174. }
  175. return options;
  176. }
  177. [Benchmark(Description = "Reflection_NestedObject_100")]
  178. [BenchmarkCategory("Nested")]
  179. public ReflectionNestedOptions Reflection_NestedObject_100()
  180. {
  181. ReflectionNestedOptions options = null!;
  182. for (int i = 0; i < 100; i++)
  183. {
  184. options = new ReflectionNestedOptions();
  185. ObjectBinder.BindSection(_nestedSection, options);
  186. }
  187. return options;
  188. }
  189. // ========== 数组/列表绑定对比 ==========
  190. [Benchmark(Description = "SourceGen_Array")]
  191. [BenchmarkCategory("Array")]
  192. public SgArrayOptions SourceGen_Array()
  193. {
  194. return SgArrayOptions.BindFrom(_arraySection);
  195. }
  196. [Benchmark(Description = "Reflection_Array")]
  197. [BenchmarkCategory("Array")]
  198. public ReflectionArrayOptions Reflection_Array()
  199. {
  200. var options = new ReflectionArrayOptions();
  201. ObjectBinder.BindSection(_arraySection, options);
  202. return options;
  203. }
  204. [Benchmark(Description = "SourceGen_Array_100")]
  205. [BenchmarkCategory("Array")]
  206. public SgArrayOptions SourceGen_Array_100()
  207. {
  208. SgArrayOptions options = null!;
  209. for (int i = 0; i < 100; i++)
  210. {
  211. options = SgArrayOptions.BindFrom(_arraySection);
  212. }
  213. return options;
  214. }
  215. [Benchmark(Description = "Reflection_Array_100")]
  216. [BenchmarkCategory("Array")]
  217. public ReflectionArrayOptions Reflection_Array_100()
  218. {
  219. ReflectionArrayOptions options = null!;
  220. for (int i = 0; i < 100; i++)
  221. {
  222. options = new ReflectionArrayOptions();
  223. ObjectBinder.BindSection(_arraySection, options);
  224. }
  225. return options;
  226. }
  227. // ========== 字典绑定对比 ==========
  228. [Benchmark(Description = "SourceGen_Dictionary")]
  229. [BenchmarkCategory("Dictionary")]
  230. public SgDictionaryOptions SourceGen_Dictionary()
  231. {
  232. return SgDictionaryOptions.BindFrom(_dictionarySection);
  233. }
  234. [Benchmark(Description = "Reflection_Dictionary")]
  235. [BenchmarkCategory("Dictionary")]
  236. public ReflectionDictionaryOptions Reflection_Dictionary()
  237. {
  238. var options = new ReflectionDictionaryOptions();
  239. ObjectBinder.BindSection(_dictionarySection, options);
  240. return options;
  241. }
  242. [Benchmark(Description = "SourceGen_Dictionary_100")]
  243. [BenchmarkCategory("Dictionary")]
  244. public SgDictionaryOptions SourceGen_Dictionary_100()
  245. {
  246. SgDictionaryOptions options = null!;
  247. for (int i = 0; i < 100; i++)
  248. {
  249. options = SgDictionaryOptions.BindFrom(_dictionarySection);
  250. }
  251. return options;
  252. }
  253. [Benchmark(Description = "Reflection_Dictionary_100")]
  254. [BenchmarkCategory("Dictionary")]
  255. public ReflectionDictionaryOptions Reflection_Dictionary_100()
  256. {
  257. ReflectionDictionaryOptions options = null!;
  258. for (int i = 0; i < 100; i++)
  259. {
  260. options = new ReflectionDictionaryOptions();
  261. ObjectBinder.BindSection(_dictionarySection, options);
  262. }
  263. return options;
  264. }
  265. // ========== 复杂对象绑定对比 ==========
  266. [Benchmark(Description = "SourceGen_ComplexObject")]
  267. [BenchmarkCategory("Complex")]
  268. public SgComplexOptions SourceGen_ComplexObject()
  269. {
  270. return SgComplexOptions.BindFrom(_complexSection);
  271. }
  272. [Benchmark(Description = "Reflection_ComplexObject")]
  273. [BenchmarkCategory("Complex")]
  274. public ReflectionComplexOptions Reflection_ComplexObject()
  275. {
  276. var options = new ReflectionComplexOptions();
  277. ObjectBinder.BindSection(_complexSection, options);
  278. return options;
  279. }
  280. [Benchmark(Description = "SourceGen_ComplexObject_100")]
  281. [BenchmarkCategory("Complex")]
  282. public SgComplexOptions SourceGen_ComplexObject_100()
  283. {
  284. SgComplexOptions options = null!;
  285. for (int i = 0; i < 100; i++)
  286. {
  287. options = SgComplexOptions.BindFrom(_complexSection);
  288. }
  289. return options;
  290. }
  291. [Benchmark(Description = "Reflection_ComplexObject_100")]
  292. [BenchmarkCategory("Complex")]
  293. public ReflectionComplexOptions Reflection_ComplexObject_100()
  294. {
  295. ReflectionComplexOptions options = null!;
  296. for (int i = 0; i < 100; i++)
  297. {
  298. options = new ReflectionComplexOptions();
  299. ObjectBinder.BindSection(_complexSection, options);
  300. }
  301. return options;
  302. }
  303. // ========== BindTo 对比 ==========
  304. [Benchmark(Description = "SourceGen_BindTo")]
  305. [BenchmarkCategory("BindTo")]
  306. public SgSimpleOptions SourceGen_BindTo()
  307. {
  308. var options = new SgSimpleOptions();
  309. SgSimpleOptions.BindTo(_simpleSection, options);
  310. return options;
  311. }
  312. [Benchmark(Description = "Reflection_BindTo")]
  313. [BenchmarkCategory("BindTo")]
  314. public ReflectionSimpleOptions Reflection_BindTo()
  315. {
  316. var options = new ReflectionSimpleOptions();
  317. ObjectBinder.BindSection(_simpleSection, options);
  318. return options;
  319. }
  320. }
  321. // ========== 源生成器配置类(使用 [CfgSection] 特性) ==========
  322. [CfgSection]
  323. public partial class SgSimpleOptions
  324. {
  325. public string? Name { get; set; }
  326. public int Port { get; set; }
  327. public bool Enabled { get; set; }
  328. public double Timeout { get; set; }
  329. public Guid Id { get; set; }
  330. }
  331. [CfgSection]
  332. public partial class SgNestedOptions
  333. {
  334. public string? Name { get; set; }
  335. public SgDatabaseOptions? Database { get; set; }
  336. public SgCacheOptions? Cache { get; set; }
  337. }
  338. [CfgSection]
  339. public partial class SgDatabaseOptions
  340. {
  341. public string? Host { get; set; }
  342. public int Port { get; set; }
  343. public string? Name { get; set; }
  344. }
  345. [CfgSection]
  346. public partial class SgCacheOptions
  347. {
  348. public string? Host { get; set; }
  349. public int Port { get; set; }
  350. }
  351. [CfgSection]
  352. public partial class SgArrayOptions
  353. {
  354. public string[]? Tags { get; set; }
  355. public List<int>? Ports { get; set; }
  356. }
  357. [CfgSection]
  358. public partial class SgDictionaryOptions
  359. {
  360. public Dictionary<string, string>? Settings { get; set; }
  361. }
  362. [CfgSection]
  363. public partial class SgComplexOptions
  364. {
  365. public string? Name { get; set; }
  366. public List<SgEndpointOptions>? Endpoints { get; set; }
  367. public Dictionary<string, string>? Metadata { get; set; }
  368. }
  369. [CfgSection]
  370. public partial class SgEndpointOptions
  371. {
  372. public string? Host { get; set; }
  373. public int Port { get; set; }
  374. }
  375. // ========== 反射绑定配置类(用于对比) ==========
  376. public class ReflectionSimpleOptions
  377. {
  378. public string? Name { get; set; }
  379. public int Port { get; set; }
  380. public bool Enabled { get; set; }
  381. public double Timeout { get; set; }
  382. public Guid Id { get; set; }
  383. }
  384. public class ReflectionNestedOptions
  385. {
  386. public string? Name { get; set; }
  387. public ReflectionDatabaseOptions? Database { get; set; }
  388. public ReflectionCacheOptions? Cache { get; set; }
  389. }
  390. public class ReflectionDatabaseOptions
  391. {
  392. public string? Host { get; set; }
  393. public int Port { get; set; }
  394. public string? Name { get; set; }
  395. }
  396. public class ReflectionCacheOptions
  397. {
  398. public string? Host { get; set; }
  399. public int Port { get; set; }
  400. }
  401. public class ReflectionArrayOptions
  402. {
  403. public string[]? Tags { get; set; }
  404. public List<int>? Ports { get; set; }
  405. }
  406. public class ReflectionDictionaryOptions
  407. {
  408. public Dictionary<string, string>? Settings { get; set; }
  409. }
  410. public class ReflectionComplexOptions
  411. {
  412. public string? Name { get; set; }
  413. public List<ReflectionEndpointOptions>? Endpoints { get; set; }
  414. public Dictionary<string, string>? Metadata { get; set; }
  415. }
  416. public class ReflectionEndpointOptions
  417. {
  418. public string? Host { get; set; }
  419. public int Port { get; set; }
  420. }