Skip to content

Commit fd3818c

Browse files
committed
v3.0.2 improve perf on AnnualView, improve render on WeekView. imrove sample wasm
1 parent 514d7b4 commit fd3818c

File tree

126 files changed

+120209
-231
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

126 files changed

+120209
-231
lines changed

Blazor-Calendar.sln

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio Version 17
4-
VisualStudioVersion = 17.0.31710.8
3+
# Visual Studio Version 18
4+
VisualStudioVersion = 18.3.11408.92 d18.3
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorCalendar", "BlazorCalendar\BlazorCalendar.csproj", "{D018189F-E4BE-4693-9BF0-FE8325CB0899}"
77
EndProject
@@ -11,6 +11,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorServer", "samples\Bla
1111
EndProject
1212
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorWebAssembly", "samples\BlazorWebAssembly\BlazorWebAssembly.csproj", "{5D3171BB-C17D-462D-AECE-3C8DEAC85441}"
1313
EndProject
14+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Benchmarks", "Benchmarks", "{C9CF3B12-5BCB-406A-B2B6-A6701652DACA}"
15+
EndProject
16+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorCalendar.Benchmarks", "BlazorCalendar.Benchmarks\BlazorCalendar.Benchmarks.csproj", "{33B127B9-2341-493B-A0FD-05EB7E1DA664}"
17+
EndProject
1418
Global
1519
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1620
Debug|Any CPU = Debug|Any CPU
@@ -29,13 +33,18 @@ Global
2933
{5D3171BB-C17D-462D-AECE-3C8DEAC85441}.Debug|Any CPU.Build.0 = Debug|Any CPU
3034
{5D3171BB-C17D-462D-AECE-3C8DEAC85441}.Release|Any CPU.ActiveCfg = Release|Any CPU
3135
{5D3171BB-C17D-462D-AECE-3C8DEAC85441}.Release|Any CPU.Build.0 = Release|Any CPU
36+
{33B127B9-2341-493B-A0FD-05EB7E1DA664}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37+
{33B127B9-2341-493B-A0FD-05EB7E1DA664}.Debug|Any CPU.Build.0 = Debug|Any CPU
38+
{33B127B9-2341-493B-A0FD-05EB7E1DA664}.Release|Any CPU.ActiveCfg = Release|Any CPU
39+
{33B127B9-2341-493B-A0FD-05EB7E1DA664}.Release|Any CPU.Build.0 = Release|Any CPU
3240
EndGlobalSection
3341
GlobalSection(SolutionProperties) = preSolution
3442
HideSolutionNode = FALSE
3543
EndGlobalSection
3644
GlobalSection(NestedProjects) = preSolution
3745
{C61DD137-FEBF-4644-8098-EC4263AF3967} = {9A175C09-D84D-4734-9E6E-00C89C09AA2A}
3846
{5D3171BB-C17D-462D-AECE-3C8DEAC85441} = {9A175C09-D84D-4734-9E6E-00C89C09AA2A}
47+
{33B127B9-2341-493B-A0FD-05EB7E1DA664} = {C9CF3B12-5BCB-406A-B2B6-A6701652DACA}
3948
EndGlobalSection
4049
GlobalSection(ExtensibilityGlobals) = postSolution
4150
SolutionGuid = {B68E7280-0A03-4BD1-B3B5-AB2A9B68195D}
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using BenchmarkDotNet.Attributes;
4+
using Microsoft.VSDiagnostics;
5+
6+
namespace BlazorCalendar.Benchmarks;
7+
8+
/// <summary>
9+
/// Benchmark comparing task lookup strategies for AnnualView calendar rendering.
10+
/// Original approach: O(days × tasks) - linear search for each day
11+
/// Dictionary approach: O(days + tasks) - pre-indexed lookup
12+
/// </summary>
13+
[MemoryDiagnoser]
14+
[CPUUsageDiagnoser]
15+
public class TaskLookupBenchmarks
16+
{
17+
private TaskData[] _tasks = null!;
18+
private DateTime _startDate;
19+
private Dictionary<DateTime, List<TaskData>>? _tasksByDate;
20+
21+
// Simule 12 mois × 31 jours = 372 cellules (vue annuelle)
22+
private const int DaysCount = 372;
23+
24+
[Params(10, 100, 500, 1000)]
25+
public int TaskCount { get; set; }
26+
27+
[GlobalSetup]
28+
public void Setup()
29+
{
30+
_startDate = new DateTime(2025, 1, 1);
31+
var random = new Random(42); // Seed fixe pour reproductibilité
32+
33+
_tasks = new TaskData[TaskCount];
34+
for (int i = 0; i < TaskCount; i++)
35+
{
36+
var startOffset = random.Next(0, 365);
37+
var duration = random.Next(1, 8); // Tâches de 1 à 7 jours
38+
39+
_tasks[i] = new TaskData
40+
{
41+
ID = i,
42+
DateStart = _startDate.AddDays(startOffset),
43+
DateEnd = _startDate.AddDays(startOffset + duration),
44+
Code = $"TASK-{i}",
45+
Caption = $"Task {i}",
46+
Color = "#FF5733"
47+
};
48+
}
49+
50+
// Pré-construction de l'index pour le benchmark Dictionary
51+
BuildTaskIndex();
52+
}
53+
54+
private void BuildTaskIndex()
55+
{
56+
_tasksByDate = new Dictionary<DateTime, List<TaskData>>();
57+
58+
foreach (var task in _tasks)
59+
{
60+
for (var date = task.DateStart.Date; date <= task.DateEnd.Date; date = date.AddDays(1))
61+
{
62+
if (!_tasksByDate.TryGetValue(date, out var list))
63+
{
64+
list = new List<TaskData>(4);
65+
_tasksByDate[date] = list;
66+
}
67+
list.Add(task);
68+
}
69+
}
70+
}
71+
72+
/// <summary>
73+
/// Approche actuelle : pour chaque jour, parcourir TOUTES les tâches
74+
/// Complexité : O(jours × tâches)
75+
/// </summary>
76+
[Benchmark(Baseline = true)]
77+
public int OriginalLinearSearch()
78+
{
79+
int tasksFound = 0;
80+
string borderStyle;
81+
82+
for (int dayIndex = 0; dayIndex < DaysCount; dayIndex++)
83+
{
84+
var currentDate = _startDate.AddDays(dayIndex);
85+
int tasksCounter = 0;
86+
int taskID = -1;
87+
borderStyle = string.Empty;
88+
89+
// Simulation de la boucle originale
90+
for (int k = 0; k < _tasks.Length; k++)
91+
{
92+
var t = _tasks[k];
93+
94+
if (t.DateStart.Date <= currentDate.Date && currentDate.Date <= t.DateEnd.Date)
95+
{
96+
taskID = t.ID;
97+
tasksCounter++;
98+
99+
// Simulation du calcul de borderStyle
100+
if (t.DateStart.Date == currentDate.Date && t.DateEnd.Date != currentDate.Date)
101+
borderStyle = "border-top";
102+
else if (t.DateEnd.Date == currentDate.Date && t.DateStart.Date != currentDate.Date)
103+
borderStyle = "border-bottom";
104+
else if (t.DateEnd.Date == currentDate.Date && t.DateStart.Date == currentDate.Date)
105+
borderStyle = "border-top border-bottom";
106+
}
107+
}
108+
109+
if (tasksCounter >= 1)
110+
tasksFound++;
111+
}
112+
113+
return tasksFound;
114+
}
115+
116+
/// <summary>
117+
/// Approche optimisée : pré-indexation + lookup O(1)
118+
/// Complexité : O(jours + tâches) pour la construction, O(1) par lookup
119+
/// </summary>
120+
[Benchmark]
121+
public int DictionaryLookup()
122+
{
123+
int tasksFound = 0;
124+
string borderStyle;
125+
126+
for (int dayIndex = 0; dayIndex < DaysCount; dayIndex++)
127+
{
128+
var currentDate = _startDate.AddDays(dayIndex);
129+
int tasksCounter = 0;
130+
int taskID = -1;
131+
borderStyle = string.Empty;
132+
133+
// Lookup O(1)
134+
if (_tasksByDate!.TryGetValue(currentDate.Date, out var tasksForDay))
135+
{
136+
tasksCounter = tasksForDay.Count;
137+
138+
foreach (var t in tasksForDay)
139+
{
140+
taskID = t.ID;
141+
142+
// Simulation du calcul de borderStyle
143+
if (t.DateStart.Date == currentDate.Date && t.DateEnd.Date != currentDate.Date)
144+
borderStyle = "border-top";
145+
else if (t.DateEnd.Date == currentDate.Date && t.DateStart.Date != currentDate.Date)
146+
borderStyle = "border-bottom";
147+
else if (t.DateEnd.Date == currentDate.Date && t.DateStart.Date == currentDate.Date)
148+
borderStyle = "border-top border-bottom";
149+
}
150+
}
151+
152+
if (tasksCounter >= 1)
153+
tasksFound++;
154+
}
155+
156+
return tasksFound;
157+
}
158+
159+
/// <summary>
160+
/// Benchmark incluant la construction de l'index (cas réaliste lors d'un changement de TasksList)
161+
/// </summary>
162+
[Benchmark]
163+
public int DictionaryWithIndexBuild()
164+
{
165+
// Reconstruction de l'index (simule OnParametersSet)
166+
var tasksByDate = new Dictionary<DateTime, List<TaskData>>();
167+
168+
foreach (var task in _tasks)
169+
{
170+
for (var date = task.DateStart.Date; date <= task.DateEnd.Date; date = date.AddDays(1))
171+
{
172+
if (!tasksByDate.TryGetValue(date, out var list))
173+
{
174+
list = new List<TaskData>(4);
175+
tasksByDate[date] = list;
176+
}
177+
list.Add(task);
178+
}
179+
}
180+
181+
// Puis lookup
182+
int tasksFound = 0;
183+
string borderStyle;
184+
185+
for (int dayIndex = 0; dayIndex < DaysCount; dayIndex++)
186+
{
187+
var currentDate = _startDate.AddDays(dayIndex);
188+
int tasksCounter = 0;
189+
int taskID = -1;
190+
borderStyle = string.Empty;
191+
192+
if (tasksByDate.TryGetValue(currentDate.Date, out var tasksForDay))
193+
{
194+
tasksCounter = tasksForDay.Count;
195+
196+
foreach (var t in tasksForDay)
197+
{
198+
taskID = t.ID;
199+
200+
if (t.DateStart.Date == currentDate.Date && t.DateEnd.Date != currentDate.Date)
201+
borderStyle = "border-top";
202+
else if (t.DateEnd.Date == currentDate.Date && t.DateStart.Date != currentDate.Date)
203+
borderStyle = "border-bottom";
204+
else if (t.DateEnd.Date == currentDate.Date && t.DateStart.Date == currentDate.Date)
205+
borderStyle = "border-top border-bottom";
206+
}
207+
}
208+
209+
if (tasksCounter >= 1)
210+
tasksFound++;
211+
}
212+
213+
return tasksFound;
214+
}
215+
}
216+
217+
/// <summary>
218+
/// Simplified task model for benchmarking (mirrors BlazorCalendar.Models.Tasks)
219+
/// </summary>
220+
public sealed class TaskData
221+
{
222+
public int ID { get; set; }
223+
public string Code { get; set; } = string.Empty;
224+
public string Caption { get; set; } = string.Empty;
225+
public string Color { get; set; } = string.Empty;
226+
public DateTime DateStart { get; set; }
227+
public DateTime DateEnd { get; set; }
228+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFrameworks>net10.0</TargetFrameworks>
5+
<OutputType>Exe</OutputType>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<PackageReference Include="BenchmarkDotNet" Version="0.15.2" />
10+
<PackageReference Include="Microsoft.VisualStudio.DiagnosticsHub.BenchmarkDotNetDiagnosers" Version="18.0.36313.1" />
11+
</ItemGroup>
12+
</Project>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using BenchmarkDotNet.Running;
2+
3+
namespace BlazorCalendar.Benchmarks
4+
{
5+
internal class Program
6+
{
7+
static void Main(string[] args)
8+
{
9+
var _ = BenchmarkRunner.Run(typeof(Program).Assembly);
10+
}
11+
}
12+
}

0 commit comments

Comments
 (0)