首页 > 解决方案 > 如何在 ICC 中并行处理 foreach_in_collection?

问题描述

有人可以帮我提供有关如何并行处理foreach_in_collection循环的代码片段吗?我有一个庞大的集合网络/引脚/单元列表,我想循环访问它们。

以串行方式一个接一个地运行脚本需要几个小时。有没有办法让我的大列表分成几部分并让循环并行执行?

###########################################################################
#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]]
            }
    }

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

标签: tclicc

解决方案


理论上,您创建一个适当配置的线程池(因为当您处理大量任务时,线程池比原始线程更容易管理),将所有任务放入池中,然后等待一切完成。复杂的部分是“适当配置”。首先,有要创建的线程数(经验法则是“创建更多,直到您在调整脚本时总体上没有获得有意义的更好的性能”,不,完全自动正确处理是非常棘手的)。

但其次,更重要的是,工作线程需要能够处理您分配给它们的任务。

如果您的任务是受 I/O 限制的,那么 Tcl 中的线程不太可能有太大帮助(因为 Tcl 实际上非常擅长异步 I/O),除非您碰巧在做一个只有 API 是同步的非常简短的事情之一。如果任务受内存限制,那么在您获得更多内存之前,线程是一个坏主意!只有在 CPU 密集型任务中,线程才能真正提供帮助。为了优化这一点,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包对你来说真的很难。你不想让所有东西都排在一个巨大的全局锁后面;这种方式实际上可能会失去性能而不是获得它。


推荐阅读