Как параллельный процесс foreach_in_collection в I CC? - PullRequest
0 голосов
/ 29 марта 2020

Может кто-нибудь помочь мне с фрагментом кода о том, как параллельный процесс foreach_in_collection l oop? У меня есть огромный список сетей / булавок / ячеек коллекций, для которых я хочу выполнить l oop через.

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

###########################################################################
#proc to trace back from input to driver through buffers/inverters
proc trace_back_bufinv { pin } {
    set i 0;
    set not_buf false;
    set not_inv false;
    while {1} {
      set out [filter_collection [all_connected [get_flat_nets -of $pin] -leaf] "direction==out"]
      if {[sizeof_collection $out] == 0} { set out [filter_collection [all_connected [get_flat_nets -of $pin] -leaf] "object_class==port"]}
      set cc [get_flat_cells -of $out -quiet]
      if {[sizeof_collection $cc] == 0 } { set cc [get_attribute $out design] }
      if {[get_object_name $cc] == [get_attribute [get_designs] top_module_name]} { 
         set ref [get_attr [get_designs] top_module_name] } else {  set ref [get_attr $cc ref_name] }
      if {[regexp {_buf} $ref]} { set not_buf false; } else { set not_buf true}
      if {[regexp {_inv} $ref]} { set not_inv false; } else { set not_inv true}
      set pin [get_flat_pins -of $cc -filter "direction==in"]
      incr i;
      if {$not_inv && $not_buf} { return [get_object_name $out]; break }
    }
}

#proc to trace front from output to loads through buffers/inverters
proc trace_front_bufinv { pin } {
    redirect -variable testfront {report_buffer_trees -from $pin -hierarchy}
    set sink {}
    foreach line [split $testfront "\n"] {
        if {[regexp -nocase {Load } $line]} { lappend sink [lindex $line end-1]  }  
    }
    return $sink;
}
#----------------------------------------------------------------------------------------------------------------------------------
       set mod_cells               [get_flat_cells -of ${module_cell} ]
       set mod_cells_pins          [get_flat_pins -of  ${mod_cells}      -filter "port_type==signal && name!=ret && name!=nret"]

    foreach_in_collection mcc $mod_cells_pins {
            incr i;
            set direction [get_attribute $mcc direction]
            if {$direction=="in"} {
              set drivers  [trace_back_bufinv $mcc]
              set drivers  [get_flat_pins $drivers -quiet]
              set drivers  [add_to_collection -unique $drivers [get_ports $drivers -quiet]]
              set loads    $mcc
            } else {
              set drivers  $mcc
              set loads    [trace_front_bufinv $mcc]
              set loads    [get_flat_pins $loads -quiet]
              set loads    [add_to_collection -unique $loads [get_ports $loads -quiet]]
            }
    }

###########################################################################

1 Ответ

1 голос
/ 30 марта 2020

Теоретически, вы создаете пул потоков с соответствующим образом сконфигурированным (поскольку пулы потоков намного проще в управлении, чем необработанные потоки, когда вы имеете дело с лотами задач), поместите все задачи в пул, а затем подождите, пока все не закончится sh. Сложная часть - «соответствующим образом настроенная». Во-первых, есть количество потоков, которые нужно создать (практическое правило гласит: «Создавайте больше, пока вы не достигнете значительного улучшения производительности в целом» при настройке сценария, и нет, это раздражающе сложно получить полностью автоматически).

Но, во-вторых, что еще более важно, рабочие потоки должны иметь возможность обрабатывать задачи, которые вы им назначаете.

Если ваши задачи связаны с вводом / выводом, многопоточность в Tcl вряд ли сильно поможет (так как Tcl на самом деле очень хорош в асинхронном вводе / выводе), если только вы не делаете один из очень короткого списка вещей, где единственный API является синхронным. Если задачи связаны с памятью, многопоточность - плохая идея, пока вы не приобрели больше памяти! Только для задач, связанных с процессором, потоки действительно могут помочь. Чтобы оптимизировать это, реализация Tcl фокусируется на том, чтобы сохранить количество блокировок действительно небольшим, и делает это, требуя от вас реплицировать большинство вашего состояния между потоками; Вы можете использовать переменную общего доступа, но это не то, что вы получаете по умолчанию. И ваш код, который обеспечивает операции с сетями / выводами / ячейками, должен быть разделен таким образом или ориентирован на потоки (с соответствующими блокировками, если необходимо). Это главное требование; Эффективное распараллеливание сложного фрагмента кода может быть задачей нескольких человек!

Тем не менее, теоретически вы просто создаете локальный пакет (здесь называемый worker), который обеспечивает реализацию каждого рабочего потока и затем выполните:

package require Thread

set numThreads 8
set pool [tpool::create -maxworkers $numThreads -initcmd {
    package require worker
}]

# Launch the tasks in the background
foreach_in_collection item $collection {
    # worker::processItem is a command you define in the worker package
    set task [tpool::post -nowait $pool [list \
        worker::processItem $item]]
    set tasks($task) $item
    lappend inProgress $task
}

# Wait for them all to complete
while {[llength $inProgress]} {
    foreach finished [tpool::wait $pool $inProgress inProgress] {
        # Pick up the results from each of the finished tasks
        set item $tasks($finished)
        set result [tpool::get $pool $finished] 
        # Not sure what you want to do here
        puts "$item mapped to $result"
    }
}

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

...