【问题标题】:Convert a float-formated char[] to float将浮点格式 char[] 转换为浮点数
【发布时间】:2019-01-06 02:57:46
【问题描述】:

我有一个char[] salary,其中包含来自string 的数据。我想将char[] salary 转换为float,但我尝试的方法似乎非常慢,即:

float ff = float.Parse(new string(salary));

根据 Visual Studio 的性能分析器,这需要太多的处理:

所以我想知道是否有更快的方法来做到这一点,因为这里的性能很重要。 char[] 的格式如下:

[ '1', '3', '2', ',', '2', '9']

并且基本上是一个类似 JSON 的浮点数,转换为适合 char[] 的每个数字(和逗号)。

编辑:

我重新格式化了代码,似乎性能损失实际上是从char[]string 的转换,而不是从stringfloat 的解析。

【问题讨论】:

  • 是来自用户还是系统?如果它是一个系统,您可以提供可以加速 float.parse 的文化。例如 float numFloat = float.Parse(System.Globalization.CultureInfo.InvariantInfo, strFloat);
  • 只是想知道为什么它是一个字符数组而不是一个字符串。如果没有这里发生的其他事情,分析器值不会提供太多信息。例如,30% 的毫秒并不多。真的是性能问题吗?
  • 信息来自 .json 文件。然后,它被读入一个 byte[],然后 byte[] 中表示浮点数的部分被提取到一个 char[] 中。性能是一个问题,因为我要处理超过 3000 万个条目。
  • 你需要解析一个非常大的json文件吗?你制造了一辆自行车,将文件部分读取到一个字节数组中,然后解析它?尝试使用流媒体JsonTextReader
  • 你在编译器release模式下测量时间了吗?使用 debug 模式会导致巨大的时间差异。

标签: c# performance parsing floating-point


【解决方案1】:

因为这个问题已经从“解析float 的最快方法是什么?”对于“从char[] 获得string 的最快方法是什么?”,我用BenchmarkDotNet 编写了一些基准来比较各种方法。我的发现是,如果您已经有一个 char[],那么您将无法比像您已经在做的那样将它传递给 string(char[]) 构造函数更快。

您说您的输入文件“读入byte[],然后将代表floatbyte[] 部分提取到char[]。”由于您有构成float 文本的bytes 隔离在byte[] 中,也许您可​​以通过跳过中间char[] 来提高性能。假设你有相当于......

byte[] floatBytes = new byte[] { 0x31, 0x33, 0x32, 0x2C, 0x32, 0x39 }; // "132,29"

...你可以使用Encoding.GetString()...

string floatString = Encoding.ASCII.GetString(floatBytes);

...这几乎是将Encoding.GetChars() 的结果传递给string(char[]) 构造函数的两倍...

char[] floatChars = Encoding.ASCII.GetChars(floatBytes);
string floatString = new string(floatChars);

您会发现我的结果中最后列出的那些基准...

BenchmarkDotNet=v0.11.0, OS=Windows 10.0.17134.165 (1803/April2018Update/Redstone4)
Intel Core i7 CPU 860 2.80GHz (Max: 2.79GHz) (Nehalem), 1 CPU, 8 logical and 4 physical cores
Frequency=2732436 Hz, Resolution=365.9738 ns, Timer=TSC
.NET Core SDK=2.1.202
  [Host] : .NET Core 2.0.9 (CoreCLR 4.6.26614.01, CoreFX 4.6.26614.01), 64bit RyuJIT
  Clr    : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3131.0
  Core   : .NET Core 2.0.9 (CoreCLR 4.6.26614.01, CoreFX 4.6.26614.01), 64bit RyuJIT


                                               Method | Runtime |       Categories |      Mean | Scaled |
----------------------------------------------------- |-------- |----------------- |----------:|-------:|
                         String_Constructor_CharArray |     Clr | char[] => string |  13.51 ns |   1.00 |
                                        String_Concat |     Clr | char[] => string | 192.87 ns |  14.27 |
 StringBuilder_Local_AppendSingleChar_DefaultCapacity |     Clr | char[] => string |  60.74 ns |   4.49 |
   StringBuilder_Local_AppendSingleChar_ExactCapacity |     Clr | char[] => string |  60.26 ns |   4.46 |
   StringBuilder_Local_AppendAllChars_DefaultCapacity |     Clr | char[] => string |  51.27 ns |   3.79 |
     StringBuilder_Local_AppendAllChars_ExactCapacity |     Clr | char[] => string |  49.51 ns |   3.66 |
                 StringBuilder_Field_AppendSingleChar |     Clr | char[] => string |  51.14 ns |   3.78 |
                   StringBuilder_Field_AppendAllChars |     Clr | char[] => string |  32.95 ns |   2.44 |
                                                      |         |                  |           |        |
                       String_Constructor_CharPointer |     Clr |  void* => string |  29.28 ns |   1.00 |
                      String_Constructor_SBytePointer |     Clr |  void* => string |  89.21 ns |   3.05 |
                   UnsafeArrayCopy_String_Constructor |     Clr |  void* => string |  42.82 ns |   1.46 |
                                                      |         |                  |           |        |
                                   Encoding_GetString |     Clr | byte[] => string |  37.33 ns |   1.00 |
                 Encoding_GetChars_String_Constructor |     Clr | byte[] => string |  60.83 ns |   1.63 |
                     SafeArrayCopy_String_Constructor |     Clr | byte[] => string |  27.55 ns |   0.74 |
                                                      |         |                  |           |        |
                         String_Constructor_CharArray |    Core | char[] => string |  13.27 ns |   1.00 |
                                        String_Concat |    Core | char[] => string | 172.17 ns |  12.97 |
 StringBuilder_Local_AppendSingleChar_DefaultCapacity |    Core | char[] => string |  58.68 ns |   4.42 |
   StringBuilder_Local_AppendSingleChar_ExactCapacity |    Core | char[] => string |  59.85 ns |   4.51 |
   StringBuilder_Local_AppendAllChars_DefaultCapacity |    Core | char[] => string |  40.62 ns |   3.06 |
     StringBuilder_Local_AppendAllChars_ExactCapacity |    Core | char[] => string |  43.67 ns |   3.29 |
                 StringBuilder_Field_AppendSingleChar |    Core | char[] => string |  54.49 ns |   4.11 |
                   StringBuilder_Field_AppendAllChars |    Core | char[] => string |  31.05 ns |   2.34 |
                                                      |         |                  |           |        |
                       String_Constructor_CharPointer |    Core |  void* => string |  22.87 ns |   1.00 |
                      String_Constructor_SBytePointer |    Core |  void* => string |  83.11 ns |   3.63 |
                   UnsafeArrayCopy_String_Constructor |    Core |  void* => string |  35.30 ns |   1.54 |
                                                      |         |                  |           |        |
                                   Encoding_GetString |    Core | byte[] => string |  36.19 ns |   1.00 |
                 Encoding_GetChars_String_Constructor |    Core | byte[] => string |  58.99 ns |   1.63 |
                     SafeArrayCopy_String_Constructor |    Core | byte[] => string |  27.81 ns |   0.77 |

...从运行此代码(需要BenchmarkDotNet assembly 并使用/unsafe 编译)...

using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using BenchmarkDotNet.Attributes;

namespace StackOverflow_51584129
{
    [CategoriesColumn()]
    [ClrJob()]
    [CoreJob()]
    [GroupBenchmarksBy(BenchmarkDotNet.Configs.BenchmarkLogicalGroupRule.ByCategory)]
    public class StringCreationBenchmarks
    {
        private static readonly Encoding InputEncoding = Encoding.ASCII;

        private const string InputString = "132,29";
        private static readonly byte[] InputBytes = InputEncoding.GetBytes(InputString);
        private static readonly char[] InputChars = InputString.ToCharArray();
        private static readonly sbyte[] InputSBytes = InputBytes.Select(Convert.ToSByte).ToArray();

        private GCHandle _inputBytesHandle;
        private GCHandle _inputCharsHandle;
        private GCHandle _inputSBytesHandle;

        private StringBuilder _builder;

        [Benchmark(Baseline = true)]
        [BenchmarkCategory("char[] => string")]
        public string String_Constructor_CharArray()
        {
            return new string(InputChars);
        }

        [Benchmark(Baseline = true)]
        [BenchmarkCategory("void* => string")]
        public unsafe string String_Constructor_CharPointer()
        {
            var pointer = (char*) _inputCharsHandle.AddrOfPinnedObject();

            return new string(pointer);
        }

        [Benchmark()]
        [BenchmarkCategory("void* => string")]
        public unsafe string String_Constructor_SBytePointer()
        {
            var pointer = (sbyte*) _inputSBytesHandle.AddrOfPinnedObject();

            return new string(pointer);
        }

        [Benchmark()]
        [BenchmarkCategory("char[] => string")]
        public string String_Concat()
        {
            return string.Concat(InputChars);
        }

        [Benchmark()]
        [BenchmarkCategory("char[] => string")]
        public string StringBuilder_Local_AppendSingleChar_DefaultCapacity()
        {
            var builder = new StringBuilder();

            foreach (var c in InputChars)
                builder.Append(c);

            return builder.ToString();
        }

        [Benchmark()]
        [BenchmarkCategory("char[] => string")]
        public string StringBuilder_Local_AppendSingleChar_ExactCapacity()
        {
            var builder = new StringBuilder(InputChars.Length);

            foreach (var c in InputChars)
                builder.Append(c);

            return builder.ToString();
        }

        [Benchmark()]
        [BenchmarkCategory("char[] => string")]
        public string StringBuilder_Local_AppendAllChars_DefaultCapacity()
        {
            var builder = new StringBuilder().Append(InputChars);

            return builder.ToString();
        }

        [Benchmark()]
        [BenchmarkCategory("char[] => string")]
        public string StringBuilder_Local_AppendAllChars_ExactCapacity()
        {
            var builder = new StringBuilder(InputChars.Length).Append(InputChars);

            return builder.ToString();
        }

        [Benchmark()]
        [BenchmarkCategory("char[] => string")]
        public string StringBuilder_Field_AppendSingleChar()
        {
            _builder.Clear();

            foreach (var c in InputChars)
                _builder.Append(c);

            return _builder.ToString();
        }

        [Benchmark()]
        [BenchmarkCategory("char[] => string")]
        public string StringBuilder_Field_AppendAllChars()
        {
            return _builder.Clear().Append(InputChars).ToString();
        }

        [Benchmark(Baseline = true)]
        [BenchmarkCategory("byte[] => string")]
        public string Encoding_GetString()
        {
            return InputEncoding.GetString(InputBytes);
        }

        [Benchmark()]
        [BenchmarkCategory("byte[] => string")]
        public string Encoding_GetChars_String_Constructor()
        {
            var chars = InputEncoding.GetChars(InputBytes);

            return new string(chars);
        }

        [Benchmark()]
        [BenchmarkCategory("byte[] => string")]
        public string SafeArrayCopy_String_Constructor()
        {
            var chars = new char[InputString.Length];

            for (int i = 0; i < InputString.Length; i++)
                chars[i] = Convert.ToChar(InputBytes[i]);

            return new string(chars);
        }

        [Benchmark()]
        [BenchmarkCategory("void* => string")]
        public unsafe string UnsafeArrayCopy_String_Constructor()
        {
            fixed (char* chars = new char[InputString.Length])
            {
                var bytes = (byte*) _inputBytesHandle.AddrOfPinnedObject();

                for (int i = 0; i < InputString.Length; i++)
                    chars[i] = Convert.ToChar(bytes[i]);

                return new string(chars);
            }
        }

        [GlobalSetup(Targets = new[] { nameof(StringBuilder_Field_AppendAllChars), nameof(StringBuilder_Field_AppendSingleChar) })]
        public void SetupStringBuilderField()
        {
            _builder = new StringBuilder();
        }

        [GlobalSetup(Target = nameof(UnsafeArrayCopy_String_Constructor))]
        public void SetupBytesHandle()
        {
            _inputBytesHandle = GCHandle.Alloc(InputBytes, GCHandleType.Pinned);
        }

        [GlobalCleanup(Target = nameof(UnsafeArrayCopy_String_Constructor))]
        public void CleanupBytesHandle()
        {
            _inputBytesHandle.Free();
        }

        [GlobalSetup(Target = nameof(String_Constructor_CharPointer))]
        public void SetupCharsHandle()
        {
            _inputCharsHandle = GCHandle.Alloc(InputChars, GCHandleType.Pinned);
        }

        [GlobalCleanup(Target = nameof(String_Constructor_CharPointer))]
        public void CleanupCharsHandle()
        {
            _inputCharsHandle.Free();
        }

        [GlobalSetup(Target = nameof(String_Constructor_SBytePointer))]
        public void SetupSByteHandle()
        {
            _inputSBytesHandle = GCHandle.Alloc(InputSBytes, GCHandleType.Pinned);
        }

        [GlobalCleanup(Target = nameof(String_Constructor_SBytePointer))]
        public void CleanupSByteHandle()
        {
            _inputSBytesHandle.Free();
        }

        public static void Main(string[] args)
        {
            BenchmarkDotNet.Running.BenchmarkRunner.Run<StringCreationBenchmarks>();
        }
    }
}

【讨论】:

  • 干得好!必须记住这个基准库。
  • 谢谢。这是我第一次尝试它,或者任何基准库,真的。很好,它上手非常简单,它使您能够编写除了要测试的代码之外的基准方法。另一方面,我确实发现它也非常强大(复杂),并且文档有些缺乏,所以试图弄清楚是什么控制了什么以及如何从默认值中改变东西真的变得相当麻烦(尤其是因为每次运行都需要一段时间才能看到刚刚更改的结果)所以我最终放弃了摆弄它。
【解决方案2】:

float-parsing 方面,根据您调用的 float.Parse() 的重载以及传递给它的内容,可以获得一些收益。我运行了一些比较这些重载的基准测试(请注意,我将小数点分隔符从 ',' 更改为 '.' 只是为了指定 CultureInfo.InvariantCulture)。

例如,调用采用IFormatProvider 的重载可以将性能提高约 10%。为NumberStyles 参数指定NumberStyles.Float(“lax”)会影响性能的变化在任一方向,并且对我们的输入数据进行一些假设,仅指定@987654340 @("strict") 网络性能提高了几分。 (float.Parse(string) overload 使用 NumberStyles.Float | NumberStyles.AllowThousands。)

关于对输入数据进行假设的主题,如果您知道您正在使用的文本具有某些特征(单字节字符编码,没有无效数字,没有负数,没有指数,不需要处理 @987654326 @ 或 positive/negative infinity 等)您最好直接从 bytes 解析并放弃任何不需要的特殊情况处理和错误检查。我在我的基准测试中包含了一个非常简单的实现,它能够从 byte[] 获得 floatfloat.Parse(string) 获得 float 的速度比 float.Parse(string)16 倍 可以从 string 获得 float

这是我的基准测试结果...

BenchmarkDotNet=v0.11.0, OS=Windows 10.0.17134.165 (1803/April2018Update/Redstone4)
Intel Core i7 CPU 860 2.80GHz (Max: 2.79GHz) (Nehalem), 1 CPU, 8 logical and 4 physical cores
Frequency=2732436 Hz, Resolution=365.9738 ns, Timer=TSC
.NET Core SDK=2.1.202
  [Host] : .NET Core 2.0.9 (CoreCLR 4.6.26614.01, CoreFX 4.6.26614.01), 64bit RyuJIT
  Clr    : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3131.0
  Core   : .NET Core 2.0.9 (CoreCLR 4.6.26614.01, CoreFX 4.6.26614.01), 64bit RyuJIT


                                                        Method | Runtime |       Mean | Scaled |
-------------------------------------------------------------- |-------- |-----------:|-------:|
                                           float.Parse(string) |     Clr | 145.098 ns |   1.00 |
                        'float.Parse(string, IFormatProvider)' |     Clr | 134.191 ns |   0.92 |
                     'float.Parse(string, NumberStyles) [Lax]' |     Clr | 145.884 ns |   1.01 |
                  'float.Parse(string, NumberStyles) [Strict]' |     Clr | 139.417 ns |   0.96 |
    'float.Parse(string, NumberStyles, IFormatProvider) [Lax]' |     Clr | 133.800 ns |   0.92 |
 'float.Parse(string, NumberStyles, IFormatProvider) [Strict]' |     Clr | 127.413 ns |   0.88 |
                       'Custom byte-to-float parser [Indexer]' |     Clr |   7.657 ns |   0.05 |
                    'Custom byte-to-float parser [Enumerator]' |     Clr | 566.440 ns |   3.90 |
                                                               |         |            |        |
                                           float.Parse(string) |    Core | 154.369 ns |   1.00 |
                        'float.Parse(string, IFormatProvider)' |    Core | 138.668 ns |   0.90 |
                     'float.Parse(string, NumberStyles) [Lax]' |    Core | 155.644 ns |   1.01 |
                  'float.Parse(string, NumberStyles) [Strict]' |    Core | 150.221 ns |   0.97 |
    'float.Parse(string, NumberStyles, IFormatProvider) [Lax]' |    Core | 142.591 ns |   0.92 |
 'float.Parse(string, NumberStyles, IFormatProvider) [Strict]' |    Core | 135.000 ns |   0.87 |
                       'Custom byte-to-float parser [Indexer]' |    Core |  12.673 ns |   0.08 |
                    'Custom byte-to-float parser [Enumerator]' |    Core | 584.236 ns |   3.78 |

...从运行此代码(需要BenchmarkDotNet assembly)...

using System;
using System.Globalization;
using BenchmarkDotNet.Attributes;

namespace StackOverflow_51584129
{
    [ClrJob()]
    [CoreJob()]
    public class FloatParsingBenchmarks
    {
        private const string InputString = "132.29";
        private static readonly byte[] InputBytes = System.Text.Encoding.ASCII.GetBytes(InputString);

        private static readonly IFormatProvider ParsingFormatProvider = CultureInfo.InvariantCulture;
        private const NumberStyles LaxParsingNumberStyles = NumberStyles.Float;
        private const NumberStyles StrictParsingNumberStyles = NumberStyles.AllowDecimalPoint;
        private const char DecimalSeparator = '.';

        [Benchmark(Baseline = true, Description = "float.Parse(string)")]
        public float SystemFloatParse()
        {
            return float.Parse(InputString);
        }

        [Benchmark(Description = "float.Parse(string, IFormatProvider)")]
        public float SystemFloatParseWithProvider()
        {
            return float.Parse(InputString, CultureInfo.InvariantCulture);
        }

        [Benchmark(Description = "float.Parse(string, NumberStyles) [Lax]")]
        public float SystemFloatParseWithLaxNumberStyles()
        {
            return float.Parse(InputString, LaxParsingNumberStyles);
        }

        [Benchmark(Description = "float.Parse(string, NumberStyles) [Strict]")]
        public float SystemFloatParseWithStrictNumberStyles()
        {
            return float.Parse(InputString, StrictParsingNumberStyles);
        }

        [Benchmark(Description = "float.Parse(string, NumberStyles, IFormatProvider) [Lax]")]
        public float SystemFloatParseWithLaxNumberStylesAndProvider()
        {
            return float.Parse(InputString, LaxParsingNumberStyles, ParsingFormatProvider);
        }

        [Benchmark(Description = "float.Parse(string, NumberStyles, IFormatProvider) [Strict]")]
        public float SystemFloatParseWithStrictNumberStylesAndProvider()
        {
            return float.Parse(InputString, StrictParsingNumberStyles, ParsingFormatProvider);
        }

        [Benchmark(Description = "Custom byte-to-float parser [Indexer]")]
        public float CustomFloatParseByIndexing()
        {
            // FOR DEMONSTRATION PURPOSES ONLY!
            // This code has been written for and only tested with
            // parsing the ASCII string "132.29" in byte form
            var currentIndex = 0;
            var boundaryIndex = InputBytes.Length;
            char currentChar;
            var wholePart = 0;

            while (currentIndex < boundaryIndex && (currentChar = (char) InputBytes[currentIndex++]) != DecimalSeparator)
            {
                var currentDigit = currentChar - '0';

                wholePart = 10 * wholePart + currentDigit;
            }

            var fractionalPart = 0F;
            var nextFractionalDigitScale = 0.1F;

            while (currentIndex < boundaryIndex)
            {
                currentChar = (char) InputBytes[currentIndex++];
                var currentDigit = currentChar - '0';

                fractionalPart += currentDigit * nextFractionalDigitScale;
                nextFractionalDigitScale *= 0.1F;
            }

            return wholePart + fractionalPart;
        }

        [Benchmark(Description = "Custom byte-to-float parser [Enumerator]")]
        public float CustomFloatParseByEnumerating()
        {
            // FOR DEMONSTRATION PURPOSES ONLY!
            // This code has been written for and only tested with
            // parsing the ASCII string "132.29" in byte form
            var wholePart = 0;
            var enumerator = InputBytes.GetEnumerator();

            while (enumerator.MoveNext())
            {
                var currentChar = (char) (byte) enumerator.Current;

                if (currentChar == DecimalSeparator)
                    break;

                var currentDigit = currentChar - '0';
                wholePart = 10 * wholePart + currentDigit;
            }

            var fractionalPart = 0F;
            var nextFractionalDigitScale = 0.1F;

            while (enumerator.MoveNext())
            {
                var currentChar = (char) (byte) enumerator.Current;
                var currentDigit = currentChar - '0';

                fractionalPart += currentDigit * nextFractionalDigitScale;
                nextFractionalDigitScale *= 0.1F;
            }

            return wholePart + fractionalPart;
        }

        public static void Main()
        {
            BenchmarkDotNet.Running.BenchmarkRunner.Run<FloatParsingBenchmarks>();
        }
    }
}

【讨论】:

  • 关于你的基准标记的一个注释,我不确定他的目标 .NET 版本是什么。在某些情况下,.NET 核心可能具有与 .NET 4.7.2 不同的特性。 Dot Net Benchmark 你可以要求它同时做多个框架版本。
  • @GlennWatson 是的,我已经将它配置为对 .NET Framework 和 Core 进行基准测试,但 Framework 的结果都返回为 NA。那时我不想再愚弄它了(请参阅我对其他答案的评论),并且正如您所指出的,我不知道作者没有使用Core,所以我把框架编号留给了作者/读者作为练习。也许我会再次尝试用更多数据填写我的答案。
  • 根据the FAQ,我只需要对我的项目文件进行一些小的编辑,即可使 .NET Framework 基准测试正常工作。我已经用 Framework 和 Core 的新基准测试结果更新了答案。
  • 干得好。为了登录,我使用了:float sgn = 1; while (currentIndex
【解决方案3】:

经过this的一些实验和测试:

char[] 获得string 的最快方法是使用new string

再注意一点FYI,按照微软的这个article在输入无效的情况下,TryParse是解析float最快的方法。所以,想想吧。。

TryParse 仅占用 0.5% 的执行时间 Parse 占用 18% 而 Convert 占用 14%

【讨论】:

  • 您的报价断章取义。前面那句话是“如下图所示,您可以看到 TryParse、Parse 和 ConvertTo 当您使用不良数据时之间的巨大差异。” (强调我的),后面的句子是“正如我们所猜测的,不同之处在于异常处理代码。”因此,这只是表明TryParse 返回falseParse 抛出异常要快得多,这是已知的。另外,文章中的数字是用于解析ints,而不是floats。
  • other answer to that same question 声称“如果您实际上有一个 char 数组,即使您将其作为 IEnumerable 传递,调用字符串构造函数也更快”,测试结果表明 @987654334 @构造函数,即使在char[] 上不必要地调用.ToArray(),也比使用StringBuilder 快得多。重要的是,另一个问题是关于从IEnumerable&lt;char&gt; 制作string,而在这个问题中我们有一个char[]
  • 该答案还涉及从IEnumerable&lt;char&gt; 创建string。在这个问题中,我们没有IEnumerable&lt;char&gt;,我们有更好的东西:char[],其中长度是预先知道的。 (而且,是的,当然char[] 实现了IEnumberable&lt;char&gt;。)您不能从测试接口中获取数字并将它们呈现为对实现该接口的所有类型都成立。在 char[] 上操作时,您需要比较 new string(char[])StringBuilder.Append(char),而不是需要调用 .ToArray() 扩展方法的 IEnumerable&lt;char&gt;
  • StringBuilder 甚至接受一个字符数组,所以sb.Append(a); 会更快。但我很难相信字符串构造函数不应该被优化。也许是时候进行自己的测试了,考虑到 BACON 的反对意见,并在编译器模式“release”下运行它们。
  • 请在the linked answer 上查看我的cmets。尽管得出了正确的结论,但代码有问题,结果(在此处引用)无效,而且无论如何它都没有测试相同的场景。无意冒犯,但这就是为什么在引用数据之前查看数据的上下文及其来源很重要的原因。 StringBuilder 很可能是从char[] 获得string 的最快方法,但我对这个答案投了反对票,因为它及其支持的 cmets 已引用或链接到误导性信息的三倍,这没有帮助。
【解决方案4】:

在家制定优化细节的有趣话题:)祝大家身体健康..

我的目标是:在 C# 中尽可能快地将 Ascii CSV 矩阵转换为浮点矩阵。为此,事实证明 string.Split() 行和单独转换每个术语也会引入开销。为了克服这个问题,我修改了 BACON 的行解析我的浮点数的解决方案,像这样使用它:

  var falist = new List<float[]>();
  for (int row=0; row<sRowList.Count; row++)
  {
    var sRow = sRowList[row];
    falist.Add(CustomFloatParseRowByIndexing(nTerms, sRow.ToCharArray(), '.'));
  }

我的行解析器变体的代码如下。这些是基准测试结果,将 40x31 矩阵转换为 1000x:

Benchmark0:拆分行并解析每个项以转换为浮点矩阵dT=704 ms

Benchmark1:拆分行并 TryParse 将每个项转换为浮点矩阵 dT=640 ms

Benchmark2:拆分行和 CustomFloatParseByIndexing 将术语转换为浮点矩阵 dT=211 ms

Benchmark3:使用 CustomFloatParseRowByIndexing 将行转换为浮点矩阵 dT=120 ms

public float[] CustomFloatParseRowByIndexing(int nItems, char[] InputBytes, char   DecimalSeparator)
{
// Convert semicolon-separated floats from InputBytes into nItems float[] result.
// Constraints are:
//   - no scientific notation or .x allowed
//   - every row has exactly nItems values
//   - semicolon delimiter after each value
//   - terms 'u'  or 'undef' or 'undefined' allowed for bad values
//   - minus sign allowed
//   - leading space allowed
//   - all terms must comply

// FOR DEMO PURPOSE ONLY
// based on BACON on Stackoverflow, modified to read nItems delimited float values
// https://stackoverflow.com/questions/51584129/convert-a-float-formated-char-to-float

var currentIndex = 0;
var boundaryIndex = InputBytes.Length;
bool termready, ready = false;
float[] result = new float[nItems];
int cItem = 0;
while (currentIndex < boundaryIndex)
{
    termready = false;
    if ((char)InputBytes[currentIndex] == ' ') { currentIndex++; continue; }
    char currentChar;
    var wholePart = 0;
    float sgn = 1;
    while (currentIndex < boundaryIndex && (currentChar = (char)InputBytes[currentIndex++]) != DecimalSeparator)
    {
        if (currentChar == 'u')
        {
            while ((char)InputBytes[currentIndex++] != ';') ;
            result[cItem++] = -9999.0f;
            continue;
        }
        else
        if (currentChar == ' ')
        {                       
            continue;
        }
        else
        if (currentChar == ';')
        {
            termready = true;
            break;
        }
        else
        if (currentChar == '-') sgn = -1;
        else
        {
            var currentDigit = currentChar - '0';
            wholePart = 10 * wholePart + currentDigit;
        }
    }
    var fractionalPart = 0F;
    var nextFractionalDigitScale = 0.1F;
    if (!termready)
        while (currentIndex < boundaryIndex)
        {
            currentChar = (char)InputBytes[currentIndex++];
            if (currentChar == ';')
            {
                termready = true;
                break;
            }
            var currentDigit = currentChar - '0';
            fractionalPart += currentDigit * nextFractionalDigitScale;
            nextFractionalDigitScale *= 0.1F;
        }
    if (termready) 
    { 
      result[cItem++] = sgn * (wholePart + fractionalPart); 
    }
  }   
  return result;
}

【讨论】:

    猜你喜欢
    • 2011-02-28
    • 2023-03-21
    • 2021-07-16
    • 1970-01-01
    • 1970-01-01
    • 2021-08-09
    • 2012-10-11
    • 2013-11-11
    • 1970-01-01
    相关资源
    最近更新 更多