Skip to content

Commit b1fd9c1

Browse files
Add cancellation and progress reporting to CSV reader
Introduces CancellationToken support and progress reporting via CsvReaderOptions for Dataplat.Dbatools.Csv. Benchmarks now compare against Sep, Sylvan.Data.Csv, and CsvHelper. Documentation and changelog updated to reflect new features and honest performance positioning. Version bumped to 1.1.0.
1 parent a430543 commit b1fd9c1

File tree

8 files changed

+624
-47
lines changed

8 files changed

+624
-47
lines changed

benchmarks/CsvBenchmarks/CsvBenchmarks.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
<ItemGroup>
1313
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
1414
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.2" />
15+
<!-- Competitor CSV libraries for benchmarking -->
16+
<PackageReference Include="Sep" Version="0.12.0" />
17+
<PackageReference Include="Sylvan.Data.Csv" Version="1.4.0" />
18+
<PackageReference Include="CsvHelper" Version="33.0.1" />
1519
</ItemGroup>
1620

1721
<ItemGroup>

benchmarks/CsvBenchmarks/Program.cs

Lines changed: 145 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,14 @@
88
using BenchmarkDotNet.Exporters;
99
using System.Data;
1010
using System.Text;
11+
using System.Globalization;
1112
using Dataplat.Dbatools.Csv.Reader;
13+
using nietras.SeparatedValues;
14+
using CsvHelper;
15+
using CsvHelper.Configuration;
16+
17+
// Alias to avoid ambiguity with Sylvan and CsvHelper
18+
using DataplatCsvReader = Dataplat.Dbatools.Csv.Reader.CsvDataReader;
1219

1320
namespace CsvBenchmarks;
1421

@@ -122,7 +129,7 @@ private void GenerateCsv(string path, int rows, int cols, bool quoteAll)
122129
public int Dataplat_Small()
123130
{
124131
int count = 0;
125-
using var reader = new CsvDataReader(_smallCsvPath);
132+
using var reader = new DataplatCsvReader(_smallCsvPath);
126133
while (reader.Read())
127134
{
128135
count++;
@@ -153,7 +160,7 @@ public int LumenWorks_Small()
153160
public int Dataplat_Medium()
154161
{
155162
int count = 0;
156-
using var reader = new CsvDataReader(_mediumCsvPath);
163+
using var reader = new DataplatCsvReader(_mediumCsvPath);
157164
while (reader.Read())
158165
{
159166
count++;
@@ -184,7 +191,7 @@ public int LumenWorks_Medium()
184191
public int Dataplat_Large()
185192
{
186193
int count = 0;
187-
using var reader = new CsvDataReader(_largeCsvPath);
194+
using var reader = new DataplatCsvReader(_largeCsvPath);
188195
while (reader.Read())
189196
{
190197
count++;
@@ -215,7 +222,7 @@ public int LumenWorks_Large()
215222
public int Dataplat_Wide()
216223
{
217224
int count = 0;
218-
using var reader = new CsvDataReader(_wideCsvPath);
225+
using var reader = new DataplatCsvReader(_wideCsvPath);
219226
while (reader.Read())
220227
{
221228
count++;
@@ -246,7 +253,7 @@ public int LumenWorks_Wide()
246253
public int Dataplat_Quoted()
247254
{
248255
int count = 0;
249-
using var reader = new CsvDataReader(_quotedCsvPath);
256+
using var reader = new DataplatCsvReader(_quotedCsvPath);
250257
while (reader.Read())
251258
{
252259
count++;
@@ -270,6 +277,138 @@ public int LumenWorks_Quoted()
270277
return count;
271278
}
272279

280+
// ===================== Modern Library Comparisons (Medium) =====================
281+
282+
[Benchmark(Description = "Sep-Medium")]
283+
[BenchmarkCategory("Modern")]
284+
public int Sep_Medium()
285+
{
286+
int count = 0;
287+
using var reader = Sep.Reader().FromFile(_mediumCsvPath);
288+
foreach (var row in reader)
289+
{
290+
count++;
291+
_ = row[0].ToString();
292+
}
293+
return count;
294+
}
295+
296+
[Benchmark(Description = "Sylvan-Medium")]
297+
[BenchmarkCategory("Modern")]
298+
public int Sylvan_Medium()
299+
{
300+
int count = 0;
301+
using var textReader = new StreamReader(_mediumCsvPath);
302+
using var reader = Sylvan.Data.Csv.CsvDataReader.Create(textReader);
303+
while (reader.Read())
304+
{
305+
count++;
306+
_ = reader.GetString(0);
307+
}
308+
return count;
309+
}
310+
311+
[Benchmark(Description = "CsvHelper-Medium")]
312+
[BenchmarkCategory("Modern")]
313+
public int CsvHelper_Medium()
314+
{
315+
int count = 0;
316+
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
317+
{
318+
HasHeaderRecord = true
319+
};
320+
using var textReader = new StreamReader(_mediumCsvPath);
321+
using var csv = new CsvHelper.CsvReader(textReader, config);
322+
csv.Read();
323+
csv.ReadHeader();
324+
while (csv.Read())
325+
{
326+
count++;
327+
_ = csv.GetField(0);
328+
}
329+
return count;
330+
}
331+
332+
[Benchmark(Description = "Dataplat-Medium-Modern")]
333+
[BenchmarkCategory("Modern")]
334+
public int Dataplat_Medium_Modern()
335+
{
336+
int count = 0;
337+
using var reader = new Dataplat.Dbatools.Csv.Reader.CsvDataReader(_mediumCsvPath);
338+
while (reader.Read())
339+
{
340+
count++;
341+
_ = reader.GetValue(0);
342+
}
343+
return count;
344+
}
345+
346+
// ===================== Modern Library Comparisons (Large) =====================
347+
348+
[Benchmark(Description = "Sep-Large")]
349+
[BenchmarkCategory("ModernLarge")]
350+
public int Sep_Large()
351+
{
352+
int count = 0;
353+
using var reader = Sep.Reader().FromFile(_largeCsvPath);
354+
foreach (var row in reader)
355+
{
356+
count++;
357+
_ = row[0].ToString();
358+
}
359+
return count;
360+
}
361+
362+
[Benchmark(Description = "Sylvan-Large")]
363+
[BenchmarkCategory("ModernLarge")]
364+
public int Sylvan_Large()
365+
{
366+
int count = 0;
367+
using var textReader = new StreamReader(_largeCsvPath);
368+
using var reader = Sylvan.Data.Csv.CsvDataReader.Create(textReader);
369+
while (reader.Read())
370+
{
371+
count++;
372+
_ = reader.GetString(0);
373+
}
374+
return count;
375+
}
376+
377+
[Benchmark(Description = "CsvHelper-Large")]
378+
[BenchmarkCategory("ModernLarge")]
379+
public int CsvHelper_Large()
380+
{
381+
int count = 0;
382+
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
383+
{
384+
HasHeaderRecord = true
385+
};
386+
using var textReader = new StreamReader(_largeCsvPath);
387+
using var csv = new CsvHelper.CsvReader(textReader, config);
388+
csv.Read();
389+
csv.ReadHeader();
390+
while (csv.Read())
391+
{
392+
count++;
393+
_ = csv.GetField(0);
394+
}
395+
return count;
396+
}
397+
398+
[Benchmark(Description = "Dataplat-Large-Modern")]
399+
[BenchmarkCategory("ModernLarge")]
400+
public int Dataplat_Large_Modern()
401+
{
402+
int count = 0;
403+
using var reader = new Dataplat.Dbatools.Csv.Reader.CsvDataReader(_largeCsvPath);
404+
while (reader.Read())
405+
{
406+
count++;
407+
_ = reader.GetValue(0);
408+
}
409+
return count;
410+
}
411+
273412
// ===================== All Values Access Benchmarks =====================
274413

275414
[Benchmark(Description = "Dataplat-AllValues")]
@@ -278,7 +417,7 @@ public int Dataplat_AllValues()
278417
{
279418
int count = 0;
280419
var options = new CsvReaderOptions { BufferSize = 65536 };
281-
using var reader = new CsvDataReader(_mediumCsvPath, options);
420+
using var reader = new DataplatCsvReader(_mediumCsvPath, options);
282421
object[] values = new object[reader.FieldCount];
283422
while (reader.Read())
284423
{

0 commit comments

Comments
 (0)