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 streampublic 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
Здравствуйте, 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
В парсерах не разбираюсь, поэтому предварительно сделал одну оптимизацию
вместо
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.
Здравствуйте, <Аноним>, Вы писали:
А>Оказывается, если собрать под архитектуру x86, тогда и на Q6600 будет примерно 0,15 mb/s. А>А вот если собирать под AnyCPU или под x64, то будет уже 0,82 mb/s.
Лучше всегда, по-возможности, собирать под AnyCPU, так быстрее.
Здравствуйте, <Аноним>, Вы писали:
А>Оказывается, если собрать под архитектуру 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
Здравствуйте, <Аноним>, Вы писали:
А>В парсерах не разбираюсь, поэтому предварительно сделал одну оптимизацию А>вместо А>
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
.
Я-то это все за какие-то минуты написал, а там видно, что человек серьезно отнесся к задаче. Тем не менее — отставание все равно в десятки раз.
... << 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.
Здравствуйте, <Аноним>, Вы писали:
А>Впрочем 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]));
}
А> 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