【问题标题】:C# file iteration performance - worth bypassing string.IsNullOrEmpty for performance?C# 文件迭代性能 - 值得绕过 string.IsNullOrEmpty 以获得性能?
【发布时间】:2016-04-20 10:57:53
【问题描述】:

目前正在编写一个文件解析器,逐行遍历数据文件并清理数据。性能是此应用程序的一个重要考虑因素。用户为数据列分配标签,让进程知道哪一列代表什么类型的数据——即哪个是姓氏字段,哪个是地址字段,哪个是电话号码等等。

我刚刚写完一堆清理电话号码的代码,并像这样应用它:

public void CleanPhoneFields(FileRow row, List<Mapping> mappings)
{
    // this will return empty if there's no field mapped with the "Telephone Number" tag
    string phoneNumber = GetValueByAssignedLabel(row, mappings, "Telephone Number"); 

    if(!string.IsNullOrEmpty(phoneNumber))
    {
        CleanTelephoneNumber(phoneNumber);
    }
}       

public void ProcessFile(FileContents fileContents)
{
    foreach (FileRow row in fileContents.FileRows)
    {
        // does other cleaning functions too
        CleanPhoneFields(row, fileContents.Mappings, fc);
    }
}

然后我意识到,逐行检查电话字段是不必要的 - 文件中的第一行是真的,所有的都是真的。所以我最好这样做:

public void CleanPhoneFields(FileRow row, List<Mapping> mappings)
{
    // this will return empty if there's no field mapped with the "Telephone Number" tag
    string phoneNumber = GetValueByAssignedLabel(row, mappings, "Telephone Number");
    CleanTelephoneNumber(phoneNumber);
}       

public void ProcessFile(FileContents fileContents)
{
    bool firstLine = true;
    bool cleanPhoneNeeded = false;

    foreach (FileRow row in fileContents.FileRows)
    {
        if(firstLine)
        {
            cleanPhoneNeeded = !string.IsNullOrEmpty(GetValueByAssignedLabel(row, fileContents.Mappings, "Telephone Number"));
            firstLine = false;
        }

        if(cleanPhoneNeeded)
        {
            CleanPhoneFields(row, fileContents.Mappings, fc);
        }
    }
}

我仍然必须去获取每一行的字段值,所以在这种情况下我“保存”的只是摆脱对每一行的 string.IsNullOrEmpty 的调用。另一方面,第二个代码(在我看来)可读性稍差,并且失去了一些防御性编码。

摆脱 string.IsNullOrEmpty 会在处理周期方面为我节省很多吗?第二种方法的小缺点是否值得。还是有更好的方法来解决这个问题?

【问题讨论】:

  • 你量过吗?无论如何,我希望String.IsNullOrEmpty 在微秒内执行。将两个值与零进行比较并取消引用第一个以获取第二个应该不会花费很长时间。
  • @MartinLiversage no - 那将是我的下一步。我首先询问部分是出于好奇,部分是为了确保我在这件事上没有走错路。
  • 第二种效率更高。如果你可以事先确定你不需要工作,那么就去做而不做工作——这对我来说似乎非常明智。

标签: c# string performance


【解决方案1】:

使用 System.Diagnostics 命名空间中的 Stopwatch 类,您可以测量程序执行所需的时间(以毫秒为单位)。

尝试使用和不使用 null 和空检查器(尽管我怀疑有很多可衡量的差异)

在此处找到更多信息: https://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch(v=vs.110).aspx

【讨论】:

    【解决方案2】:

    IMO 调用 string.IsNullOrEmpty 的影响可以忽略不计。

    第二个解决方案的好处是它消除了整个CleanPhoneFields 调用,更重要的是,GetValueByAssignedLabel 调用似乎是最慢的部分(除了无法避免的实际Clean 部分需要时)。

    但是,我会稍微不同地重构原始过程,保持可读性和性能之间的权衡。

    首先,我会让CleanPhoneFields 方法返回bool

    public bool CleanPhoneFields(FileRow row, List<Mapping> mappings)
    {
        // this will return empty if there's no field mapped with the "Telephone Number" tag
        string phoneNumber = GetValueByAssignedLabel(row, mappings, "Telephone Number"); 
        if(string.IsNullOrEmpty(phoneNumber)) return false;
        CleanTelephoneNumber(phoneNumber);
        return true;
    }
    

    那么main方法可能是这样的:

    public void ProcessFile(FileContents fileContents)
    {
        bool cleanPhoneFields = true;
        foreach (FileRow row in fileContents.FileRows)
        {
            if (cleanPhoneFields)
                cleanPhoneFields = CleanPhoneFields(row, fileContents.Mappings, fc);
            // Other stuff
        }
    }
    

    【讨论】:

      【解决方案3】:

      string.IsNullOrEmpty 几乎没有任何成本(反正你不用担心)

      另一方面,您可能想做的是将“电话号码”声明为 private const 字段,以防止每次调用 CleanPhoneFields 方法时创建它。

      您也可以使用string.Intern 来防止这种情况...

      【讨论】:

        【解决方案4】:

        在第二个解决方案中,每次检查两个条件加上第一行的空条件检查时,循环内部,而不是在第一个解决方案中,只有一个空条件检查,而且更干净。理论上第一个解决方案似乎更好。

        正如@Harry Young 所说,使用Stopwatch 来衡量性能,如果你使用VS2015,在调试模式下它会衡量性能。

        【讨论】:

          猜你喜欢
          • 2014-07-22
          • 1970-01-01
          • 1970-01-01
          • 2013-02-04
          • 1970-01-01
          • 1970-01-01
          • 2021-11-27
          • 1970-01-01
          • 2017-12-26
          相关资源
          最近更新 更多