Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/321.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# 性能问题:与String.Format比较_C#_Performance_String - Fatal编程技术网

C# 性能问题:与String.Format比较

C# 性能问题:与String.Format比较,c#,performance,string,C#,Performance,String,不久前,Jon Skeet的一篇文章在我脑海中灌输了这样一个想法:构建一个编译格式化程序类,用于循环中,而不是String.Format() 其思想是调用String.Format()解析格式字符串所花费的部分开销;我们应该能够通过将代码移出循环来提高性能。当然,诀窍在于新代码应该与String.Format()行为完全匹配 这周我终于做到了。我通过使用来直接调整他们的解析器(结果是String.Format()实际上将工作转移到StringBuilder.AppendFormat())。我提出

不久前,Jon Skeet的一篇文章在我脑海中灌输了这样一个想法:构建一个
编译格式化程序
类,用于循环中,而不是
String.Format()

其思想是调用
String.Format()
解析格式字符串所花费的部分开销;我们应该能够通过将代码移出循环来提高性能。当然,诀窍在于新代码应该与
String.Format()
行为完全匹配

这周我终于做到了。我通过使用来直接调整他们的解析器(结果是
String.Format()
实际上将工作转移到
StringBuilder.AppendFormat()
)。我提出的代码是有效的,因为我的结果在我的(公认有限的)测试数据中是准确的

不幸的是,我还有一个问题:性能。在我的初始测试中,代码的性能与正常的
String.Format()
非常匹配。没有任何改善;它甚至持续地慢了几毫秒。至少它的顺序是一样的(即:较慢的数量不会增加;即使测试集增长,它也会保持在几毫秒之内),但我希望有更好的东西

StringBuilder.Append()
的内部调用可能是真正推动性能的因素,但我想看看这里的聪明人是否可以帮助改进

以下是相关部分:

private class FormatItem
{
    public int index; //index of item in the argument list. -1 means it's a literal from the original format string
    public char[] value; //literal data from original format string
    public string format; //simple format to use with supplied argument (ie: {0:X} for Hex

    // for fixed-width format (examples below) 
    public int width;    // {0,7} means it should be at least 7 characters   
    public bool justify; // {0,-7} would use opposite alignment
}

//this data is all populated by the constructor
private List<FormatItem> parts = new List<FormatItem>(); 
private int baseSize = 0;
private string format;
private IFormatProvider formatProvider = null;
private ICustomFormatter customFormatter = null;

// the code in here very closely matches the code in the String.Format/StringBuilder.AppendFormat methods.  
// Could it be faster?
public String Format(params Object[] args)
{
    if (format == null || args == null)
        throw new ArgumentNullException((format == null) ? "format" : "args");

    var sb = new StringBuilder(baseSize);
    foreach (FormatItem fi in parts)
    {
        if (fi.index < 0)
            sb.Append(fi.value);
        else
        {
            //if (fi.index >= args.Length) throw new FormatException(Environment.GetResourceString("Format_IndexOutOfRange"));
            if (fi.index >= args.Length) throw new FormatException("Format_IndexOutOfRange");

            object arg = args[fi.index];
            string s = null;
            if (customFormatter != null)
            {
                s = customFormatter.Format(fi.format, arg, formatProvider);
            }

            if (s == null)
            {
                if (arg is IFormattable)
                {
                    s = ((IFormattable)arg).ToString(fi.format, formatProvider);
                }
                else if (arg != null)
                {
                    s = arg.ToString();
                }
            }

            if (s == null) s = String.Empty;
            int pad = fi.width - s.Length;
            if (!fi.justify && pad > 0) sb.Append(' ', pad);
            sb.Append(s);
            if (fi.justify && pad > 0) sb.Append(' ', pad);
        }
    }
    return sb.ToString();
}

//alternate implementation (for comparative testing)
// my own test call String.Format() separately: I don't use this.  But it's useful to see
// how my format method fits.
public string OriginalFormat(params Object[] args)
{
    return String.Format(formatProvider, format, args);
}
私有类格式化项
{
public int index;//参数列表中项的索引,-1表示它是原始格式字符串中的文本
public char[]值;//原始格式字符串中的文字数据
公共字符串格式;//与提供的参数一起使用的简单格式(即:{0:X}表示十六进制
//对于固定宽度格式(以下示例)
公共int-width;/{0,7}表示它至少应该是7个字符
公共bool justify;//{0,-7}将使用相反的对齐方式
}
//此数据全部由构造函数填充
私有列表零件=新列表();
私有int baseSize=0;
私有字符串格式;
私有IFormatProvider formatProvider=null;
私有ICustomFormatter customFormatter=null;
//这里的代码与String.Format/StringBuilder.AppendFormat方法中的代码非常匹配。
//能快点吗?
公共字符串格式(参数对象[]参数)
{
if(format==null | | args==null)
抛出新ArgumentNullException((format==null)?“format”:“args”);
var sb=新的StringBuilder(基本尺寸);
foreach(格式项目fi部分)
{
如果(fi.index<0)
某人附加(金融价值);
其他的
{
//如果(fi.index>=args.Length)抛出新的FormatException(Environment.GetResourceString(“Format_IndexOutFrange”);
如果(fi.index>=args.Length)抛出新的FormatException(“Format_IndexOutFrange”);
对象arg=args[fi.index];
字符串s=null;
if(customFormatter!=null)
{
s=customFormatter.Format(fi.Format,arg,formatProvider);
}
如果(s==null)
{
if(arg可附加)
{
s=((IFormattable)arg.ToString(fi.format,formatProvider);
}
else if(arg!=null)
{
s=arg.ToString();
}
}
如果(s==null)s=String.Empty;
int pad=fi.宽度-s.长度;
如果(!fi.justify&&pad>0)某人追加(“”,pad);
某人追加;
如果(fi.justify&&pad>0)某人追加(“”,pad);
}
}
使某人返回字符串();
}
//替代实现(用于比较测试)
//单独使用我自己的测试调用String.Format():我不使用它,但它很有用
//我的格式方法如何适合。
公共字符串原始格式(参数对象[]args)
{
返回String.Format(formatProvider、Format、args);
}
补充说明: 我对为构造函数提供源代码持谨慎态度,因为我不确定依赖原始.Net实现会带来什么样的许可影响。但是,任何想要测试这一点的人都可以将相关的私有数据公开,并分配模拟特定格式字符串的值

此外,如果有人提出可以改进构建时间的建议,我非常愿意更改
FormatInfo
类,甚至更改
部分列表。因为我主要关心的是从前端到后端的顺序迭代时间,所以
LinkedList
会更好吗

[更新]: 嗯……我还可以尝试调整我的测试。我的基准测试相当简单:将名称组合成
“{lastname},{firstname}”
从区号、前缀、号码和分机组件格式化和组合格式化的电话号码。这两个组件在字符串中都没有太多的文字段。当我思考原始状态机解析器的工作方式时,我认为这些文字段正是我的代码最有可能做好的地方,因为se我不再需要检查字符串中的每个字符

另一个想法是:
这个类仍然很有用,即使我不能让它运行得更快。只要性能不低于基本字符串。Format(),我仍然创建了一个强类型接口,允许程序组装自己的“格式字符串”在运行时。我所需要做的就是提供对部件列表的公共访问。

您也考虑过进行JIT编译的时间吗?毕竟,框架将被更新,这可以解释差异?

在我看来,为了获得实际的性能改进,您需要考虑任何格式分析将customFormatter和formattable参数转换为一个函数,该函数返回一些数据结构,告诉以后的格式化调用要执行的操作
StringFormatter f = StringFormatter.parse(
   "the quick brown {animal} jumped over the {attitude} dog"
);
String s = f.format(myMap);
// After calling obj.toString(), all space characters in the formatted
// object string are converted to underscores.
StringFormatter f = StringFormatter.parse(
   "blah blah blah {0:/\\s+/_/} blah blah blah"
);
StringFormatter f = StringFormatter.parse(
   "blah blah blah {0:?'NULL'|'NOT NULL'} blah blah blah"
);
// Wraps each elements in single-quote charts, separating
// adjacent elements with a comma.
StringFormatter f = StringFormatter.parse(
   "blah blah blah {0:@['$'][,]} blah blah blah"
);