• 通过解析库探究函数式抽象代价 ( ini 解析示例补充)


    上一篇 用 HexColor 作为示例,可能过于简单

    这里再补充一个 ini 解析的示例

    由于实在写不动用其他库解析 ini 了, 春节都要过完了,累了,写不动了,

    所以随意找了一份解析ini的库, 仅供参考,对比不准确,毕竟完整库包含了更多功能

    先看看结果

    
    BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.3085/23H2/2023Update/SunValley3)
    Intel Core i7-9750H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores
    .NET SDK 8.0.200
      [Host]     : .NET 8.0.2 (8.0.224.6711), X64 RyuJIT AVX2
      DefaultJob : .NET 8.0.2 (8.0.224.6711), X64 RyuJIT AVX2
    
    
    
    Method Mean Error StdDev Gen0 Gen1 Allocated
    Hande_Ini 567.9 ns 11.24 ns 21.66 ns 0.2851 - 1.75 KB
    RuQu_Ini 1,691.4 ns 33.48 ns 64.51 ns 0.4177 - 2.56 KB
    IniDataParser 4,836.3 ns 94.44 ns 167.87 ns 1.1215 0.0076 6.91 KB
    // * Legends *
      Mean      : Arithmetic mean of all measurements
      Error     : Half of 99.9% confidence interval
      StdDev    : Standard deviation of all measurements
      Gen0      : GC Generation 0 collects per 1000 operations
      Gen1      : GC Generation 1 collects per 1000 operations
      Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
      1 ns      : 1 Nanosecond (0.000000001 sec)
    

    总结

    • delegate 肯定会有调用消耗,ini 场景使用的函数远多于 HexColor ,可以看到消耗大了很多

    先看来自 dotnet Microsoft.Extensions.Configuration 中解析 ini 的代码

    public static IDictionary<string, string?> Read(string content)
    {
        var data = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
        using (var reader = new StringReader(content))
        {
            string sectionPrefix = string.Empty;
    
            while (reader.Peek() != -1)
            {
                string rawLine = reader.ReadLine()!; // Since Peak didn't return -1, stream hasn't ended.
                string line = rawLine.Trim();
    
                // Ignore blank lines
                if (string.IsNullOrWhiteSpace(line))
                {
                    continue;
                }
                // Ignore comments
                if (line[0] is ';' or '#' or '/')
                {
                    continue;
                }
                // [Section:header]
                if (line[0] == '[' && line[line.Length - 1] == ']')
                {
                    // remove the brackets
                    sectionPrefix = string.Concat(line.AsSpan(1, line.Length - 2).Trim(), ConfigurationPath.KeyDelimiter);
                    continue;
                }
    
                // key = value OR "value"
                int separator = line.IndexOf('=');
                if (separator < 0)
                {
                    throw new FormatException(rawLine);
                }
    
                string key = sectionPrefix + line.Substring(0, separator).Trim();
                string value = line.Substring(separator + 1).Trim();
    
                // Remove quotes
                if (value.Length > 1 && value[0] == '"' && value[value.Length - 1] == '"')
                {
                    value = value.Substring(1, value.Length - 2);
                }
    
                if (data.ContainsKey(key))
                {
                    throw new FormatException(key);
                }
    
                data[key] = value;
            }
        }
        return data;
    }
    

    再来看看部分函数语义优化的代码

    public class Ini
    {
        private static Ini instance = new Ini();
    
        public Funcchar>, bool> WhiteSpace = Chars.IngoreWhiteSpace.Map(i => i > 0);
    
        public Funcchar>, bool> Comment = Chars.In(";#/").Delimited(Chars.NotCRLF.ToSlice().Opt(), Chars.IsCRLF, "Comment not right").Map((c, x, y, z) => c);
    
        public Funcchar>, string> SectionName = Chars.Is('[').Delimited(Chars.Not(']').ToSlice().Once("Section name is required."), Chars.Is(']'), "Section name must end with ']'").Map((c, x, y, z) => y?.ToString());
    
        public Funcchar>, string> Key = Chars.Not('=').ToSlice().Once("key is required.").Map(i => i.ToString());
        public Funcchar>, char> Separater = Chars.Is('=').Once("Section name is required.");
    
        public Funcchar>, string> Value = Chars.NotCRLF.ToSlice().Once("value is required.").Map(i =>
        {
            var v = i.ToString().Trim();
            return v.StartsWith('"') ? v[1..^1] : v;
        });
    
        public IDictionary<string, string> ParseString(string content)
        {
            var input = Input.From(content);
            var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            while (input.TryPeek(out var v))
            {
                if (!(WhiteSpace(input) || Comment(input) || Section(input, dict)))
                {
                    throw new NotSupportedException(v.ToString());
                }
            }
            return dict;
        }
    
        public bool SectionContentEnd(IPeeker<char> input)
        {
            return !input.TryPeek(out var v) || v is '[';
        }
    
        public bool Section(StringPeeker input, Dictionary<string, string> dict)
        {
            var name = SectionName(input);
            if (name == null) return false;
            while (!SectionContentEnd(input))
            {
                if (WhiteSpace(input) || Comment(input))
                {
                    continue;
                }
                SectionKV(input, dict, name);
            }
            return true;
        }
    
        public void SectionKV(StringPeeker input, Dictionary<string, string> dict, string name)
        {
            var k = Key(input);
            Separater(input);
            var v = Value(input);
            k = $"{name}:{k.Trim()}";
            dict.Add(k, v.Trim());
        }
    
        public static IDictionary<string, string> Parse(string content)
        {
            return instance.ParseString(content);
        }
    }
    

    最后截取 部分 ini 解析库的代码 仅供参考

    public IniData Parse(string iniDataString)
    {
        IniData iniData = (Configuration.CaseInsensitive ? new IniDataCaseInsensitive() : new IniData());
        iniData.Configuration = Configuration.Clone();
        if (string.IsNullOrEmpty(iniDataString))
        {
            return iniData;
        }
    
        _errorExceptions.Clear();
        _currentCommentListTemp.Clear();
        _currentSectionNameTemp = null;
        try
        {
            string[] array = iniDataString.Split(new string[2] { "\n", "\r\n" }, StringSplitOptions.None);
            for (int i = 0; i < array.Length; i++)
            {
                string text = array[i];
                if (text.Trim() == string.Empty)
                {
                    continue;
                }
    
                try
                {
                    ProcessLine(text, iniData);
                }
                catch (Exception ex)
                {
                    ParsingException ex2 = new ParsingException(ex.Message, i + 1, text, ex);
                    if (Configuration.ThrowExceptionsOnError)
                    {
                        throw ex2;
                    }
    
                    _errorExceptions.Add(ex2);
                }
            }
    
            if (_currentCommentListTemp.Count > 0)
            {
                if (iniData.Sections.Count > 0)
                {
                    iniData.Sections.GetSectionData(_currentSectionNameTemp).TrailingComments.AddRange(_currentCommentListTemp);
                }
                else if (iniData.Global.Count > 0)
                {
                    iniData.Global.GetLast().Comments.AddRange(_currentCommentListTemp);
                }
    
                _currentCommentListTemp.Clear();
            }
        }
        catch (Exception item)
        {
            _errorExceptions.Add(item);
            if (Configuration.ThrowExceptionsOnError)
            {
                throw;
            }
        }
    
        if (HasError)
        {
            return null;
        }
    
        return (IniData)iniData.Clone();
    }
    
    

    完整代码参考 https://github.com/fs7744/ruqu

  • 相关阅读:
    算法: 编辑距离使得单词相同 72. Edit Distance
    2023高教社杯全国大学生数学建模竞赛选题建议
    人类认知的贝叶斯与机器的贝叶斯
    【C++】&与&&用法总结
    读高性能MySQL(第4版)笔记18_扩展MySQL
    Go中原生http服务的实现方式
    并查集拓展(扩展域并查集)
    springboot+redis
    认识etcd
    vuepress的使用
  • 原文地址:https://www.cnblogs.com/fs7744/p/18016371