diff --git a/performance/CsvHelper.Benchmarks/BenchmarkEnumerateRecordsSingleInstance.cs b/performance/CsvHelper.Benchmarks/BenchmarkEnumerateRecordsSingleInstance.cs new file mode 100644 index 000000000..2879b9fd8 --- /dev/null +++ b/performance/CsvHelper.Benchmarks/BenchmarkEnumerateRecordsSingleInstance.cs @@ -0,0 +1,60 @@ +using System; +using System.Globalization; +using System.IO; +using BenchmarkDotNet.Attributes; + +namespace CsvHelper.Benchmarks; + +[MemoryDiagnoser] +public class BenchmarkEnumerateRecordsSingleInstance +{ + private const int entryCount = 2000; + private readonly MemoryStream stream = new(); + + public class SimpleWithValueType + { + public int Id { get; set; } + public bool Flag1 { get; set; } + public bool Flag2 { get; set; } + } + + [GlobalSetup] + public void GlobalSetupSimple() + { + using var streamWriter = new StreamWriter(this.stream, null, -1, true); + using var writer = new CsvWriter(streamWriter, CultureInfo.InvariantCulture, true); + var random = new Random(43); // Different seed for variety + + writer.WriteHeader(typeof(SimpleWithValueType)); + writer.NextRecord(); + for (int i = 0; i < entryCount; ++i) + { + writer.WriteRecord(new SimpleWithValueType() + { + Id = random.Next(), + Flag1 = random.Next(2) == 0, + Flag2 = random.Next(2) == 0 + }); + writer.NextRecord(); + } + } + + [GlobalCleanup] + public void GlobalCleanupSimple() + { + this.stream.Dispose(); + } + + [Benchmark] + public void EnumerateRecordsSingleInstance() + { + this.stream.Position = 0; + using var streamReader = new StreamReader(this.stream, null, true, -1, true); + using var csv = new CsvReader(streamReader, CultureInfo.InvariantCulture, true); + var instance = new SimpleWithValueType(); + foreach (var record in csv.EnumerateRecords(instance)) + { + _ = record; + } + } +} diff --git a/performance/CsvHelper.Benchmarks/BenchmarkMain.cs b/performance/CsvHelper.Benchmarks/BenchmarkMain.cs index 849ffcc1b..6a0a247c9 100644 --- a/performance/CsvHelper.Benchmarks/BenchmarkMain.cs +++ b/performance/CsvHelper.Benchmarks/BenchmarkMain.cs @@ -4,8 +4,8 @@ namespace CsvHelper.Benchmarks; internal class BenchmarkMain { - static void Main(string[] args) - { - _ = BenchmarkRunner.Run(); - } + static void Main(string[] args) + { + _ = BenchmarkSwitcher.FromAssembly(System.Reflection.Assembly.GetExecutingAssembly()).Run(args); + } } diff --git a/performance/CsvHelper.Benchmarks/CsvHelper.Benchmarks.csproj b/performance/CsvHelper.Benchmarks/CsvHelper.Benchmarks.csproj index 248fbd13f..b3cf69f11 100644 --- a/performance/CsvHelper.Benchmarks/CsvHelper.Benchmarks.csproj +++ b/performance/CsvHelper.Benchmarks/CsvHelper.Benchmarks.csproj @@ -7,6 +7,7 @@ + diff --git a/src/CsvHelper/TypeConversion/BooleanConverter.cs b/src/CsvHelper/TypeConversion/BooleanConverter.cs index e11e1f5fa..e30bee9e3 100644 --- a/src/CsvHelper/TypeConversion/BooleanConverter.cs +++ b/src/CsvHelper/TypeConversion/BooleanConverter.cs @@ -12,59 +12,62 @@ namespace CsvHelper.TypeConversion; /// public class BooleanConverter : DefaultTypeConverter { - /// - public override object? ConvertFromString(string? text, IReaderRow row, MemberMapData memberMapData) - { - if (bool.TryParse(text, out var b)) - { - return b; - } + private static readonly object BoxedTrue = true; + private static readonly object BoxedFalse = false; - if (short.TryParse(text, out var sh)) - { - if (sh == 0) - { - return false; - } - if (sh == 1) - { - return true; - } - } + /// + public override object? ConvertFromString(string? text, IReaderRow row, MemberMapData memberMapData) + { + if (bool.TryParse(text, out var b)) + { + return b ? BoxedTrue : BoxedFalse; + } - var t = (text ?? string.Empty).Trim(); - foreach (var trueValue in memberMapData.TypeConverterOptions.BooleanTrueValues) - { - if (memberMapData.TypeConverterOptions.CultureInfo!.CompareInfo.Compare(trueValue, t, CompareOptions.IgnoreCase) == 0) - { - return true; - } - } + if (short.TryParse(text, out var sh)) + { + if (sh == 0) + { + return BoxedFalse; + } + if (sh == 1) + { + return BoxedTrue; + } + } - foreach (var falseValue in memberMapData.TypeConverterOptions.BooleanFalseValues) - { - if (memberMapData.TypeConverterOptions.CultureInfo!.CompareInfo.Compare(falseValue, t, CompareOptions.IgnoreCase) == 0) - { - return false; - } - } + var t = (text ?? string.Empty).Trim(); + foreach (var trueValue in memberMapData.TypeConverterOptions.BooleanTrueValues) + { + if (memberMapData.TypeConverterOptions.CultureInfo!.CompareInfo.Compare(trueValue, t, CompareOptions.IgnoreCase) == 0) + { + return BoxedTrue; + } + } - return base.ConvertFromString(text, row, memberMapData); - } + foreach (var falseValue in memberMapData.TypeConverterOptions.BooleanFalseValues) + { + if (memberMapData.TypeConverterOptions.CultureInfo!.CompareInfo.Compare(falseValue, t, CompareOptions.IgnoreCase) == 0) + { + return BoxedFalse; + } + } - /// - public override string? ConvertToString(object? value, IWriterRow row, MemberMapData memberMapData) - { + return base.ConvertFromString(text, row, memberMapData); + } + + /// + public override string? ConvertToString(object? value, IWriterRow row, MemberMapData memberMapData) + { var b = value as bool?; if (b == true && memberMapData.TypeConverterOptions.BooleanTrueValues.Count > 0) - { - return memberMapData.TypeConverterOptions.BooleanTrueValues.First(); - } + { + return memberMapData.TypeConverterOptions.BooleanTrueValues.First(); + } else if (b == false && memberMapData.TypeConverterOptions.BooleanFalseValues.Count > 0) - { - return memberMapData.TypeConverterOptions.BooleanFalseValues.First(); - } + { + return memberMapData.TypeConverterOptions.BooleanFalseValues.First(); + } - return base.ConvertToString(value, row, memberMapData); - } + return base.ConvertToString(value, row, memberMapData); + } }