C# 在对字符串中的字符进行转义时,首先测试是否需要转义会更快吗?

C# 在对字符串中的字符进行转义时,首先测试是否需要转义会更快吗?,c#,performance,string-comparison,C#,Performance,String Comparison,如果没有任何要转义的字符,则 if (s.Contains("\"")) s = s.Replace("\"", "\"\""); 真的比你跑得快吗 s = s.Replace("\"", "\"\""); Contains方法必须像Replace方法一样搜索字符串,如果Replace方法没有找到任何要转义的内容,那么我认为它不应该比Contains方法搜索字符串花费更长的时间。但是如果有一个字符需要转义,那么您必须在字符串中搜索两次。这取决于需要修复的字符串与所有字符串的比率。

如果没有任何要转义的字符,则

if (s.Contains("\"")) 
    s = s.Replace("\"", "\"\""); 
真的比你跑得快吗

s = s.Replace("\"", "\"\"");

Contains方法必须像Replace方法一样搜索字符串,如果Replace方法没有找到任何要转义的内容,那么我认为它不应该比Contains方法搜索字符串花费更长的时间。但是如果有一个字符需要转义,那么您必须在字符串中搜索两次。

这取决于需要修复的字符串与所有字符串的比率。如果必须修正很少的条件,这肯定是一场胜利。虽然“替换”在没有找到匹配项的情况下肯定不会分配新字符串,但赢得的机会将非常小。这是一个常见的优化


不过,盈亏平衡点无法预测。测量。

这取决于需要修复的字符串与所有字符串的比率。如果必须修正很少的条件,这肯定是一场胜利。虽然“替换”在没有找到匹配项的情况下肯定不会分配新字符串,但赢得的机会将非常小。这是一个常见的优化


不过,盈亏平衡点无法预测。测量。

与所有绩效相关的问题一样,答案是测量


它取决于
Replace
的智能程度(它是否“提前”分配内存?如果是,性能影响有多大?),取决于需要替换的字符串与处理的字符串总数的比率,取决于字符串的长度,第一个
的索引位于哪个位置(
Contains
如果能够更快地返回
true
)等

与所有性能相关的问题一样,答案是measure

它取决于
Replace
的智能程度(它是否“提前”分配内存?如果是,性能影响有多大?),取决于需要替换的字符串与处理的字符串总数的比率,取决于字符串的长度,第一个
的索引位于哪个位置(
Contains
如果能够更快地返回
true
)等。

此方法执行顺序(区分大小写和 不区分区域性)搜索以查找oldValue

此方法执行顺序(区分大小写和 (不区分区域性)比较。搜索从第一个 此字符串的字符位置,并持续到最后一个字符 角色位置

所以,我要说的是没有实际的区别。不过,我会在这里运行一个微观基准测试


微观基准测试(伙计,我喜欢这个)

守则:

        List<string> StringList = new List<string>();

        for (int i = 0; i < 10000; i++)
        {
            StringList.Add(DateTime.Now.Ticks + " abc ");
        }

        string temp;

        Dictionary<string, string> StringsToReplace = new Dictionary<string, string>();
        StringsToReplace.Add("1", ".1.");
        StringsToReplace.Add("a", "z");
        StringsToReplace.Add("b", "x");
        StringsToReplace.Add("c", "v");
        StringsToReplace.Add("d", "u");
        StringsToReplace.Add("e", "t");

        long ReplaceElapsedTime = 0;
        long ContainsReplaceElapsedTime = 0;

        int TotalIterations = 10000;

        for (int j = 0; j < TotalIterations; j++)
        {
            Stopwatch stopWatch = new Stopwatch();
            stopWatch.Start();

            for (int i = 0; i < StringList.Count; i++)
            {
                foreach (KeyValuePair<string, string> CurrentPair in StringsToReplace)
                {
                    temp = StringList[i].Replace(CurrentPair.Value, CurrentPair.Key);
                }
            }

            stopWatch.Stop();

            ReplaceElapsedTime += stopWatch.ElapsedMilliseconds;

            stopWatch.Reset();
            stopWatch.Start();

            for (int i = 0; i < StringList.Count; i++)
            {
                foreach (KeyValuePair<string, string> CurrentPair in StringsToReplace)
                {
                    if (StringList[i].Contains(CurrentPair.Value))
                        temp = StringList[i].Replace(CurrentPair.Value, CurrentPair.Key);
                }
            }

            stopWatch.Stop();

            ContainsReplaceElapsedTime += stopWatch.ElapsedMilliseconds;
        }

        Console.WriteLine(string.Format("Replace: {0} ms", ReplaceElapsedTime/TotalIterations));
        Console.WriteLine(string.Format("Contains > Replace: {0} ms", ContainsReplaceElapsedTime/TotalIterations));

        Console.ReadLine();
=)

此方法执行顺序(区分大小写和 不区分区域性)搜索以查找oldValue

此方法执行顺序(区分大小写和 (不区分区域性)比较。搜索从第一个 此字符串的字符位置,并持续到最后一个字符 角色位置

所以,我要说的是没有实际的区别。不过,我会在这里运行一个微观基准测试


微观基准测试(伙计,我喜欢这个)

守则:

        List<string> StringList = new List<string>();

        for (int i = 0; i < 10000; i++)
        {
            StringList.Add(DateTime.Now.Ticks + " abc ");
        }

        string temp;

        Dictionary<string, string> StringsToReplace = new Dictionary<string, string>();
        StringsToReplace.Add("1", ".1.");
        StringsToReplace.Add("a", "z");
        StringsToReplace.Add("b", "x");
        StringsToReplace.Add("c", "v");
        StringsToReplace.Add("d", "u");
        StringsToReplace.Add("e", "t");

        long ReplaceElapsedTime = 0;
        long ContainsReplaceElapsedTime = 0;

        int TotalIterations = 10000;

        for (int j = 0; j < TotalIterations; j++)
        {
            Stopwatch stopWatch = new Stopwatch();
            stopWatch.Start();

            for (int i = 0; i < StringList.Count; i++)
            {
                foreach (KeyValuePair<string, string> CurrentPair in StringsToReplace)
                {
                    temp = StringList[i].Replace(CurrentPair.Value, CurrentPair.Key);
                }
            }

            stopWatch.Stop();

            ReplaceElapsedTime += stopWatch.ElapsedMilliseconds;

            stopWatch.Reset();
            stopWatch.Start();

            for (int i = 0; i < StringList.Count; i++)
            {
                foreach (KeyValuePair<string, string> CurrentPair in StringsToReplace)
                {
                    if (StringList[i].Contains(CurrentPair.Value))
                        temp = StringList[i].Replace(CurrentPair.Value, CurrentPair.Key);
                }
            }

            stopWatch.Stop();

            ContainsReplaceElapsedTime += stopWatch.ElapsedMilliseconds;
        }

        Console.WriteLine(string.Format("Replace: {0} ms", ReplaceElapsedTime/TotalIterations));
        Console.WriteLine(string.Format("Contains > Replace: {0} ms", ContainsReplaceElapsedTime/TotalIterations));

        Console.ReadLine();
=)

如果有人好奇(相信我,你不好奇),那么要找出需要替换OP方法以提高效率的字符串的必要百分比,方程式(大致)如下:

其中:

C = Time it takes to create a new string
R = average Ratio of Delimiter position to string length
n = average string length
p = average position of the delimiter (quote in this case) in the string
t = time it takes to search one character of a string
因此,如果字符串中有引号的百分比大于此值,请使用第一种方法

这并没有考虑到我认为可以忽略不计的变量,例如返回时间、字符的实际替换(尽管您可以在
C
中包含这些变量),或者我不知道的任何类型的编译器优化

另一方面:我刚刚花了半个小时解了这个世界上最没用的方程,这就是我为什么从数学转向计算机科学的一个完美例子

如果有人好奇(相信我,你不好奇),那么找出OP方法更有效所需字符串百分比的公式(大致)如下:

其中:

C = Time it takes to create a new string
R = average Ratio of Delimiter position to string length
n = average string length
p = average position of the delimiter (quote in this case) in the string
t = time it takes to search one character of a string
因此,如果字符串中有引号的百分比大于此值,请使用第一种方法

这并没有考虑到我认为可以忽略不计的变量,例如返回时间、字符的实际替换(尽管您可以在
C
中包含这些变量),或者我不知道的任何类型的编译器优化


另一方面:我刚刚花了半个小时解了这个世界上最没用的方程,这就是我为什么从数学转向计算机科学的一个完美例子

一些基本的性能测试,每10000次(使用一个大约30个字符的字符串,中间有一个引号),给出:

Contains
调用所需时间约为
Replace
调用的一半。我原以为查找字符会比查找字符串快,但令人惊讶的是,事实并非如此

因此,比较“先检查”和“始终更换”以获得收支平衡的情况,其中x是
包含的调用时间,y是您需要进行更换的频率:

x + 2xy = 2x
给出:

y = 0.5

这意味着这两种方法的盈亏平衡率约为50%。如果您需要在超过一半的时间内进行替换,那么首先检查是否存在是没有好处的。

一些基本性能测试,每进行10000次(字符串大约30个字符,中间有一个引号),会给出:

Contains
调用所需时间约为
Replace
调用的一半。我原以为查找字符会比查找字符串快,但令人惊讶的是,事实并非如此

因此,比较先检查和总是更换的情况,以获得收支平衡,其中x是故障发生的时间
        string test = "A quick brown fox jumps over a lazy dog.";

        int count = 1000 * 1000;

        Stopwatch watch = new Stopwatch();

        for (int i = 0; i < 4; i++)
        {
            string result = String.Empty;

            watch.Restart();

            for (int c = 0; c < count; c++)
            {
                switch (i)
                {
                    case 0: // warmup
                        break;

                    case 1:
                        if (test.Contains("\""))
                        {
                            result = test.Replace("\"", "\"\"");
                        }
                        break;

                    case 2:
                        result = test.Replace("\"", "\"\"");
                        break;

                    case 3:
                        if (test.IndexOf('\"') >= 0)
                        {
                            result = test.Replace("\"", "\"\"");
                        }
                        break;
                }
            }

            watch.Stop();

            Console.WriteLine("Test {0,16} {1,7:N3} ms {2}", 
                new string[]{"Base","Contains-Replace","Replace","IndexOf-Replace"}[i],
                watch.Elapsed.TotalMilliseconds,
                result);
Test             Base   3.026 ms
Test Contains-Replace 284.780 ms
Test          Replace 214.503 ms
Test  IndexOf-Replace  64.447 ms
        List<string> StringList = new List<string>();

        for (int i = 0; i < 10000; i++)
        {
            StringList.Add(DateTime.Now.Ticks + " abc ");
        }

        string temp;

        KeyValuePair<string, string>[] StringsToReplace = new KeyValuePair<string, string>[6];

        // Use array to avoid dictionary access cost
        StringsToReplace[0] = new KeyValuePair<string,string>("1", ".1.");
        StringsToReplace[1] = new KeyValuePair<string,string>("a", "z");
        StringsToReplace[2] = new KeyValuePair<string,string>("b", "x");
        StringsToReplace[3] = new KeyValuePair<string,string>("c", "v");
        StringsToReplace[4] = new KeyValuePair<string,string>("d", "u");
        StringsToReplace[5] = new KeyValuePair<string,string>("e", "t");

        int TotalIterations = 100;
        Stopwatch stopWatch1 = new Stopwatch();
        Stopwatch stopWatch2 = new Stopwatch();

        GC.Collect(); // remove influence of garbage objects

        for (int j = 0; j <= TotalIterations; j++)
        {
            stopWatch1.Start(); // StopWatch Start/Stop does its own accumation

            for (int i = 0; i < StringList.Count; i++)
            {
                for (int k = 0; k < StringsToReplace.Length; k++)
                {
                    temp = StringList[i].Replace(StringsToReplace[k].Value, StringsToReplace[k].Key);
                }
            }

            stopWatch1.Stop();

            stopWatch2.Start();

            for (int i = 0; i < StringList.Count; i++)
            {
                for (int k = 0; k < StringsToReplace.Length; k++)
                {
                    if (StringList[i].Contains(StringsToReplace[k].Value))
                        temp = StringList[i].Replace(StringsToReplace[k].Value, StringsToReplace[k].Key);
                }
            }

            stopWatch2.Stop();

            if (j == 0) // discard first run, warm only
            {
                stopWatch1.Reset();
                stopWatch2.Reset();
            }
        }

        // Elapsed.TotalMilliseconds return in double, more accurate than ElapsedMilliseconds
        Console.WriteLine("Replace           : {0:N3} ms", stopWatch1.Elapsed.TotalMilliseconds / TotalIterations);
        Console.WriteLine("Contains > Replace: {0:N3} ms", stopWatch2.Elapsed.TotalMilliseconds / TotalIterations);
Replace           : 7.453 ms
Contains > Replace: 8.381 ms