Re[2]: Haskell :: не такой тормоз, как кажется
От: Димчанский Литва http://dimchansky.github.io/
Дата: 19.03.08 10:09
Оценка:
Здравствуйте, Basil B, Вы писали:
BB>? На плюсах получилось гораздо короче и яснее, имхо.

Пример кода на плюсах скажет за себя все..
Экстренное торможение средствами LINQ
От: Klapaucius  
Дата: 20.03.08 18:26
Оценка: 32 (3) :))) :)
C# имеет репутацию не самого тормозного языка, но умелые руки и смекалка могут поставить на колени еще и не такие творения сумрачного скандинавского гения.
Luke Hoban написал небольшой фреймворк для построения монадических комбинаторных парсеров на С# 3:
    // The result of a parse consists of a value and the unconsumed input.
    public class Result<TInput, TValue>
    {
        public readonly TValue Value;
        public readonly TInput Rest;
        public Result(TValue value, TInput rest) { Value = value; Rest = rest; }
    }

    // A Parser is a delegate which takes an input and returns a result.
    public delegate Result<TInput, TValue> Parser<TInput, TValue>(TInput input);

    public static class ParserCombinatorExtensions
    {
        public static Parser<TInput, TValue> OR<TInput, TValue>(this Parser<TInput, TValue> parser1, Parser<TInput, TValue> parser2)
        {
            return input => parser1(input) ?? parser2(input);
        }

        public static Parser<TInput, TValue2> AND<TInput, TValue1, TValue2>(this Parser<TInput, TValue1> parser1, Parser<TInput, TValue2> parser2)
        {
            return input => parser2(parser1(input).Rest);
        }
    }

    // Expose some operations on the Parser type using Extension methods.
    public static class ParserCombinatorsMonad
    {
        // By providing Select, Where and SelecMany methods on Parser<TInput,TValue> we make the 
        // C# Query Expression syntax available for manipulating Parsers.  
        public static Parser<TInput, TValue> Where<TInput, TValue>(this Parser<TInput, TValue> parser, Func<TValue, bool> pred)
        {
            return input =>
            {
                var res = parser(input);
                if (res == null || !pred(res.Value)) return null;
                return res;
            };
        }
        public static Parser<TInput, TValue2> Select<TInput, TValue, TValue2>(this Parser<TInput, TValue> parser, Func<TValue, TValue2> selector)
        {
            return input =>
            {
                var res = parser(input);
                if (res == null) return null;
                return new Result<TInput, TValue2>(selector(res.Value), res.Rest);
            };
        }
        public static Parser<TInput, TValue2> SelectMany<TInput, TValue, TIntermediate, TValue2>(this Parser<TInput, TValue> parser, Func<TValue, Parser<TInput, TIntermediate>> selector, Func<TValue, TIntermediate, TValue2> projector)
        {
            return input =>
            {
                var res = parser(input);
                if (res == null) return null;
                var val = res.Value;
                var res2 = selector(val)(res.Rest);
                if (res2 == null) return null;
                return new Result<TInput, TValue2>(projector(val, res2.Value), res2.Rest);
            };
        }
    }

    // Contains all the basic parsers that are independent of return type.
    public abstract class Parsers<TInput>
    {
        public Parser<TInput, TValue> Succeed<TValue>(TValue value)
        {
            return input => new Result<TInput, TValue>(value, input);
        }
        public Parser<TInput, TValue[]> Rep<TValue>(Parser<TInput, TValue> parser)
        {
            return Rep1(parser).OR(Succeed(new TValue[0]));
        }
        public Parser<TInput, TValue[]> Rep1<TValue>(Parser<TInput, TValue> parser)
        {
            return from x in parser
                   from xs in Rep(parser)
                   select (new[] { x }).Concat(xs).ToArray();
        }
    }

    // Contains a few parsers that parse characters from an input stream
    public abstract class CharParsers<TInput> : Parsers<TInput>
    {
        public abstract Parser<TInput, char> AnyChar { get; }
        public Parser<TInput, char> Char(char ch) { return from c in AnyChar where c == ch select c; }
        public Parser<TInput, char> Char(Predicate<char> pred) { return from c in AnyChar where pred(c) select c; }
    }

Воспользовавшись им, я легко и непринужденно написал парсер для чисел. Этот код, разумеется, весьма далек от идеального и избыточен.
public abstract class DoubleParser<TInput> : CharParsers<TInput>
{
    public Parser<TInput, double> Number;
    public Parser<TInput, char[]> Whitespace;

    protected DoubleParser()
    {
        Whitespace = Rep(Char(' ').OR(Char('\t').OR(Char('\n')).OR(Char('\r'))));
        Number = from ws in Whitespace
                 let Digit = from c in Char(char.IsDigit) select int.Parse(c.ToString())
                 let Dot = Char('.').OR(Char(','))
                 let Sign = (from c in Char('+') select 1).OR(from c in Char('-') select -1)
                 let IntPart = from ws_ in Whitespace
                               from ds in Rep1(Digit)
                               select ds.FoldRightI(0, (i, d, acc) => acc + d*(int)Math.Pow(10, i))
                 let FracPart = from dt in Dot
                                from ds in Rep1(Digit)
                                select ds.FoldLeftI(0.0, (i, d, acc) => acc + d*Math.Pow(0.1, i))
                 let Expn = from c in Char('E').OR(Char('e'))
                            from s in Sign
                            from i in IntPart
                            select Math.Pow(10, s*i)
                 from s in Sign.OR(Default(1))
                 from i in IntPart
                 from f in FracPart.OR(Default(0.0))
                 from e in Expn.OR(Default(1.0))
                 select s * (i + f) * e;
    }

    public IEnumerable<double> Stream(TInput src)
    {
        var s = src;
        var res = Number(s);
        while (res != null)
        {
            yield return res.Value;
            res = Number(res.Rest);
        }
    }

    public Parser<TInput, T> Default<T>(T val)
    {
        return input => new Result<TInput, T>(val, input);
    }
}

public struct StringList
{
    private readonly int _cursor;
    private readonly string _s;

    public StringList(string s)
    {
        _s = s;
        _cursor = 0;
    }

    private StringList(string s, int c)
    {
        _s = s;
        _cursor = c;
    }

    public bool NonNil
    {
        get { return _cursor < _s.Length; }
    }

    public char Head
    {
        get { return _s[_cursor]; }
    }

    public StringList Tail
    {
        get { return new StringList(_s, _cursor + 1); }
    }
}

public static class Helper
{
    public static RT FoldRightI<T, RT>(this T[] arr, RT acc, Func<int, T, RT, RT> f)
    {
        var _acc = acc;
        var len = arr.Length - 1;
        for (var i = 0; i < arr.Length; i++)
            _acc = f(len - i, arr[i], _acc);
        return _acc;
    }

    public static RT FoldLeftI<T, RT>(this T[] arr, RT acc, Func<int, T, RT, RT> f)
    {
        var _acc = acc;
        for (var i = 0; i < arr.Length; i++)
            _acc = f(i + 1, arr[i], _acc);
        return _acc;
    }
}

public class DoubleParserFromIterator : DoubleParser<StringList>
{
    public override Parser<StringList, char> AnyChar
    {
        get
        {
            return input => input.NonNil
                                ?
                                    new Result<StringList, char>(input.Head, input.Tail)
                                :
                                    null;
        }
    }
}

internal class Program
{
    private static void Main()
    {
        var str = File.ReadAllText(@"c:\numbers_large.txt");
        
        var parser = new DoubleParserFromIterator();

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

        var sum = parser.Stream(new StringList(str)).Sum();

        sw.Stop();

        Console.WriteLine(str.Length/1024.0/1024.0/(sw.ElapsedMilliseconds/1000.0));
        Console.WriteLine(sum);
    }
}

На E6750 этот парсер работает, с не побоюсь этого слова, феерической скоростью в 0,15 mb/s
... << RSDN@Home 1.2.0 alpha rev. 774>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re: Экстренное торможение средствами LINQ
От: Schade Россия  
Дата: 20.03.08 19:56
Оценка:
Здравствуйте, Klapaucius, Вы писали:

K>Luke Hoban написал небольшой фреймворк для построения монадических комбинаторных парсеров на С# 3:

Какой кошмар. И этот человек сейчас работает над F#. Все, приплыли, хана языку
Re: Экстренное торможение средствами LINQ
От: Schade Россия  
Дата: 21.03.08 09:18
Оценка:
Здравствуйте, Klapaucius, Вы писали:
K>Luke Hoban написал небольшой фреймворк для построения монадических комбинаторных парсеров на С# 3:
У, да там еще и рейтрейсер на LINQ есть
А вообще забавная тенденция: раньше народ, чтобы проиллюстрировать ФП-фичи, писал факториалы да фибоначчи. Теперь каждый считает своим долгом написать парсер и рейтрейсер.
Re: Экстренное торможение средствами LINQ
От: Аноним  
Дата: 22.03.08 16:53
Оценка:
Здравствуйте, Klapaucius, Вы писали:

K>На E6750 этот парсер работает, с не побоюсь этого слова, феерической скоростью в 0,15 mb/s


Обидно стало за C#. Решил свой велосипед тоже изобразить (взяв за основу вышеозначенный фреймворк и Parsec за образцы). Тока малехо доработав напильником. Получилось не ахти какая скорость (в районе 2Mb/s), но уж не 0.1 хотя бы Монады сохранены, но внутри немного императившины добавлено. Со скоростью думаю проблема в том, что С# слегка ошалевает от обилия функций высшего порядка — надеюсь в F# таке вещи компилит в более съедобный для CLR вид. Ниже получившийся гибридный ужас:
Собственно "do-notation"-вида парсеры и парсинг:
    class Program
    {
        static void Main(string[] args)
        {
            Func<char, Parser<char, char>> symbol = s => Parsers.Satisfy((char c) => c == s);
            Func<string, Parser<char, char>> anyOf = symbols => Parsers.Satisfy((char c) => symbols.Contains(c));

            var digit = Parsers.Satisfy((char c) => c >= '0' && c <= '9');
            var digitValue = from c in digit select (long)c - '0';
            var dot = anyOf(",.");
            var minus = symbol('-');
            var space = anyOf(" \n");

            var integer = Parsers.Fold1Many1(digitValue, (s, i) => 10 * s + i);

            var fraction =
                from d in Parsers.FoldMany1(digitValue, (Tuple<long, int> acc, long i) => new Tuple<long, int>(10 * acc.First + i, acc.Second + 1), () => new Tuple<long, int>(0, 0))
                select d.First / (Math.Pow(10, d.Second));

            var sign = Parsers.Optional(from _ in minus select -1.0, 1.0);

            var exp = Parsers.Optional(
                from _ in anyOf("eE")
                from s in sign
                from i in integer
                select Math.Pow(10, i * s), 1);

            var real =
                from s in sign
                from i in integer
                from d in dot
                from f in fraction
                from e in exp
                select (i + f) * s * e;

            var realNumberSum = Parsers.Fold1Many1(
                    from r in real from _ in space select r,
                (f, s) => f + s);

            using (var fs = File.OpenText(@"C:\numbers_large.txt"))
            {
                DateTime dt = DateTime.Now;
                var sum = realNumberSum(new StreamEnumerator<char>(GetFileContentBlock(fs)));
                Console.WriteLine(sum.Output);
                Console.WriteLine(DateTime.Now - dt);
            }
        }

        static IEnumerable<char> GetFileContentBlock(StreamReader sr)
        {
            int size = 65536;
            char[] buf = new char[size];
            int actual;
            do
            {
                actual = sr.ReadBlock(buf, 0, size);
                for (int i = 0; i < actual; ++i)
                    yield return buf[i];
            } while (actual == size);
        }
    }


Вот сам мини-фрейморк:
    public delegate Result<TInput, TOutput> Parser<TInput, TOutput>(EnumeratorEx<TInput> input);

    public static class Parsers
    {
        public static Parser<TSymbol, TSymbol> Satisfy<TSymbol>(Func<TSymbol,bool> predicate)
        {
            return input =>
                !input.IsValid ?
                new Result<TSymbol, TSymbol>(input) :
                    predicate(input.Head) ?
                    new Result<TSymbol, TSymbol>(input.Head, input.GetTail()) :
                    new Result<TSymbol, TSymbol>(input);
        }

        public static Parser<TInput, TOutput> FoldMany<TInput, TParsed, TOutput>(Parser<TInput, TParsed> parser, Func<TOutput, TParsed, TOutput> accumulator, Func<TOutput> seed)
        {
            return input =>
            {
                TOutput accumulated = seed();
                Result<TInput, TParsed> result;
                var curInput = input;
                while ((result = parser(curInput)).Success)
                {
                    accumulated = accumulator(accumulated, result.Output);
                    curInput = result.Remaining;
                }
                return new Result<TInput, TOutput>(accumulated, curInput);
            };
        }

        public static Parser<TInput, LinkedList<TOutput>> Many<TInput, TOutput>(Parser<TInput, TOutput> parser)
        {
            return FoldMany(parser, (LinkedList<TOutput> all, TOutput i) => { all.AddLast(i); return all; }, () => new LinkedList<TOutput>());
        }


        public static Parser<TInput, LinkedList<TOutput>> Many1<TInput, TOutput>(Parser<TInput, TOutput> parser)
        {
            Func<LinkedList<TOutput>,TOutput,LinkedList<TOutput>> car = (LinkedList<TOutput> list, TOutput elem) => {list.AddFirst(elem) ; return list;};

            return from p in parser
                   from rest in Many(parser)
                   select car(rest, p);
        }

        public static Parser<TInput, TOutput> FoldMany1<TInput, TParsed, TOutput>(Parser<TInput, TParsed> parser, Func<TOutput, TParsed, TOutput> accumulator, Func<TOutput> seed)
        {
            return from p in parser
                   from all in FoldMany(parser, accumulator, () => accumulator(seed(), p))
                   select all;
        }

        public static Parser<TInput, TOutput> Fold1Many1<TInput, TOutput>(Parser<TInput, TOutput> parser, Func<TOutput, TOutput, TOutput> accumulator)
        {
            return from p in parser
                   from all in FoldMany(parser, accumulator, () => p)
                   select all;
        }

        public static Parser<TInput, TOutput> Optional<TInput, TOutput>(Parser<TInput, TOutput> parser, TOutput defaultValue)
        {
            return input =>
            {
                if (!input.IsValid)
                    return new Result<TInput, TOutput>(defaultValue, input);
                var input1 = new RollbackableEnumerator<TInput>(input);
                var result1 = parser(input1);
                return result1.Success ?
                    result1 :
                    new Result<TInput, TOutput>(defaultValue, input1.GetMemorized());
            };
        }

        public static Parser<TInput, TOutput> Choise<TInput, TOutput>(params Parser<TInput, TOutput>[] parsers)
        {
            return input =>
            {
                var result1 = new Result<TInput, TOutput>(default(TOutput), input);
                var input1 = new RollbackableEnumerator<TInput>(input);
                foreach (var parser in parsers)
                {
                    result1 = parser(input1);
                    if (result1.Success)
                        break;
                    input1 = new RollbackableEnumerator<TInput>(input1.GetMemorized());
                }
                return result1;
            };
        }

        public static Parser<TInput, TOutput> All<TInput, TParsed, TOutput>(Func<TOutput, TParsed, TOutput> accumulator, Func<TOutput> seed, params Parser<TInput, TParsed>[] parsers)
        {
            return input =>
            {
                var result = seed();
                var input1 = input;
                foreach (var parser in parsers)
                {
                    var result1 = parser(input1);
                    if (!result1.Success)
                        return new Result<TInput, TOutput>(input1);
                    result = accumulator(result, result1.Output);
                    input1 = result1.Remaining;
                }
                return new Result<TInput, TOutput>(result, input1);
            };
        }


Монадные bind и lift
    public static class ParserExtension
    {
        public static Parser<TInput, TOutput> Select<TInput, TIntermediate, TOutput>(this Parser<TInput, TIntermediate> parser, Func<TIntermediate, TOutput> selector)
        {
            return input =>
            {
                var rez = parser(input);
                return rez.Success ?
                    new Result<TInput, TOutput>(selector(rez.Output), rez.Remaining) :
                    new Result<TInput, TOutput>(rez.Remaining);
            };
        }

        public static Parser<TInput, TOutput> SelectMany<TInput, TIntermediate, TIntermediate2, TOutput>
            (this Parser<TInput, TIntermediate> parser, Func<TIntermediate, Parser<TInput, TIntermediate2>> selector, Func<TIntermediate, TIntermediate2, TOutput> projector)
        {
            return input =>
            {
                var rez1 = parser(input);
                if (!(rez1.Success))
                    return new Result<TInput, TOutput>(rez1.Remaining);
                var rez2 = selector(rez1.Output)(rez1.Remaining);
                return rez2.Success ?
                    new Result<TInput, TOutput>(projector(rez1.Output, rez2.Output), rez2.Remaining) :
                    new Result<TInput, TOutput>(rez2.Remaining);
            };
        }
    }


И всякая мелочь до кучи:
    public struct Tuple<T1, T2>
    {
        T1 first;
        T2 second;

        public Tuple(T1 first, T2 second)
        {
            this.first = first;
            this.second = second;
        }

        public T1 First
        {
            get { return first; }
        }

        public T2 Second
        {
            get { return second; }
        }
    }

    public struct Result<TInput, TOutput>
    {
        EnumeratorEx<TInput> remaining;
        TOutput output;
        bool success;

        public Result(TOutput output, EnumeratorEx<TInput> remaining, bool success)
        {
            this.output = output;
            this.remaining = remaining;
            this.success = success;
        }

        public Result(TOutput output, EnumeratorEx<TInput> remaining)
            : this(output, remaining, true)
        {
        }

        public Result(EnumeratorEx<TInput> remaining)
            :this(default(TOutput),remaining,false)
        {
        }

        public bool Success
        {
            get { return success; }
        }

        public EnumeratorEx<TInput> Remaining
        {
            get { return remaining; }
        }

        public TOutput Output
        {
            get { return output; }
        }
    }

    public abstract class EnumeratorEx<T> : IDisposable
    {
        public abstract bool IsValid
        {
            get;
        }

        public abstract EnumeratorEx<T> GetTail();

        public abstract T Head
        {
            get;
        }

        public abstract void Dispose();
    }

    public class StreamEnumerator<T> : EnumeratorEx<T>
    {
        IEnumerator<T> enumerator;
        bool isValid;

        public StreamEnumerator(IEnumerable<T> enumerable)
        {
            this.enumerator = enumerable.GetEnumerator();
            this.isValid = this.enumerator.MoveNext();
        }

        public override bool IsValid
        {
            get { return isValid; }
        }

        public override EnumeratorEx<T> GetTail()
        {
            if (!isValid)
                throw new InvalidOperationException("Cannot move non valid stream enumerator");
            this.isValid = enumerator.MoveNext();
            return this;
        }

        public override T Head
        {
            get
            {
                if (!isValid)
                    throw new InvalidOperationException("Cannot return head for non valid stream enumerator");
                return enumerator.Current;
            }
        }

        #region IDisposable Members

        public override void Dispose()
        {
            enumerator.Dispose();
        }

        #endregion
    }

    public class RollbackableEnumerator<T> : EnumeratorEx<T>
    {
        class RolledBackEnmerator : EnumeratorEx<T>
        {
            Queue<T> log;
            EnumeratorEx<T> real;

            public RolledBackEnmerator(Queue<T> log, EnumeratorEx<T> real)
            {
                this.log = log;
                this.real = real;
            }

            public override bool IsValid
            {
                get { return true; }
            }

            public override EnumeratorEx<T> GetTail()
            {
                if (log.Count == 0)
                    return real;
                log.Dequeue();
                return this;
            }

            public override T Head
            {
                get { return log.Peek(); }
            }

            public override void Dispose()
            {
                real.Dispose();
            }
        }

        EnumeratorEx<T> real;
        Queue<T> log;

        public RollbackableEnumerator(EnumeratorEx<T> from)
        {
            this.real = from;
            this.log = new Queue<T>();
        }

        public EnumeratorEx<T> GetMemorized()
        {
            return log.Count > 0 ?
                new RolledBackEnmerator(log, real) :
                real;
        }

        public override bool IsValid
        {
            get { return real.IsValid; }
        }

        public override EnumeratorEx<T> GetTail()
        {
            log.Enqueue(real.Head);
            return real = real.GetTail();
        }

        public override T Head
        {
            get { return real.Head; }
        }

        public override void Dispose()
        {
            real.Dispose();
        }
    }


Вот такая ужасная попытка переложить функциональный стиль на C#...
Re: Экстренное торможение средствами LINQ
От: Аноним  
Дата: 14.05.09 18:53
Оценка:
K>На E6750 этот парсер работает, с не побоюсь этого слова, феерической скоростью в 0,15 mb/s

Взяло сомнение — решил проверить

Intel E6750 (2.66 Hz)

Решил проверить:

AMD X2 3800 (2.0 Hz) — 0,41 mb/s
Intel Q6600 (2.4 Hz) — 0,82 mb/s

В парсерах не разбираюсь, поэтому предварительно сделал одну оптимизацию

вместо

let Digit = from c in Char(char.IsDigit) select int.Parse(c.ToString())


сделал

let Digit = from c in Char(char.IsDigit) select c
Re[2]: Экстренное торможение средствами LINQ
От: Аноним  
Дата: 15.05.09 04:32
Оценка:
Здравствуйте, Аноним, Вы писали:

K>>На E6750 этот парсер работает, с не побоюсь этого слова, феерической скоростью в 0,15 mb/s


А>Взяло сомнение — решил проверить


А>Intel E6750 (2.66 Hz)


А>Решил проверить:


А>AMD X2 3800 (2.0 Hz) — 0,41 mb/s

А>Intel Q6600 (2.4 Hz) — 0,82 mb/s

А>В парсерах не разбираюсь, поэтому предварительно сделал одну оптимизацию


Оказывается, если собрать под архитектуру x86, тогда и на Q6600 будет примерно 0,15 mb/s.
А вот если собирать под AnyCPU или под x64, то будет уже 0,82 mb/s.
Re[3]: Экстренное торможение средствами LINQ
От: jenyavb  
Дата: 15.05.09 04:39
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>Оказывается, если собрать под архитектуру x86, тогда и на Q6600 будет примерно 0,15 mb/s.

А>А вот если собирать под AnyCPU или под x64, то будет уже 0,82 mb/s.
Лучше всегда, по-возможности, собирать под AnyCPU, так быстрее.
... << RSDN@Home 1.2.0 alpha 4 rev. 1217>>
Re[3]: Экстренное торможение средствами LINQ
От: Klapaucius  
Дата: 15.05.09 06:47
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>Оказывается, если собрать под архитектуру x86, тогда и на Q6600 будет примерно 0,15 mb/s.

А>А вот если собирать под AnyCPU или под x64, то будет уже 0,82 mb/s.

Я тоже собирал под AnyCPU. Но запускал под 32х разрядной ОС.
... << RSDN@Home 1.2.0 alpha 4 rev. 1110>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[2]: Экстренное торможение средствами LINQ
От: Klapaucius  
Дата: 15.05.09 06:47
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>В парсерах не разбираюсь, поэтому предварительно сделал одну оптимизацию

А>вместо
А>
let Digit = from c in Char(char.IsDigit) select int.Parse(c.ToString())

А>сделал
А>
let Digit = from c in Char(char.IsDigit) select c


Парсер будет работать некорректно. Вместо того, чтобы возводить в степень числовое значение распарсенной цифры, он будет возводить в степень ASCII код цифры.
... << RSDN@Home 1.2.0 alpha 4 rev. 1110>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re: Экстренное торможение средствами LINQ
От: vdimas Россия  
Дата: 15.05.09 07:16
Оценка:
Здравствуйте, Klapaucius, Вы писали:

K>На E6750 этот парсер работает, с не побоюсь этого слова, феерической скоростью в 0,15 mb/s


А если char.IsDigit заменить на свою легковесную ф-ию? Об этот IsDigit уже спотыкались многократно.
Re[2]: Экстренное торможение средствами LINQ
От: Klapaucius  
Дата: 15.05.09 07:58
Оценка:
Здравствуйте, vdimas, Вы писали:

V>А если char.IsDigit заменить на свою легковесную ф-ию? Об этот IsDigit уже спотыкались многократно.


Ну, это одно из изменений, которое сделано здесь
Автор:
Дата: 22.03.08
.
Я-то это все за какие-то минуты написал, а там видно, что человек серьезно отнесся к задаче. Тем не менее — отставание все равно в десятки раз.
... << RSDN@Home 1.2.0 alpha 4 rev. 1110>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[3]: Экстренное торможение средствами LINQ
От: Аноним  
Дата: 15.05.09 09:12
Оценка:
Здравствуйте, Klapaucius, Вы писали:

K>Парсер будет работать некорректно. Вместо того, чтобы возводить в степень числовое значение распарсенной цифры, он будет возводить в степень ASCII код цифры.


Ах, вот оно что.

Впрочем int.Parse(c.ToString()) все равно слишком медленно для случая когда c.IsDigit.

Такой метод-расширение

    public static int ToDigit(this char argument)
    {
        switch (argument)
        {
            case '0': return 0;
            case '1': return 1;
            case '2': return 2;
            case '3': return 3;
            case '4': return 4;
            case '5': return 5;
            case '6': return 6;
            case '7': return 7;
            case '8': return 8;
            case '9': return 9;
        }
        throw new ArgumentException(argument.ToString());
    }


ускоряет mb/s процентов на 10.
Re[4]: Экстренное торможение средствами LINQ
От: Klapaucius  
Дата: 15.05.09 09:24
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>Впрочем int.Parse(c.ToString()) все равно слишком медленно для случая когда c.IsDigit.


А>Такой метод-расширение

А><...>
А>ускоряет mb/s процентов на 10.

А
(int)с - '0'

не проще?
... << RSDN@Home 1.2.0 alpha 4 rev. 1110>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[2]: Экстренное торможение средствами LINQ
От: Аноним  
Дата: 15.05.09 17:46
Оценка:
Здравствуйте, vdimas, Вы писали:

V>А если char.IsDigit заменить на свою легковесную ф-ию? Об этот IsDigit уже спотыкались многократно.

IsDigit достаточно быстрый. Своя легковесная функция его не ускоряет особо. Другое дело, что IsDigit подходит не только для '0' — '9'.
Re[5]: Экстренное торможение средствами LINQ
От: Аноним  
Дата: 15.05.09 17:56
Оценка:
Здравствуйте, Klapaucius, Вы писали:

K>А

K>
K>(int)с - '0' 
K>

K>не проще?
Ну с точки зрения понятности не так ясно, конечно.
Зато по скорости на равных с методом расширением.
Re: Экстренное торможение средствами LINQ
От: Аноним  
Дата: 16.05.09 00:42
Оценка:
Здравствуйте, Klapaucius, Вы писали:

K>На E6750 этот парсер работает, с не побоюсь этого слова, феерической скоростью в 0,15 mb/s

Оказывается, если заменить все массивы на IEnumerable<>, а также в реализации Rep сделать цикл вместо рекурсии, как парсер начинает работать со скоростью 2 mb/s на Q6600. В x86 режиме, производительность тоже поднялась до 1 mb/s. Так что на 3 GHz, будет все 2.5 mb/s для x64.

    public Parser<TInput, IEnumerable<TValue>> Rep<TValue>(Parser<TInput, TValue> parser)
    {
        return input =>
        {
            var result = parser(input);

            var values = new List<TValue>();
            
            while (result != null) {
                input = result.Rest;
                values.Add(result.Value);
                result = parser(input);
            }

            return new Result<TInput, IEnumerable<TValue>>(values, input);
        };
        //return Rep1(parser).OR(Succeed(new TValue[0]));
    }
Re[2]: Экстренное торможение средствами LINQ
От: Klapaucius  
Дата: 18.05.09 16:06
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>
А>    public Parser<TInput, IEnumerable<TValue>> Rep<TValue>(Parser<TInput, TValue> parser)
А>    {
А>        return input =>
А>        {
А>            var result = parser(input);

А>            var values = new List<TValue>();
            
А>            while (result != null) {
А>                input = result.Rest;
А>                values.Add(result.Value);
А>                result = parser(input);
А>            }

А>            return new Result<TInput, IEnumerable<TValue>>(values, input);
А>        };
А>        //return Rep1(parser).OR(Succeed(new TValue[0]));
А>    }
А>


Ну, все правильно, отлично подтверждает мой тезис о том, что именно комбинаторы обходятся дорого — переписываем код на комбинаторах в код без них — и получаем ускорение.
... << RSDN@Home 1.2.0 alpha 4 rev. 1110>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[3]: Экстренное торможение средствами LINQ
От: Аноним  
Дата: 18.05.09 18:56
Оценка:
Здравствуйте, Klapaucius, Вы писали:

K>Ну, все правильно, отлично подтверждает мой тезис о том, что именно комбинаторы обходятся дорого — переписываем код на комбинаторах в код без них — и получаем ускорение.

Не совсем. Прикладной-то код остается таким же, как и был, то есть на комбинаторах.
А вот в библиотеке которая их реализует разумнее от них отказаться.

Просто первоначальный автор решил и сами базовые комбинаторы определить друг через друга, рекурсивно.
Что, конечно, привело к тормозам.
Re[2]: Экстренное торможение средствами LINQ
От: Аноним  
Дата: 20.05.09 03:44
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Оказывается, если заменить все массивы на IEnumerable<>, а также в реализации Rep сделать цикл вместо рекурсии, как парсер начинает работать со скоростью 2 mb/s на Q6600. В x86 режиме, производительность тоже поднялась до 1 mb/s. Так что на 3 GHz, будет все 2.5 mb/s для x64.


.NET 4.0 (Q6600) — 2.8 mb/s, на 3 GHz будет вероятно — 3.5 mb/s
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.