Потоки вложенных циклов foreach?

2

Helllo!

Я пытаюсь создать программу, которая вычисляет лучший результат из разных предметов с помощью bruteforce. И я считаю, что скорость вычисления этого может быть улучшена на системах с многоядерными процессорами с помощью потоковой передачи. Поправьте меня если я ошибаюсь. Но как мне это достичь?

Вот часть моего кода, и он хорошо выполняет работу (без потоковой передачи).

private double _bestScore = 0.0;

private void BruteForce()
{
  foreach (Stats head in _heads)
    foreach (Stats chest in _chests)
      foreach (Stats leg in _legs)
        foreach (Stats feet in _feets)
        {
          int stamina = head.sta + chest.sta + leg.sta + feet.sta;
          int power = head.power + chest.power + leg.power + feet.power;
          int armor = head.armor + chest.armor + leg.armor + feet.armor;
          int hit = head.hit + chest.hit + leg.hit + feet.hit;

          double total = stamina * _scaleSta + power * _scalePower + armor * _scaleArmor;

          if (total > _bestScore && hit >= 100)
          {
            _bestScore = total;

            // Store best setup for output when done with bruteforce
            _bestHead = head;
            _bestChest = chest;
            _bestLeg = leg;
            _bestFeet = feet;
          }
        }
}

Итак, есть ли способ улучшить это с помощью потоковой передачи?

Изменить: Исправлена ​​опечатка и обновлялась, чтобы включить другой stat, hit. Хит должен достигнуть 100, чтобы стать возможной частью "лучшего результата". Это делает так, что я не могу проверить каждый отдельный слот первым, чтобы попытаться найти лучшее снаряжение. Хит - очень важный стат до 100, но каждый момент после 100 бесполезен. Кроме того, эта часть моего кода имеет намного больше циклов foreach-loops (28 вместо 4). Таким образом, количество итераций - это много. Однако каждый список (_heads, _chests и т.д.) Обычно содержит не более двух элементов.

  • 0
    Хороший вопрос Ждем ответов.
  • 0
    Сколько времени это обычно занимает? Запомните, что многопоточность и esp раскручивают потоки, чтобы начать работать. С другой стороны, вложенные циклы затрудняют чтение кода, а то, как Энди пишет свой код, намного чище и проще для чтения.
Теги:
multithreading
foreach

4 ответа

3
Лучший ответ

Если вам нужен простой способ добавления многопоточности, просмотрите Parallel Extensions for.NET. По соображениям производительности вам нужно будет только вызвать вызов Parallel.For() в самом удаленном цикле.

  • 0
    Это сработало очень хорошо! Спасибо! Результат моей первоначальной работы: 48157949952 итераций было сделано, это заняло 7731,192708 секунд. (6229045,34537959 итераций в секунду) Результат с параллельными расширениями: было выполнено 48157949952 итераций, это заняло 3817,62699 секунд. (12614629,4748403 итераций в секунду)
  • 0
    Теперь, где проблема в этом: с. Хорошее решение, хотя.
1

Я уверен, что это будет сделано, не имея никаких тестовых данных или компилятора, чтобы я не уверен на 100%:

private void BestItems()
{
    _bestHead = GetBestItem(_heads);
    _bestChest = GetBestItem(_chests);
    _bestLeg = GetBestItem(_legs);
    _bestFeet = GetBestItem(_feets);
}


private Stats GetBestItem(List<Stats> items)
{
    double best = 0.0;
    Stats result = null;

    foreach stats item in items
    {
        double total = item.stamina * _scaleSta + item.power * _scalePower + item.armor * _scaleArmor;

        if (total > best)
        {
            result = item;
        }
    }

    return result;
}

Edit:

Шаги:

  • Создайте список для каждого слота в порядке наиболее важного стата (наименьший из первых)
  • Прокрутитесь, используя какой-то весовой коэффициент, чтобы найти наименьшие значения удара, которые удовлетворяют вашему рейтингу удара. (yuo понадобится для каждого слота для следующего шага)
  • Для каждого элемента выбора слота с наилучшей статистикой, соответствующей минимальным рейтингам игровых автоматов.

вам понадобится много циклов один за другим, но лучше, чем 2 ^ 28, я думаю: p

Edit2: Снова, до сих пор нет компилятора здесь... но это может сработать. В итоге вы получите нагрузку на ведро потоков, хотя...

Для соединения и ожидания потоков см. здесь (msdn) (посмотрите на мьютекс, монитор, ManualResetEvent, AutoResetEvent)

private void BruteForce()
{

  var threads = new List<Thread>;
  foreach (Stats head in _heads)
    foreach (Stats chest in _chests)
      foreach (Stats leg in _legs)
        foreach (Stats feet in _feets)
        {
            if (threads.Count <= 2)


            thread worker = new thread(addressof Process, new object() {head, chest, leg, feet, ...});
            worker.start();
            threads.add(worker);
        }

    foreach (Thread t in threads)
        t.join();  //this might not be the best as it might make the main thread wait for each thread one after the other, not when all finished.  A manual/auto reset is probably better here.
}


private void Process(params...)
{

    int stamina = head.sta + chest.sta + leg.sta + feet.sta;
    int power = head.power + chest.power + leg.power + feet.power;
    int armor = head.armor + chest.armor + leg.armor + feet.armor;
    int hit = head.hit + chest.hit + leg.hit + feet.hit;

    double total = stamina * _scaleSta + power * _scalePower + armor * _scaleArmor;

    lock _bestscore
    {
        if (total > _bestScore && hit >= 100)
        {
            _bestScore = total;

            // Store best setup for output when done with bruteforce
            _bestHead = head;
            _bestChest = chest;
            _bestLeg = leg;
            _bestFeet = feet;
        }
    }
}

РЕДАКТИРОВАТЬ 4: Угадайте, у кого до сих пор нет компилятора рядом с ним? Что-то в соответствии с этим должно убедиться, что у вас есть только 2 нити в любой точке.

var threads = new Dictionary<Guid, Thread>;

private void BruteForce()
{
  foreach (Stats head in _heads)
    foreach (Stats chest in _chests)
      foreach (Stats leg in _legs)
        foreach (Stats feet in _feets)
        {
            while (threads.Count >= 2) {}    //im sure thread.join or equivelent can do this instead of a nasty loop :p

            var guid = Guid.NewGuid();
            thread worker = new thread(addressof Process, new object() {guid, head, chest, leg, feet, ...});
            worker.start();
            threads.add(guid, worker);
        }

    foreach (Thread t in threads)
        t.join();  //this might not be the best as it might make the main thread wait for each thread one after the other, not when all finished.  A manual/auto reset is probably better here.
}

private void Process(params...)
{
    int stamina = head.sta + chest.sta + leg.sta + feet.sta;
    int power = head.power + chest.power + leg.power + feet.power;
    int armor = head.armor + chest.armor + leg.armor + feet.armor;
    int hit = head.hit + chest.hit + leg.hit + feet.hit;

    double total = stamina * _scaleSta + power * _scalePower + armor * _scaleArmor;

    lock _bestscore
    {
        if (total > _bestScore && hit >= 100)
        {
            _bestScore = total;

            // Store best setup for output when done with bruteforce
            _bestHead = head;
            _bestChest = chest;
            _bestLeg = leg;
            _bestFeet = feet;
        }
    }

    _threads.remove(guid)
}
  • 0
    К сожалению, это не так просто. Не знаю, смогу ли я отредактировать / обновить мой вопрос. Но у каждого предмета также есть «хит». Это действительно важное значение до определенной точки, когда эта точка достигнута, значение попадания равно 0. Поэтому я не могу проверить каждый отдельный слот на предмет лучшего предмета, так как не знаю, удовлетворю ли я свое значение попадания как можно точнее.
  • 1
    Вы случайно не делаете калькулятор снастей World Of Warcraft? ^^ И я буду думать о рейтинге хитов ...
Показать ещё 6 комментариев
0

Апроверка этой проблемы заключается в том, чтобы отключить некоторые из циклов и создать некоторые рабочие элементы, которые можно обрабатывать параллельно. Что-то вроде этого:

private struct Stat
{
   int sta;
   int power;
   int armor;
   int hit;
};
private List<Stat[]> workItems = new List<Stat[]>();
private const int NUMBER_OF_CPUS = 4;

private void BruteForce()
{

    //create some work load to split into separate threads
    //we do this by unnesting the first 3 loops.
    //maybe more unnesting is needed to get some more work items
    //for thread to process
    //at least one item per thread/CPU makes sense
    foreach (Stats head in _heads)
      foreach (Stats chest in _chests)
        foreach (Stats leg in _legs)
        {
          this.workItems .Add(new Stat[3] { head, chest, leg });
        }

Далее следует разделить эти рабочие элементы на куски, которые могут обрабатываться одним потоком. Вы выбрали бы ряд потоков, равных числу ядер ЦП. Функция потока будет, по крайней мере, иметь параметр, обеспечивающий рабочие элементы потоков.

Начало вашей функции потока может выглядеть так:

private void Process(object param)
        {
            List<Stat[]> threadWorkitems = (List<Stat[]>)param;

            foreach (Stat workItem in threadWorkitems)
            {
                Stats head = threadWorkitems[0];
                Stats chest = threadWorkitems[1];
                Stats leg = threadWorkitems[2];

                foreach (Stats feet in _feets)
                {
                    int stamina = head.sta + chest.sta + leg.sta + feet.sta;
                    int power = head.power + chest.power + leg.power + feet.power;
                    int armor = head.armor + chest.armor + leg.armor + feet.armor;
                    int hit = head.hit + chest.hit + leg.hit + feet.hit;

                    double total = stamina * _scaleSta + power * _scalePower + armor * _scaleArmor;

                    if (total > _bestScore && hit >= 100)
                    {

В этом фрагменте отсутствует то, что вам нужно собрать наилучшие результаты для всех потоков, а затем сравнить их, чтобы найти ваш самый лучший набор.

Надеюсь, это даст некоторые подсказки.

Привет

0

Вероятно, вы обнаружите, что добавление нескольких потоков к этому приведет к замедлению работы. Этот расчет очень медленный? Каковы ваши ожидаемые средние значения для голов, сундуков, ног и ног?

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

  • 0
    Я читаю в каждом слоте из XML-файла. Дело в том, что я упростил этот пример, который я здесь написал, до 4 слотов (голова, грудь, нога, ступни). В моей настоящей программе гораздо больше слотов (28). Таким образом, с 2 предметами в каждом слоте, это будет 2 ^ 28, что много. Однако, когда я пишу элементы в XML, я пытаюсь выяснить, какие из них являются действительно лучшими, поэтому у 15 слотов есть только один выбор. Делая это 2 ^ 13, что все еще много.
  • 0
    Для каждого слота, суммируйте sta, power и armor, также используйте переменные _scale. Теперь у вас есть лучший товар в каждой категории. Я не думаю, что вам нужно использовать подход с вложенными циклами.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню