5 вариантов unescape - угадайте какой самый быстрый (неожида
От: Shmj Ниоткуда  
Дата: 29.11.19 23:09
Оценка:
Нужно чтобы в строке не было символа '\n'. Для этого:

1. Заменяем все "\\" на "\\\\".
2. Заменяем все "\n" на "\\n".

Это все понятно. Самый смак — как возвернуть все взад...

Накидал 5 вариантов, см. полный код:

class Program
    {
        static void Main(string[] args)
        {
            Random r = new Random();

            var sb = new StringBuilder(2000000 * 7);

            sb.Append("test1\ntest2\\n\\\\test3");

            for (int i = 0; i < 2000000; i++)
            {
                if (r.Next(1, 3) == 1)
                    sb.Append("\\");

                if (r.Next(1, 3) == 1)
                    sb.Append("\\");

                if (r.Next(1, 3) == 1)
                    sb.Append("\\n");

                if (r.Next(1, 4) == 3)
                    sb.Append("\n");

                sb.Append("word");
            }

            var input = sb.ToString();
            
            var escaped = input.Replace("\\", "\\\\").Replace("\n", "\\n");

            var sw = new Stopwatch();
            
            sw.Reset();
            sw.Start();

            //var unescaped = Test1(escaped);
            //var unescaped = Test2(escaped);
            //var unescaped = Test3(escaped);
            //var unescaped = Test4(escaped);
            var unescaped = Test5(escaped);

            sw.Stop();

            Console.WriteLine(sw.ElapsedMilliseconds);
            Console.ReadLine();
            Console.WriteLine(unescaped.Equals(input));
        }

        static string Test1(string escaped)
        {
            var guid = Guid.NewGuid().ToString();
            return escaped.Replace("\\\\", guid).Replace("\\n", "\n").Replace(guid, "\\");
        }

        static string Test2(string escaped)
        {
            return escaped.Split(new[] {"\\\\"}, StringSplitOptions.None)
                .Aggregate((seed, next) => seed + "\\" + next.Replace("\\n", "\n"));
        }

        static string Test3(string escaped)
        {
            var parts = escaped.Split(new[] { "\\\\" }, StringSplitOptions.None).Select(part => part.Replace("\\n", "\n"));
            return string.Join("\\", parts);
        }

        static string Test4(string escaped)
        {
            var unescaped = new StringBuilder(escaped.Length);

            bool slash = false;

            foreach (var ch in escaped)
            {
                if (slash)
                {
                    if ('\\' == ch)
                        unescaped.Append('\\');

                    if ('n' == ch)
                        unescaped.Append('\n');

                    slash = false;
                    continue;
                }

                if ('\\' == ch)
                    slash = true;
                else
                    unescaped.Append(ch);
            }

            return unescaped.ToString();
        }

        static string Test5(string escaped)
        {
            var unescaped = new StringBuilder(escaped.Length);

            int currentIndex = 0;

            while (true)
            {
                var slashIndex = escaped.IndexOf('\\', currentIndex);

                if (-1 == slashIndex)
                {
                    unescaped.Append(escaped, currentIndex, escaped.Length - currentIndex);
                    break;
                }

                unescaped.Append(escaped, currentIndex, slashIndex - currentIndex);
                currentIndex = slashIndex;

                var nextChar = escaped[slashIndex + 1];

                switch (nextChar)
                {
                    case '\\':
                        unescaped.Append('\\');
                        break;
                    case 'n':
                        unescaped.Append('\n');
                        break;
                    default:
                        throw new InvalidOperationException("nextChar=" + nextChar);
                }

                currentIndex++;
                currentIndex++;
            }

            return unescaped.ToString();
        }
    }


Сокращенная версия для запуска: https://dotnetfiddle.net/52uORv

И вопрос: как вы думаете, какой вариант самый быстрый? Как сделать быстрее?
Отредактировано 29.11.2019 23:13 Shmj . Предыдущая версия .
escape
Re: 5 вариантов unescape - угадайте какой самый быстрый (нео
От: Sharowarsheg  
Дата: 29.11.19 23:47
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Нужно чтобы в строке не было символа '\n'. Для этого:

S>Накидал 5 вариантов, см. полный код:

S>
S>class Program
S>    {
            
S>            var escaped = input.Replace("\\", "\\\\").Replace("\n", "\\n");

S>            var sw = new Stopwatch();
            
S>            sw.Reset();
S>            sw.Start();

S>            //var unescaped = Test1(escaped);
S>            //var unescaped = Test2(escaped);
S>            //var unescaped = Test3(escaped);
S>            //var unescaped = Test4(escaped);
S>            var unescaped = Test5(escaped);

S>            sw.Stop();

S>            Console.WriteLine(sw.ElapsedMilliseconds);
S>            Console.ReadLine();
S>            Console.WriteLine(unescaped.Equals(input));
S>        }

S>И вопрос: как вы думаете, какой вариант самый быстрый? Как сделать быстрее?

Она что у тебя там, всего один раз выполняется?
Отредактировано 29.11.2019 23:56 Sharowarsheg . Предыдущая версия .
Re[2]: 5 вариантов unescape - угадайте какой самый быстрый (нео
От: Shmj Ниоткуда  
Дата: 29.11.19 23:58
Оценка:
Здравствуйте, Sharowarsheg, Вы писали:

S>Она что у тебя там, всего один раз выполняется?


Один раз, но строка достаточно длинная.
Re[3]: 5 вариантов unescape - угадайте какой самый быстрый (
От: Sharowarsheg  
Дата: 30.11.19 00:42
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Здравствуйте, Sharowarsheg, Вы писали:


S>>Она что у тебя там, всего один раз выполняется?


S>Один раз, но строка достаточно длинная.


Нет, нужно в тыщу раз больше, по крайней мере. Кроме того, это разные режимы — один раз и очень длинная строка, или много раз маленькие строки. В любом случае, самый быстрый будет тот, в котором больше всего for int i=0.... и меньше всего всяких прогрессивных LINQ и проча. Не исключено, что еще быстрее будет переделать строку в массив слов Uint16 и с ними работать.
Отредактировано 30.11.2019 0:47 Sharowarsheg . Предыдущая версия .
Re: 5 вариантов unescape - угадайте какой самый быстрый (неожида
От: adetkov Россия  
Дата: 30.11.19 01:52
Оценка: 3 (1)
Здравствуйте, Shmj, Вы писали:

S>И вопрос: как вы думаете, какой вариант самый быстрый? Как сделать быстрее?


Есть еще такой вариант:
private static string Unescape(string str)
{
    char[] array = str.ToCharArray();
    int cur = 0;
    bool waitControl = false;

    for (int i = 0; i < array.Length; i++)
    {
        if (!waitControl)
        {
            if (array[i] == '\\')
            {
                waitControl = true;
                continue;
            }

            array[cur++] = array[i];
            continue;
        }

        waitControl = false;
        array[cur++] = array[i] switch
        {
            'n' => '\n',
            '\\' => '\\',
            _ => throw new Exception("Unexpected control symbol")
        };
    }

    return new string(array, 0, cur);
}
Re[2]: 5 вариантов unescape - угадайте какой самый быстрый (неожида
От: Shmj Ниоткуда  
Дата: 30.11.19 02:04
Оценка:
Здравствуйте, adetkov, Вы писали:

S>>И вопрос: как вы думаете, какой вариант самый быстрый? Как сделать быстрее?


A>Есть еще такой вариант:


По логике похож на Test4.
Re[2]: 5 вариантов unescape - угадайте какой самый быстрый (неожида
От: Shmj Ниоткуда  
Дата: 30.11.19 02:09
Оценка:
Здравствуйте, adetkov, Вы писали:

A>Есть еще такой вариант:


А теперь сравните его по скорости с вариантом Test5. Как думаете — какой быстрее?
Re[3]: 5 вариантов unescape - угадайте какой самый быстрый (неожида
От: adetkov Россия  
Дата: 30.11.19 02:33
Оценка:
Здравствуйте, Shmj, Вы писали:

S>А теперь сравните его по скорости с вариантом Test5. Как думаете — какой быстрее?


BenchmarkDotNet=v0.12.0, OS=Windows 8.1 (6.3.9600.0)

Intel Core i7-4510U CPU 2.00GHz (Haswell), 1 CPU, 4 logical and 2 physical cores

Frequency=2539066 Hz, Resolution=393.8456 ns, Timer=TSC
.NET Core SDK=3.0.100
  [Host]     : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214),
X64 RyuJIT
  DefaultJob : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214),
X64 RyuJIT


|   Method |      Mean |    Error |   StdDev |
|--------- |----------:|---------:|---------:|
| Unescape |  74.69 ms | 1.471 ms | 2.063 ms |
|    Test5 | 126.85 ms | 2.401 ms | 2.128 ms |
Re[4]: 5 вариантов unescape - угадайте какой самый быстрый (
От: Shmj Ниоткуда  
Дата: 30.11.19 02:58
Оценка:
Здравствуйте, adetkov, Вы писали:

A>| Method | Mean | Error | StdDev |

A>|--------- |----------:|---------:|---------:|
A>| Unescape | 74.69 ms | 1.471 ms | 2.063 ms |
A>| Test5 | 126.85 ms | 2.401 ms | 2.128 ms |

Странно, у меня другие результаты:

  Скрытый текст
Test6=206
Test5=184

Test6=194
Test5=200

Test6=205
Test5=182

Test6=209
Test5=181

Test6=204
Test5=184

Test6=204
Test5=186

Test6=206
Test5=187

Test6=208
Test5=272

Test6=223
Test5=176

Test6=206
Test5=189


Test5 практически всегда быстре.

Полный код:

  Скрытый текст
using System;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Random r = new Random();

            var sb = new StringBuilder(2000000 * 7);

            sb.Append("test1\ntest2\\n\\\\test3");

            for (int i = 0; i < 2000000; i++)
            {
                if (r.Next(1, 3) == 1)
                    sb.Append("\\");

                if (r.Next(1, 3) == 1)
                    sb.Append("\\");

                if (r.Next(1, 3) == 1)
                    sb.Append("\\n");

                if (r.Next(1, 4) == 3)
                    sb.Append("\n");

                sb.Append("word");
            }

            var input = sb.ToString();

            var escaped = input.Replace("\\", "\\\\").Replace("\n", "\\n");

            var sw = new Stopwatch();


            for (int i = 0; i < 10; i++)
            {
                sw.Reset();
                sw.Start();

                Test6(escaped);
                sw.Stop();
                Console.WriteLine("Test6=" + sw.ElapsedMilliseconds);


                sw.Reset();
                sw.Start();

                Test5(escaped);
                sw.Stop();
                Console.WriteLine("Test5=" + sw.ElapsedMilliseconds);

                Console.WriteLine();
            }

            sw.Reset();
            sw.Start();

            //var unescaped = Test1(escaped); // 1235
            //var unescaped = Test2(escaped); mamma mia!
            //var unescaped = Test3(escaped); // 891
            //var unescaped = Test4(escaped); // 289
            //var unescaped = Test5(escaped); // 183
            var unescaped = Test6(escaped); // 208

            sw.Stop();

            Console.WriteLine(sw.ElapsedMilliseconds);
            Console.ReadLine();
            Console.WriteLine(unescaped.Equals(input));
        }

        static string Test1(string escaped)
        {
            var guid = Guid.NewGuid().ToString();
            return escaped.Replace("\\\\", guid).Replace("\\n", "\n").Replace(guid, "\\");
        }

        static string Test2(string escaped)
        {
            return escaped.Split(new[] {"\\\\"}, StringSplitOptions.None)
                .Aggregate((seed, next) => seed + "\\" + next.Replace("\\n", "\n"));
        }

        static string Test3(string escaped)
        {
            var parts = escaped.Split(new[] {"\\\\"}, StringSplitOptions.None)
                .Select(part => part.Replace("\\n", "\n"));
            return string.Join("\\", parts);
        }

        static string Test4(string escaped)
        {
            var unescaped = new StringBuilder(escaped.Length);

            bool slash = false;

            foreach (var ch in escaped)
            {
                if (slash)
                {
                    if ('\\' == ch)
                        unescaped.Append('\\');

                    if ('n' == ch)
                        unescaped.Append('\n');

                    slash = false;
                    continue;
                }

                if ('\\' == ch)
                    slash = true;
                else
                    unescaped.Append(ch);
            }

            return unescaped.ToString();
        }

        static string Test5(string escaped)
        {
            var unescaped = new StringBuilder(escaped.Length);

            int currentIndex = 0;

            while (true)
            {
                var slashIndex = escaped.IndexOf('\\', currentIndex);

                if (-1 == slashIndex)
                {
                    unescaped.Append(escaped, currentIndex, escaped.Length - currentIndex);
                    break;
                }

                unescaped.Append(escaped, currentIndex, slashIndex - currentIndex);
                currentIndex = slashIndex;

                var nextChar = escaped[slashIndex + 1];

                switch (nextChar)
                {
                    case '\\':
                        unescaped.Append('\\');
                        break;
                    case 'n':
                        unescaped.Append('\n');
                        break;
                    default:
                        throw new InvalidOperationException("nextChar=" + nextChar);
                }

                currentIndex++;
                currentIndex++;
            }

            return unescaped.ToString();
        }

        private static string Test6(string str)
        {
            char[] array = str.ToCharArray();
            int cur = 0;
            bool waitControl = false;

            for (int i = 0; i < array.Length; i++)
            {
                if (!waitControl)
                {
                    if (array[i] == '\\')
                    {
                        waitControl = true;
                        continue;
                    }

                    array[cur++] = array[i];
                    continue;
                }

                waitControl = false;


                switch (array[i])
                {
                    case 'n':
                        array[cur++] = '\n';
                        break;
                    case '\\':
                        array[cur++] = '\\';
                        break;
                    default:
                        throw new Exception("Unexpected control symbol");
                }
            }

            return new string(array, 0, cur);
        }
    }
}


Вы какие данные подаете на вход?
Отредактировано 30.11.2019 3:07 Shmj . Предыдущая версия .
Re[5]: 5 вариантов unescape - угадайте какой самый быстрый (
От: adetkov Россия  
Дата: 30.11.19 03:15
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Вы какие данные подаете на вход?


Проверка скорости исполнения штука достаточно тонкая, я для этих целей использую специальную библиотеку. Программу запускаю в версии Release

  Benchmark
using System.Text;
using System;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

public class Test
{
    private string _test;

    public Test()
    {
        Random r = new Random();

        var sb = new StringBuilder(2000000 * 7);

        sb.Append("test1\ntest2\\n\\\\test3");

        for (int i = 0; i < 2000000; i++)
        {
            if (r.Next(1, 3) == 1)
                sb.Append("\\");

            if (r.Next(1, 3) == 1)
                sb.Append("\\");

            if (r.Next(1, 3) == 1)
                sb.Append("\\n");

            if (r.Next(1, 4) == 3)
                sb.Append("\n");

            sb.Append("word");
        }

        var input = sb.ToString();

        _test = input.Replace("\\", "\\\\").Replace("\n", "\\n");
    }

    [Benchmark(Description = "Unescape")]
    public string Unescape()
    {
        string str = _test;
        char[] array = str.ToCharArray();
        int cur = 0;
        bool waitControl = false;

        for (int i = 0; i < array.Length; i++)
        {
            if (!waitControl)
            {
                if (array[i] == '\\')
                {
                    waitControl = true;
                    continue;
                }

                array[cur++] = array[i];
                continue;
            }

            waitControl = false;
            array[cur++] = array[i] switch
            {
                'n' => '\n',
                '\\' => '\\',
                _ => throw new Exception("Unexpected symbol")
            };
        }

        return new string(array, 0, cur);
    }

    [Benchmark(Description = "Test5")]
    public string Test5()
    {
        string escaped = _test;
        var unescaped = new StringBuilder(escaped.Length);

        int currentIndex = 0;

        while (true)
        {
            var slashIndex = escaped.IndexOf('\\', currentIndex);

            if (-1 == slashIndex)
            {
                unescaped.Append(escaped, currentIndex, escaped.Length - currentIndex);
                break;
            }

            unescaped.Append(escaped, currentIndex, slashIndex - currentIndex);
            currentIndex = slashIndex;

            var nextChar = escaped[slashIndex + 1];

            switch (nextChar)
            {
                case '\\':
                    unescaped.Append('\\');
                    break;

                case 'n':
                    unescaped.Append('\n');
                    break;

                default:
                    throw new InvalidOperationException("nextChar=" + nextChar);
            }

            currentIndex++;
            currentIndex++;
        }

        return unescaped.ToString();
    }
}

internal class Solution
{
    private static void Main(string[] args)
    {
        BenchmarkRunner.Run<Test>();
        Console.ReadLine();
    }
}
Re[5]: 5 вариантов unescape - угадайте какой самый быстрый (
От: _NN_ www.nemerleweb.com
Дата: 30.11.19 06:50
Оценка:
Здравствуйте, Shmj, Вы писали:

Использование Stopwatch не всегда правильно отражает действительность.
Проверяйте через Benchmark.NET
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[5]: 5 вариантов unescape - угадайте какой самый быстрый (
От: Ночной Смотрящий Россия  
Дата: 30.11.19 07:27
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Странно, у меня другие результаты:


Кто бы сомневался. Если человек альтернативно талантлив, он талантлив во всем, включая программирование.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[6]: 5 вариантов unescape - угадайте какой самый быстрый (
От: Mystic Artifact  
Дата: 30.11.19 09:09
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Использование Stopwatch не всегда правильно отражает действительность.

Что же он по вашему отражает?
Re[7]: 5 вариантов unescape - угадайте какой самый быстрый (
От: _NN_ www.nemerleweb.com
Дата: 30.11.19 11:43
Оценка:
Здравствуйте, Mystic Artifact, Вы писали:

MA>Здравствуйте, _NN_, Вы писали:


_NN>>Использование Stopwatch не всегда правильно отражает действительность.

MA> Что же он по вашему отражает?

Нужно учитывать сборку мусора, прогрев JIT компилятора.
Не забыть запускать релизную сборку правильно. Бывает, что запускают под отладчиком.

https://github.com/dotnet/BenchmarkDotNet
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[8]: 5 вариантов unescape - угадайте какой самый быстрый (
От: Mystic Artifact  
Дата: 30.11.19 21:06
Оценка: +1 -1
Здравствуйте, _NN_, Вы писали:

MA>> Что же он по вашему отражает?

_NN>Нужно учитывать сборку мусора, прогрев JIT компилятора.
_NN>Не забыть запускать релизную сборку правильно. Бывает, что запускают под отладчиком.
Конечно это всё нужно учитывать и делать правильно. Есть еще масса других вещей которые учесть невозможно или трудно, наподобии влияния ОС, фоновых задач, спрогнозировать степень деградации красивых цифр полученных с помощью любых утилит, навроде бенчмаркдотнет, при увеличении нагрузки/конкуренции.

Вместе с тем, время выполнения — очень легко доступный показатель (и его производные) и к счастью именно оно отражает субьективную реальность, при чем его сильная сторона и она же ахилесова пята, что в него уже включены все затраты: на старт процесса, на JIT, на GC, кол-во перемолотой памяти.

PS: Не нравится Stopwatch — можно взять стрелочный секундомер.
Отредактировано 30.11.2019 21:07 Mystic Artifact . Предыдущая версия .
Re[6]: 5 вариантов unescape - угадайте какой самый быстрый (
От: Shmj Ниоткуда  
Дата: 01.12.19 01:07
Оценка: :))
Здравствуйте, _NN_, Вы писали:

_NN>Использование Stopwatch не всегда правильно отражает действительность.

_NN>Проверяйте через Benchmark.NET

На самом деле я затупил — проверял в режиме Debug.
Re: 5 вариантов unescape - угадайте какой самый быстрый (неожида
От: L_G Россия  
Дата: 01.12.19 09:19
Оценка: 1 (1)
Shmj,
Упростил вариант adetkov. Есть ускорение.
    private static string Test7(string escaped)
    {
        char[] array = escaped.ToCharArray();

        int i = 0, j = 0;
        while (j < array.Length) // странно, но чуть быстрее, чем for
        {
            if (array[j] == '\\')
            {
                j++;
                if (j >= array.Length) // увы, но для полной корректности придется проверить
                    break;
                if (array[j] == 'n')
                    array[i] = '\n';
                else
                    array[i] = array[j];
            }
            else
                array[i] = array[j];
            i++; j++;
        }
        return new string(array, 0, i);
    }
Каша в голове — пища для ума (с)
Re[7]: 5 вариантов unescape - угадайте какой самый быстрый (
От: Ночной Смотрящий Россия  
Дата: 01.12.19 09:28
Оценка:
Здравствуйте, Shmj, Вы писали:

S>На самом деле я затупил — проверял в режиме Debug.


Причем, похоже, не первый раз — .Net Core в действии...
Автор: Shmj
Дата: 28.11.19
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re: 5 вариантов unescape - угадайте какой самый быстрый (нео
От: Sinclair Россия https://github.com/evilguest/
Дата: 06.12.19 08:30
Оценка: 72 (7)
Здравствуйте, Shmj, Вы писали:

Неожиданно, самый быстрый вариант выглядит так:
  AVXUnescape()
        [Benchmark]
        public unsafe string AVXUnescape()
        {
            if (!Avx2.IsSupported)
                return UnsafeCharArrayScan();        // fallback для процессоров без AVX2. По-хорошему, надо деградировать постепенно.
            var slash = stackalloc ushort[1] { '\\' };
            var nchar = stackalloc ushort[1] { 'n' };
            var slashes = Avx2.BroadcastScalarToVector256(slash);
            var nchars = Avx2.BroadcastScalarToVector256(nchar);

            var result = new char[_escaped.Length];
            var escaped = _escaped.AsSpan(); 
            fixed (char* s = escaped, t = result)
            {
                var src = (ushort*)s;
                var trg = (ushort*)t;
                var i = 0;
                var j = 0;
                var trailingSlash = false;
                var k = (int)((((UIntPtr)(void*)src).ToUInt64() >> 1) & 0xF);  // вычисляем смещение src 

                while (i + 16 <= escaped.Length)  // проверяем, что остаток больше либо равен длине векторного регистра
                {
                    while (((k + i) & 0xf) > 0)   // медленный поиск для выравнивания по 32-byte boundary
                    {
                        if (trailingSlash)
                            switch(s[i])
                            {
                                case 'n':  // \n
                                    t[j - 1] = '\n';
                                    goto case '\\';
                                case '\\': // double-slash
                                  i++;
                                  trailingSlash = false;
                                  continue;
                            }

                        trailingSlash = s[i] == '\\';
                        t[j++] = s[i++];
                    }
                    var current = Avx2.LoadAlignedVector256(src + i); // чтение с выравниванием выигрывает примерно 30% времени от обычного
                    var slashbits = (uint)Avx2.MoveMask(Avx2.CompareEqual(current, slashes).AsByte());
                    var ncharbits = (uint)(Avx2.MoveMask(Avx2.CompareEqual(current, nchars).AsByte()));

                    if (slashbits == 0 && ncharbits == 0) // no slashes, no n chars
                    {
                        Avx2.Store(trg + j, current);
                        i += 16; j += 16;
                        continue;
                    }

                    if (trailingSlash)
                    {
                        if ((slashbits & 1) > 0) // starting slash after trailing slash
                        {
                            i++;
                            trailingSlash = false;
                            continue;
                        }
                        if ((ncharbits & 1) > 0)
                        {
                            i++;
                            trailingSlash = false;
                            trg[j - 1] = '\n';
                            continue;
                        }
                    }

                    var doubleslashbits = (slashbits >> 2) & slashbits;
                    var slashncharbits = (ncharbits >> 2) & slashbits;

                    var snpos = (int)Bmi1.TrailingZeroCount(slashncharbits) / 2;

                    if (doubleslashbits == 0)
                    {
                        if (slashncharbits == 0)  // no \\, nor \n - plain copy
                        {
                            Avx2.Store(trg + j, current);
                            i += 16; j += 16;
                            trailingSlash = (slashbits & 0xF0000000) == 1;
                        }
                        else
                        {
                            var n = snpos;
                            while (n >= 4)
                            {
                                var ls = (ulong*)(src + i);
                                var lt = (ulong*)(trg + j);
                                *lt = *ls;
                                n -= 4;
                                i += 4; j += 4;
                            }
                            if (n >= 2)
                            {
                                var qs = (uint*)(src + i);
                                var qt = (uint*)(trg + j);
                                *qt = *qs;
                                n -= 2;
                                i += 2; j += 2;
                            }
                            if (n > 0)
                            {
                                trg[j] = src[i];
                                j++; i++;
                            }
                            trg[j++] = '\n';
                            i += 2;
                        }
                        continue;
                    }

                    var dspos = (int)Bmi1.TrailingZeroCount(doubleslashbits) / 2;
                    if (slashncharbits != 0 && snpos < dspos)
                    {
                        var n = snpos;
                        while (n >= 4)
                        {
                            var ls = (ulong*)(src + i);
                            var lt = (ulong*)(trg + j);
                            *lt = *ls;
                            n -= 4;
                            i += 4; j += 4;
                        }
                        if (n >= 2)
                        {
                            var qs = (uint*)(src + i);
                            var qt = (uint*)(trg + j);
                            *qt = *qs;
                            n -= 2;
                            i += 2; j += 2;
                        }
                        if (n > 0)
                        {
                            trg[j] = src[i];
                            j++; i++;
                        }
                        trg[j++] = '\n';
                        i += 2;
                    }
                    else
                    {
                        var n = dspos + 1; 
                        while (n >= 4)
                        {
                            var ls = (ulong*)(src + i);
                            var lt = (ulong*)(trg + j);
                            *lt = *ls;
                            n -= 4;
                            i += 4; j += 4;
                        }
                        if (n >= 2)
                        {
                            var qs = (uint*)(src + i);
                            var qt = (uint*)(trg + j);
                            *qt = *qs;
                            n -= 2;
                            i += 2; j += 2;
                        }
                        if (n > 0)
                        {
                            trg[j] = src[i];
                            j++; i++;
                        }
                        i++;
                    }
                }

                // handle the remainder
                while (i < escaped.Length)
                {
                    if (trailingSlash)
                        switch (s[i])
                        {
                            case 'n':  // \n
                                t[j - 1] = '\n';
                                goto case '\\';
                            case '\\': // double-slash
                                i++;
                                trailingSlash = false;
                                continue;
                        }

                    trailingSlash = s[i] == '\\';
                    t[j++] = s[i++];
                }
                return new string(result, 0, j);
            }
        }


Результаты моего лэптопа — https://files.rsdn.org/5743/TestUnescape.UnescapeTests-report.html.
За единицу взят предыдущий победитель: https://rsdn.org/forum/dotnet/7601511.1
Автор: L_G
Дата: 01.12.19

N = количество повторов (в оригинале — 2000000, я сократил).
Scale — это коэффициент уменьшения плотности спецсимволов. В оригинале их напихано очень часто; я проверял, как зависит производительность от их частоты. В пределе, при очень больших Scale, в строке вообще не встречается ни \ ни n, и алгоритм вырождается в тупое копирование строки. При scale = 1 частоты совпадают с исходными.
Вот, собственно, код инициализации:
  UnescapeTests()
        public UnescapeTests()
        {
            var sb = new StringBuilder(N * 7);

            sb.Append("start:");

            for (int i = 0; i < N; i++)
            {
                if (_r.Next(1, 3 * Scale) == 1)
                    sb.Append("\\");

                if (_r.Next(1, 3 * Scale) == 1)
                    sb.Append("\\");

                if (_r.Next(1, 3 * Scale) == 1)
                    sb.Append("\\n");

                if (_r.Next(1, 4 * Scale) == 1)
                    sb.Append("\n");

                sb.Append("word");
            }

            _escaped = sb.ToString()
                         .Replace("\\", "\\\\")
                         .Replace("\n", "\\n");
        }

Понятно, что по соображениям воспроизводимости Random инициализируется константой. (42).
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Отредактировано 06.12.2019 8:31 Sinclair . Предыдущая версия .
Re[2]: 5 вариантов unescape - угадайте какой самый быстрый (нео
От: Shmj Ниоткуда  
Дата: 06.12.19 09:18
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Неожиданно, самый быстрый вариант выглядит так:


Это вы взяли готовый код для подобной задачи или писали с нуля для спорт. интереса?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.