jjzjj

c# - 性能问题 : comparing to String. 格式

coder 2024-05-21 原文

不久前,Jon Skeet 的一篇文章在我脑海中植入了构建一个 CompiledFormatter 类的想法,用于在循环中使用而不是 String.Format()

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

这周我终于做到了。我经历了使用 .Net framework source provided by Microsoft直接调整他们的解析器(事实证明 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);
}
补充笔记:

我对提供我的构造函数的源代码持谨慎态度,因为我不确定我对原始 .Net 实现的依赖所带来的许可影响。然而,任何想要对此进行测试的人都可以将相关的私有(private)数据公开并分配模仿特定格式字符串的值。

此外,如果有人提出可以缩短构建时间的建议,我非常愿意更改 FormatInfo 类,甚至更改 parts List。由于我主要关心的是从前端到端的顺序迭代时间,也许 LinkedList 会更好?

[更新]:

嗯...我可以尝试的其他事情是调整我的测试。我的基准测试相当简单:将姓名组成 "{lastname}, {firstname}" 格式,并根据区号、前缀、号码和分机号组成格式化的电话号码。这些都没有太多影响字符串中的文字段。当我思考原始状态机解析器的工作方式时,我认为这些文字段正是我的代码最有可能做好的地方,因为我不再需要检查字符串中的每个字符。

另一个想法:

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

最佳答案

这是最终结果:

我在基准试验中将格式字符串更改为更适合我的代码的内容:

The quick brown {0} jumped over the lazy {1}.

如我所料,这比原来的票价要好得多;此代码在 5.3 秒内进行了 200 万次迭代,而 String.Format 则为 6.1 秒。这是不可否认的改进。您甚至可能想开始使用它作为许多 String.Format 情况下的简单替代品。毕竟,您不会做得更糟,甚至可能会获得小幅性能提升:多达 14%,这没什么好小瞧的。

除此之外。请记住,在专门为支持此代码而设计的情况下,我们仍在谈论 2 百万 次尝试的差异小于半秒。即使是繁忙的 ASP.Net 页面也不太可能产生那么大的负载,除非您有幸在排名前 100 的网站上工作。

最重要的是,这忽略了一个重要的替代方案:您可以每次创建一个新的 StringBuilder 并使用原始 Append() 调用手动处理您自己的格式。使用该技术,我的基准测试在仅 3.9 秒内完成。这是一个更大的改进。


总而言之,如果性能不是那么重要,您应该坚持使用内置选项的清晰和简单。但是,当分析显示这确实在提高性能时,可以通过 StringBuilder.Append() 获得更好的替代方案。

关于c# - 性能问题 : comparing to String. 格式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/761121/

有关c# - 性能问题 : comparing to String. 格式的更多相关文章

  1. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  2. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

  3. ruby - 通过 rvm 升级 ruby​​gems 的问题 - 2

    尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub

  4. ruby - 通过 RVM (OSX Mountain Lion) 安装 Ruby 2.0.0-p247 时遇到问题 - 2

    我的最终目标是安装当前版本的RubyonRails。我在OSXMountainLion上运行。到目前为止,这是我的过程:已安装的RVM$\curl-Lhttps://get.rvm.io|bash-sstable检查已知(我假设已批准)安装$rvmlistknown我看到当前的稳定版本可用[ruby-]2.0.0[-p247]输入命令安装$rvminstall2.0.0-p247注意:我也试过这些安装命令$rvminstallruby-2.0.0-p247$rvminstallruby=2.0.0-p247我很快就无处可去了。结果:$rvminstall2.0.0-p247Search

  5. ruby - Fast-stemmer 安装问题 - 2

    由于fast-stemmer的问题,我很难安装我想要的任何ruby​​gem。我把我得到的错误放在下面。Buildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingfast-stemmer:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcreatingMakefilemake"DESTDIR="cleanmake"DESTDIR=

  6. ruby - 安装 Ruby 时遇到问题(无法下载资源 "readline--patch") - 2

    当我尝试安装Ruby时遇到此错误。我试过查看this和this但无济于事➜~brewinstallrubyWarning:YouareusingOSX10.12.Wedonotprovidesupportforthispre-releaseversion.Youmayencounterbuildfailuresorotherbreakages.Pleasecreatepull-requestsinsteadoffilingissues.==>Installingdependenciesforruby:readline,libyaml,makedepend==>Installingrub

  7. ruby-on-rails - 将 Ruby 中的日期/时间格式化为 YYYY-MM-DD HH :MM:SS - 2

    这个问题在这里已经有了答案:Railsformattingdate(4个答案)关闭4年前。我想格式化Time.Now函数以显示YYYY-MM-DDHH:MM:SS而不是:“2018-03-0909:47:19+0000”该函数需要放在时间中.现在功能。require‘roo’require‘roo-xls’require‘byebug’file_name=ARGV.first||“Template.xlsx”excel_file=Roo::Spreadsheet.open(“./#{file_name}“,extension::xlsx)xml=Nokogiri::XML::Build

  8. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  9. ruby - 我可以将我的 README.textile 以正确的格式放入我的 RDoc 中吗? - 2

    我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:

  10. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

随机推荐