Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/309.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用.Net中的Span将ID从输入字符串解析为长数字_C#_.net_Parsing_Memory - Fatal编程技术网

C# 使用.Net中的Span将ID从输入字符串解析为长数字

C# 使用.Net中的Span将ID从输入字符串解析为长数字,c#,.net,parsing,memory,C#,.net,Parsing,Memory,我试图从一个包含不同数字的字符串中解析不同的IDslong,我需要最小化内存分配以提高性能 下面是使用Split提取id的代码,但是我发现我可以使用AsSpan和Splice来做同样的事情,而无需分配内存。但不幸的是,即使在网上查找之后,我也不太熟悉这个Span概念。有谁能插话告诉我怎样才能做到这一点 正如您在下面看到的,输入字符串有3个不同的ID,但我只需要其中的2个,并解析为长类型 string[] machineIdPart; string[] employ

我试图从一个包含不同数字的字符串中解析不同的IDslong,我需要最小化内存分配以提高性能

下面是使用Split提取id的代码,但是我发现我可以使用AsSpan和Splice来做同样的事情,而无需分配内存。但不幸的是,即使在网上查找之后,我也不太熟悉这个Span概念。有谁能插话告诉我怎样才能做到这一点

正如您在下面看到的,输入字符串有3个不同的ID,但我只需要其中的2个,并解析为长类型

        string[] machineIdPart;
        string[] employeeIdPart;
        long machineId;            
        long employeeId;

        //Input String
        var description = "machineId: 276744, engineId: 59440, employeeId: 4619825";

        Console.Out.Write(description);
        Console.Out.WriteLine();
        var infoList = description.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
        foreach (var info in infoList)
        {
            if (info.TrimStart().StartsWith("machineId", StringComparison.OrdinalIgnoreCase))
            {
                machineIdPart = info.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);

                if (machineIdPart.Count() > 1)
                {
                    long.TryParse(machineIdPart[1].Trim(), out machineId);
                }                                     
            }

            if (info.TrimStart().StartsWith("employeeId", StringComparison.OrdinalIgnoreCase))
            {
                employeeIdPart = info.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);

                if (employeeIdPart.Count() > 1)
                {
                    long.TryParse(employeeIdPart[1].Trim(), out employeeId);
                }
            }
        }   

我想修改此代码以最小化内存分配,因为此方法将非常频繁地运行。

解决方案将比当前稍微复杂一些,但不会有更多的字符串分配。适用于.NETCore2.2

long machineId = 0;
long employeeId = 0;

var description = "machineId: 276744, engineId: 59440, employeeId: 4619825";

ReadOnlySpan<char> descriptionSpan = description.AsSpan();

var nameValueBlockStartIndex = 0;
while(nameValueBlockStartIndex < description.Length)
{
    var blockEndIndex = description.IndexOf(',', nameValueBlockStartIndex);
    if (blockEndIndex == -1)
    {
        blockEndIndex = description.Length;
    }

    var namePartEndIndex = description.IndexOf(':', nameValueBlockStartIndex);
    var namePartLength = namePartEndIndex - nameValueBlockStartIndex;
    var namePart = descriptionSpan.Slice(nameValueBlockStartIndex, namePartLength);

    var valuePartStartIndex = namePartEndIndex + 1;
    var valuePartLength = blockEndIndex - valuePartStartIndex + 1;
    var valuePart = descriptionSpan.Slice(valuePartStartIndex, valuePartLength - 1);

    while(namePart[0] == ' ')
    {
        namePart = namePart.Slice(1);
    }

    if (namePart.Equals("machineId", StringComparison.OrdinalIgnoreCase))
    {
        Int64.TryParse(valuePart, out machineId);
    }
    else if (namePart.Equals("employeeId", StringComparison.OrdinalIgnoreCase))
    {
        Int64.TryParse(valuePart, out employeeId);
    }

    nameValueBlockStartIndex = blockEndIndex + 1;
}

此解决方案适用于.NET Core 2.2。它在ReadOnlySpan SplitNext上使用无分配扩展方法

除了字符串处理优化之外,还可以优化实际的解析函数。这将是一个更快的长解析器:

    public static long LongParseFast(ReadOnlySpan<char> value) {
        long result = 0;
        for (int i = 0; i < value.Length; i++) {
            result = 10 * result + (value[i] - 48);
        }
        return result;
    }

如果在我的示例中使用,在我的基准测试中,它将性能提高一倍,达到216.0 ns。当然,这个函数不能处理负数、逗号、点和其他语言环境的东西。但是,如果您对此没有意见,这可能是您所能达到的最快速度。

您现在是否看到任何性能问题?您会发现,从字符串解析long也不太快。考虑一下,通过跳过字符串寻找,并用x= x* 10 +c-’0来解析自己,避免在一个膨胀的FoopP中的所有字符串操作。如果这仍然是一个性能问题,那么也可以从Span中完成,但在另一个层面上,这也意味着以更有效的方式提取描述。然而,根据Matt-establish,这首先是一个实际的瓶颈,即使您知道它将非常频繁地运行。优化错误的东西是浪费时间。@Matt.G我会说是的。上面的代码将被插入到庞大的处理代码中。在进行上述更改之后,它添加了一个性能开销比较。这并不重要,但仍希望进一步改进以将影响降至最低。@Jeroemostert是的,请参阅我的上述评论。如果您不介意的话,还可以详细介绍一下使用Span的“老式”方法吗?编写一个简单的解析器,跟踪索引位置,而不是实际创建新字符串。对于这样简单的字符串,可以重复调用.IndexOf,不过您也可以编写一个小的状态机开关描述[i]{case':':state=state.ParsingNumber;++i;break;}。其主要思想是让字符串保持原样,而不是创建新的字符串。在不分配任何内容的情况下解析这个字符串可以通过多种方式完成;你甚至不需要为这个深入研究Span。不,对不起,我现在懒得写解析器-汉克斯!这确实是我基准测试中最快的。顺便说一句,您介意告诉我您是如何使用Benchmarkdotnet生成分配内存列的吗?再次感谢!当然只需将ad[MemoryDiagnoser]属性添加到要进行基准测试的类。以下是我的完整基准代码:
BenchmarkDotNet=v0.11.4, OS=Windows 10.0.17763.316 (1809/October2018Update/Redstone5)
Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), 1 CPU, 4 logical and 4 physical cores
.NET Core SDK=2.2.104
  [Host]     : .NET Core 2.2.2 (CoreCLR 4.6.27317.07, CoreFX 4.6.27318.02), 64bit RyuJIT
  DefaultJob : .NET Core 2.2.2 (CoreCLR 4.6.27317.07, CoreFX 4.6.27318.02), 64bit RyuJIT

|   Method |       Mean |     Error |    StdDev | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op |
|--------- |-----------:|----------:|----------:|------------:|------------:|------------:|--------------------:|
| Original | 1,164.1 ns | 11.606 ns | 10.289 ns |      0.2937 |           - |           - |               928 B |
|   Answer |   460.5 ns |  4.527 ns |  4.234 ns |           - |           - |           - |                   - |
| MyAnswer |   445.7 ns |  2.578 ns |  2.412 ns |           - |           - |           - |                   - |
    public static long LongParseFast(ReadOnlySpan<char> value) {
        long result = 0;
        for (int i = 0; i < value.Length; i++) {
            result = 10 * result + (value[i] - 48);
        }
        return result;
    }