2010-06-08 3 views
2

Я пытаюсь написать не очень умную программу факторизации и пытаюсь сделать это параллельно с использованием TPL. Однако, примерно через 15 минут работы на двухъядерном процессоре ядра, я получаю общее исключение из исключения переполнения внутри него. Все записи в трассировке стека являются частью платформы .NET, переполнение происходит не из моего кода. Любая помощь была бы оценена при выяснении, почему это происходит.Исключение переполнения при параллельной факторизации

Вот прокомментировал код, надеюсь, это достаточно просто, чтобы понять:

class Program 
{ 
    static List<Tuple<BigInteger, int>> factors = new List<Tuple<BigInteger, int>>(); 

    static void Main(string[] args) 
    { 
     BigInteger theNumber = BigInteger.Parse(
      "653872562986528347561038675107510176501827650178351386656875178" + 
      "568165317809518359617865178659815012571026531984659218451608845" + 
      "719856107834513527"); 
     Stopwatch sw = new Stopwatch(); 
     bool isComposite = false; 
     sw.Start(); 

     do 
     { 
      /* Print out the number we are currently working on. */ 
      Console.WriteLine(theNumber); 

      /* Find a factor, stop when at least one is found 
       (using the Any operator). */ 
      isComposite = Range(theNumber) 
          .AsParallel() 
          .Any(x => CheckAndStoreFactor(theNumber, x)); 

      /* Of the factors found, take the one with the lowest base. */ 
      var factor = factors.OrderBy(x => x.Item1).First(); 
      Console.WriteLine(factor); 

      /* Divide the number by the factor. */ 
      theNumber = BigInteger.Divide(
          theNumber, 
          BigInteger.Pow(factor.Item1, factor.Item2)); 

      /* Clear the discovered factors cache, and keep looking. */ 
      factors.Clear(); 
     } while (isComposite); 

     sw.Stop(); 
     Console.WriteLine(isComposite + " " + sw.Elapsed); 
    } 

    static IEnumerable<BigInteger> Range(BigInteger squareOfTarget) 
    { 
     BigInteger two = BigInteger.Parse("2"); 
     BigInteger element = BigInteger.Parse("3"); 
     while (element * element < squareOfTarget) 
     { 
      yield return element; 
      element = BigInteger.Add(element, two); 
     } 
    } 

    static bool CheckAndStoreFactor(BigInteger candidate, BigInteger factor) 
    { 
     BigInteger remainder, dividend = candidate; 
     int exponent = 0; 
     do 
     { 
      dividend = BigInteger.DivRem(dividend, factor, out remainder); 
      if (remainder.IsZero) 
      { 
       exponent++; 
      } 
     } while (remainder.IsZero); 
     if (exponent > 0) 
     { 
      lock (factors) 
      { 
       factors.Add(Tuple.Create(factor, exponent)); 
      } 
     } 
     return exponent > 0; 
    } 
} 

Вот исключение брошено:

Unhandled Exception: System.AggregateException: One or more errors occurred. --- 
> System.OverflowException: Arithmetic operation resulted in an overflow. 
    at System.Linq.Parallel.PartitionedDataSource`1.ContiguousChunkLazyEnumerator.MoveNext(T& currentElement, Int32& currentKey) 
    at System.Linq.Parallel.AnyAllSearchOperator`1.AnyAllSearchOperatorEnumerator`1.MoveNext(Boolean& currentElement, Int32& currentKey) 
    at System.Linq.Parallel.StopAndGoSpoolingTask`2.SpoolingWork() 
    at System.Linq.Parallel.SpoolingTaskBase.Work() 
    at System.Linq.Parallel.QueryTask.BaseWork(Object unused) 
    at System.Linq.Parallel.QueryTask.<.cctor>b__0(Object o) 
    at System.Threading.Tasks.Task.InnerInvoke() 
    at System.Threading.Tasks.Task.Execute() 
    --- End of inner exception stack trace --- 
    at System.Linq.Parallel.QueryTaskGroupState.QueryEnd(Boolean userInitiatedDispose) 
    at System.Linq.Parallel.SpoolingTask.SpoolStopAndGo[TInputOutput,TIgnoreKey](QueryTaskGroupState groupState, PartitionedStream`2 partitions, SynchronousChannel`1[] channels, TaskScheduler taskScheduler) 
    at System.Linq.Parallel.DefaultMergeHelper`2.System.Linq.Parallel.IMergeHelper<TInputOutput>.Execute() 
    at System.Linq.Parallel.MergeExecutor`1.Execute[TKey](PartitionedStream`2 partitions, Boolean ignoreOutput, ParallelMergeOptions options, TaskScheduler taskScheduler, Boolean isOrdered, CancellationState cancellationState, Int32 queryId) 

    at System.Linq.Parallel.PartitionedStreamMerger`1.Receive[TKey](PartitionedStream`2 partitionedStream) 
    at System.Linq.Parallel.AnyAllSearchOperator`1.WrapPartitionedStream[TKey](PartitionedStream`2 inputStream, IPartitionedStreamRecipient`1 recipient, BooleanpreferStriping, QuerySettings settings) 
    at System.Linq.Parallel.UnaryQueryOperator`2.UnaryQueryOperatorResults.ChildResultsRecipient.Receive[TKey](PartitionedStream`2 inputStream) 
    at System.Linq.Parallel.ScanQueryOperator`1.ScanEnumerableQueryOperatorResults.GivePartitionedStream(IPartitionedStreamRecipient`1 recipient) 
    at System.Linq.Parallel.UnaryQueryOperator`2.UnaryQueryOperatorResults.GivePartitionedStream(IPartitionedStreamRecipient`1 recipient) 
    at System.Linq.Parallel.QueryOperator`1.GetOpenedEnumerator(Nullable`1 mergeOptions, Boolean suppressOrder, Boolean forEffect, QuerySettings querySettings) 
    at System.Linq.Parallel.QueryOpeningEnumerator`1.OpenQuery() 
    at System.Linq.Parallel.QueryOpeningEnumerator`1.MoveNext() 
    at System.Linq.Parallel.AnyAllSearchOperator`1.Aggregate() 
    at System.Linq.ParallelEnumerable.Any[TSource](ParallelQuery`1 source, Func`2 predicate) 
    at PFact.Program.Main(String[] args) in d:\myprojects\PFact\PFact\Program.cs:line 34 

Любая помощь будет оценена.

Спасибо!

EDIT

После ответа Саймона, я снова побежал код, на этот раз с пунктом catch(AggregateException x), и я рассмотрел все элементы в InnerExceptions коллекции. Было ровно 2 элемента (я предполагаю, что это один для каждого потока выполнения, так как у меня есть 2 ядра процессора, TPL будет оптимизировать использование только 2 потоков). Оба исключения были идентичны (оба были OverflowException) ... Так что это не ответ.

РЕШЕНИЕ

ответ Henk в оказывается правильным, а вот полуофициальные ссылку из майкрософт блога, подтверждающее, что: What's New in Beta 2 for PLINQ

ответ

3

Глядя сквозь StackTrace, в верхней части, я вижу это:

at System.Linq.Parallel.PartitionedDataSource`1. 
    ContiguousChunkLazyEnumerator.MoveNext(T& currentElement, Int32& currentKey) 
at System.Linq.Parallel.AnyAllSearchOperator`1. 
    AnyAllSearchOperatorEnumerator`1.MoveNext(Boolean& currentElement, Int32& currentKey) 

Теперь это выглядит как счетчику TPL использует Int32 для это собственная внутренняя бухгалтерская отчетность, и вы просто можете делать больше, чем итерации Int32.MaxValue ...

Чтобы убедиться, что вам нужно будет взглянуть на IL созданного statemachine для блока итератора.

+0

Это похоже на наиболее вероятное объяснение, см. Ссылку в моем вопросе под заголовком ** РЕШЕНИЕ ** –

1

Это в значительной степени догадка, потому что еще не побежал ваш код в течение 15 минут, чтобы проверить его = ;-), но при условии, что вы получаете исключение переполнения, может ли быть так, что значение exponent растет больше, чем 2,147,483,647.

int exponent = 0; 

возможно сделать это:

BigInteger exponent = 0; 

я бы ожидал увидеть метод CheckAndStoreFactor на стеке след одного из внутренних исключений, хотя. (Помните AggregateException имеет InnerExceptions свойство, которое может содержать несколько внутренних исключений.)

+0

Это не может быть так, 'exponent' - это количество раз, когда число делится на' base' фактора. Если показатель высок, даже с наименьшей базой из 2, результат все равно будет намного больше, чем число, которое я пытаюсь учитывать. Также обратите внимание, что исключение не происходит в моем коде ... –

+0

@Aviad: Я запускаю ваш код сейчас, ожидая его сбой. Я дам вам знать, если что-нибудь случится. –

+0

Спасибо, тоже, на этот раз я пытаюсь поймать 'AggregateException' и изучить второй элемент в' InnerExceptions' ... Я уверен, это ответ. –

 Смежные вопросы

  • Нет связанных вопросов^_^