首页 > 解决方案 > 将浮点格式的 char[] 转换为浮点数

问题描述

我有一个char[] salary包含来自string. 我想转换char[] salaryfloat,但我正在尝试的方法似乎非常慢,即:

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

根据 Visual Studio 的 Performance Profiler,这需要太多的处理:

在此处输入图像描述

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

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

并且基本上是一个类似 JSON 的浮点数,转换为适合char[].

编辑:

我已经重新格式化了代码,看起来性能影响实际上是从char[]to的转换,而不是从tostring的解析。stringfloat

标签: c#performanceparsingfloating-point

解决方案


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

您说您的输入文件“读入 a byte[],然后将byte[]代表 的float部分提取到 achar[]中”。由于您有byte组成float文本的 s 隔离在 a 中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汇编和编译/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>();
        }
    }
}

推荐阅读