在没有任何字符要转义的情况下,将
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 方法花费的时间更长细绳。但是,如果有一个字符要转义,那么您必须在字符串中搜索两次。
一些基本的性能测试,每进行 10 000 000 次(大约 30 个字符的字符串,中间有一个引号),给出:
Contains("\"") 1327 ms.
Contains('"') 2949 ms.
Replace("\"", "\"\"") 2528 m.s
Contains
通话大约需要通话时间的一半Replace
。我认为寻找一个字符会比寻找一个字符串更快,但令人惊讶的是事实并非如此。
因此,比较首先检查并始终更换以达到收支平衡的情况,其中 x 是Contains
调用时间,y 是您需要进行更换的频率:
x + 2xy = 2x
给出:
y = 0.5
这意味着这两种方法的收支平衡在 50% 左右。如果您需要在超过一半的时间内进行替换,则首先检查存在性没有任何好处。
Andre Calil 的微基准修改:
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,因为 50% 的时间需要真正的替换:
Replace : 7.453 ms
Contains > Replace: 8.381 ms
如果有人好奇(你不是,相信我),找出需要替换 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
),或者我不知道的任何类型的编译器优化。
附带说明:我只花了半个小时解决世界上最没用的方程式,这就是我为什么从数学转向 CS 的一个完美例子。
新版本:
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
所以 Contains(string) 本身很慢。原因是由于 NLS(自然语言处理 API。但 IndexOf(char) 快得多。
这取决于需要修复的字符串与所有字符串的比率。如果必须修复很少的字符串,这肯定是一个胜利。尽管如果没有找到任何匹配项,Replace 肯定不会分配新字符串,但胜利将非常小。这是一个常见的优化。
但是,无法预测收支平衡点。措施。
与所有与性能相关的问题一样,答案是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();
结果:
Replace: 14 ms
Contains > Replace: 14 ms
=)