编译器使用经典交换对字符串反向方法进行了哪些优化?

时间:2018-01-10 04:49:47

标签: c# compiler-optimization reversing

问题背景

我读了this问题,该问题是关于如何尽快反转字符串。我发现其中一个答案是比较不同的方法。在其中一个中,它们只是运行一个循环交换来自位置function LoadData(data) { var rows_count = 22; -- will be fetched from DB side var rowNum = Math.ceil(parseFloat(rows_count)); var resultHtml = ''; resultHtml += "<table style = \'width:100%;\' border=\'0\' colspan=\'2\' id=\'tbl_user\'>"; for (var i = 0; i <=rowNum-1 ; i++) { resultHtml += '<tr>'; resultHtml += '<td border = 1><input type="name" placeholder="text goes here..."></td>'; resultHtml += '</tr>'; } } resultHtml += '</table>'; } 的元素和位于i的元素,但它们通过XOR使用已知的棘手交换。我想知道使用通过XOR交换字符串的速度是多快,与通过时间变量使用经典交换的相同方法相比。令人惊讶的是,与XOR相比,我的成绩几乎提高了50%。

问题

编译器是否在幕后做了一些魔术,为什么我得到这个结果?

使用基准测试的修改后的代码

string.Length-1-i

3 个答案:

答案 0 :(得分:4)

原因很简单,让我们检查每个函数的IL代码中有多少个操作。 但首先,让我们看看两种功能的真正时间差异。你说XOR函数比其他函数慢了近50%,当我在调试模式下运行代码时我得到了结果,但你必须在Release模式下运行代码才能完全允许优化器完成它的工作:)。在释放模式下,XOR功能几乎慢了3倍。

图片中有for循环内部分的IL代码,这是唯一改变的部分。

第一张照片是带有临时变量的函数

Classic code with the temp variable

第二张图是具有XOR的功能

XOR function

正如您所看到的,指令数量的差异很大,14与34相比。时差3倍来自某些操作,如 conv.u2 ,这些操作有点贵。

答案 1 :(得分:2)

我同意哈罗德的观点。 XOR交换并不比使用临时变量快。

我认为XOR交换的神话可以追溯到为新变量分配内存的日子是一项耗时的工作。

最初我认为这可能与该类型有关,并且在int数组中使用XOR交换可能会比char数组提供更好的结果,所以我&# 39;基于int数组的基准测试构建文本 - 为int得出相同的结果(或多或少)XOR交换比使用临时变量慢。

更新

正如Damien_The_Unbeliever在他对你的问题的评论中写的那样,R. Martinho Fernandes在你所链接的问题中给出的answer实际上是第一页中正确反转字符串的唯一答案,即使是英语以外的语言 事实上,基于该答案和one of it's comments我已经编写了一个扩展方法来正确反转字符串。

用我的母语(希伯来语),我们有各种各样的点和符号来指定元音,而数组的简单反转(或IEnumerable)做错了,就像在法语例子中一样。 / p>

它明显慢于基于常规交换的实现(并且比基于XOR的交换速度慢大约10倍),但我几乎认为这不会成为一个问题,因为反转字符串不是你经常做的事情,在紧密的循环中扭转很多弦,甚至更少 根据您的基准测试方法进行测试,使用此方法反转10,000个长度为10,000的字符串大约需要13.5秒 如果有人想编写一个实际上需要为这么长的字符串做很多反转的程序,我会非常惊讶。

所以这是我的国际安全字符串反向实现,我希望有人能从中受益:

public static string Reverse(this string source)
{
    if (string.IsNullOrEmpty(source))
    {
        return source;
    }
    var info = new StringInfo(source);
    var sb = new StringBuilder();

    for (int i = info.LengthInTextElements - 1; i > -1; i--)
    {
        sb.Append(info.SubstringByTextElements(i, 1));
    }
    return sb.ToString();
}

答案 2 :(得分:0)

您可以使用Array.Reverse测试下面的代码,它会比您提到的其他方法更有效, Array.Reverse本地编码,维护非常简单理解。

    public static string ReverseArray(string text)
    {
        if (text == null) return null;

        char[] array = text.ToCharArray();
        Array.Reverse(array);
        return new String(array);
    }

以下是完整的演示代码,

委托字符串StringDelegate(string s);

    static void Benchmark(string description, StringDelegate d, int times, string text)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        for (int j = 0; j < times; j++)
        {
            d(text);
        }
        sw.Stop();
        Console.WriteLine("{0} Ticks {1} : called {2} times.", sw.ElapsedTicks, description, times);
    }

    public static string ReverseArray(string text)
    {
        if (text == null) return null;

        // this was posted by petebob as well 
        char[] array = text.ToCharArray();
        Array.Reverse(array);
        return new String(array);
    }

    public static string ReverseXor(string s)
    {
        char[] charArray = s.ToCharArray();
        int len = s.Length - 1;

        for (int i = 0; i < len; i++, len--)
        {
            charArray[i] ^= charArray[len];
            charArray[len] ^= charArray[i];
            charArray[i] ^= charArray[len];
        }

        return new string(charArray);
    }

    public static string ReverseClassic(string s)
    {
        char[] charArray = s.ToCharArray();
        int len = s.Length-1;

        for (int i = 0; i < len; i++, len--)
        {
            char temp = charArray[len];
            charArray[len] = charArray[i];
            charArray[i] = temp;
        }
        return new string(charArray);
    }

    public static string StringOfLength(int length)
    {
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++)
        {
            sb.Append(Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65))));
        }
        return sb.ToString();
    }

    static void Main(string[] args)
    {

        int[] lengths = new int[] { 1, 10, 100, 1000, 10000, 100000 };

        foreach (int l in lengths)
        {
            int iterations = 10000;
            string text = StringOfLength(l);                
            Benchmark(String.Format("Classic (Length: {0})", l), ReverseClassic, iterations, text);
            Benchmark(String.Format("Array (Length: {0})", l), ReverseArray, iterations, text);
            Benchmark(String.Format("Xor (Length: {0})", l), ReverseXor, iterations, text);
            Console.WriteLine();
        }
        Console.Read();
    }

注意:我已经更正了用于反转字符串的代码,它没有正确地将字符串反转为您的帖子