Skip to content

Commit 7b04660

Browse files
committed
Improve column output performances
Improve the performance of the module’s column output by removing the costly optimization that attempts to fit more columns within the available screen width. As a result in some situations, module list or module avail may now have an output a little less dense. This new grid output is exactly the same as the one from the "ls" command. Output performances are clearly improved. On "avail4" test case of script/mb utility, a "module avail" over 962 modulefiles takes 47 ms instead of 73 ms (-35% elapsed time). Closes #622 Signed-off-by: Xavier Delaruelle <xavier.delaruelle@cea.fr>
1 parent 8fd1ba2 commit 7b04660

47 files changed

Lines changed: 1970 additions & 1980 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

NEWS.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ Modules 5.7.0 (not yet released)
7070
:mconfig:`non_exportable_tags` is changed with :subcmd:`config` sub-command,
7171
it sets the :envvar:`MODULES_NON_EXPORTABLE_TAGS` environment variable. (fix
7272
issue #608)
73+
* Improve the performance of the module’s column output by removing the costly
74+
optimization that attempts to fit more columns within the available screen
75+
width. (fix issue #622)
7376

7477

7578
.. _5.6 release notes:

tcl/report.tcl.in

Lines changed: 60 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,7 +1590,6 @@ proc reportModules {search_queries header hsgrkey hstyle show_mtime show_idx\
15901590
}
15911591
15921592
set len_list {}
1593-
set max_len 0
15941593
# dictionary-sort results unless if output order is specified
15951594
if {![llength $mod_list_order]} {
15961595
set clean_list [lsort -dictionary $clean_list]
@@ -1607,9 +1606,6 @@ proc reportModules {search_queries header hsgrkey hstyle show_mtime show_idx\
16071606
# compute display element length list on sorted result
16081607
lappend display_list $sgrmap($disp)
16091608
lappend len_list $lenmap($disp)
1610-
if {$lenmap($disp) > $max_len} {
1611-
set max_len $lenmap($disp)
1612-
}
16131609
}
16141610
}
16151611
@@ -1622,7 +1618,7 @@ proc reportModules {search_queries header hsgrkey hstyle show_mtime show_idx\
16221618
16231619
# output formatted elements
16241620
displayElementList $header $hsgrkey $hstyle $one_per_line $show_idx 1\
1625-
$display_list $len_list $max_len $via
1621+
$display_list $len_list $via
16261622
}
16271623
16281624
proc showModulePath {} {
@@ -1667,10 +1663,10 @@ proc displaySeparatorLine {{title {}} {sgrkey {}} {extra {}}} {
16671663
# get a list of elements and print them in a column or in a
16681664
# one-per-line fashion
16691665
proc displayElementList {header sgrkey hstyle one_per_line display_idx\
1670-
start_idx display_list {len_list {}} {max_len 0} {via {}}} {
1666+
start_idx display_list {len_list {}} {via {}}} {
16711667
set elt_cnt [llength $display_list]
16721668
reportDebug "header=$header, sgrkey=$sgrkey, hstyle=$hstyle,\
1673-
elt_cnt=$elt_cnt, max_len=$max_len, one_per_line=$one_per_line,\
1669+
elt_cnt=$elt_cnt, one_per_line=$one_per_line,\
16741670
display_idx=$display_idx, start_idx=$start_idx, via=$via"
16751671
16761672
# end proc if no element are to print
@@ -1716,97 +1712,10 @@ proc displayElementList {header sgrkey hstyle one_per_line display_idx\
17161712
# save room for numbers and spacing: 2 or 3 digits + ) + space
17171713
set elt_prefix_len [expr {$display_idx ? $idx_len + 2 : {0}}]
17181714
# save room for two spaces after element
1719-
set elt_suffix_len 2
1720-
1721-
# compute rows*cols grid size with optimized column number
1722-
# the size of each column is computed to display as much column
1723-
# as possible on each line
1724-
incr max_len $elt_suffix_len
1725-
foreach len $len_list {
1726-
lappend elt_len [incr len $elt_suffix_len]
1727-
}
1728-
17291715
set tty_cols [getState term_columns]
1730-
# find valid grid by starting with non-optimized solution where each
1731-
# column length is equal to the length of the biggest element to display
1732-
set cur_cols [tcl::mathfunc::max [expr {int(($tty_cols - \
1733-
$elt_prefix_len) / $max_len)}] 0]
1734-
# when display is found too short to display even one column
1735-
if {$cur_cols == 0} {
1736-
set cols 1
1737-
set rows $elt_cnt
1738-
array set col_width [list 0 $max_len]
1739-
} else {
1740-
set cols 0
1741-
set rows 0
1742-
}
1743-
set last_round 0
1744-
set restart_loop 0
1745-
while {$cur_cols > $cols} {
1746-
if {!$restart_loop} {
1747-
if {$last_round} {
1748-
incr cur_rows
1749-
} else {
1750-
set cur_rows [expr {int(ceil(double($elt_cnt) / $cur_cols))}]
1751-
}
1752-
for {set i 0} {$i < $cur_cols} {incr i} {
1753-
set cur_col_width($i) 0
1754-
}
1755-
for {set i 0} {$i < $cur_rows} {incr i} {
1756-
set row_width($i) 0
1757-
}
1758-
set istart 0
1759-
} else {
1760-
##nagelfar ignore Unknown variable
1761-
set istart [expr {$col * $cur_rows}]
1762-
# only remove width of elements from current col
1763-
for {set row 0} {$row < ($i % $cur_rows)} {incr row} {
1764-
##nagelfar ignore Unknown variable
1765-
incr row_width($row) -[expr {$pre_col_width + $elt_prefix_len}]
1766-
}
1767-
}
1768-
set restart_loop 0
1769-
for {set i $istart} {$i < $elt_cnt} {incr i} {
1770-
set col [expr {int($i / $cur_rows)}]
1771-
set row [expr {$i % $cur_rows}]
1772-
# restart loop if a column width change
1773-
if {[lindex $elt_len $i] > $cur_col_width($col)} {
1774-
set pre_col_width $cur_col_width($col)
1775-
set cur_col_width($col) [lindex $elt_len $i]
1776-
set restart_loop 1
1777-
break
1778-
}
1779-
# end search of maximum number of columns if computed row width
1780-
# is larger than terminal width
1781-
if {[incr row_width($row) +[expr {$cur_col_width($col) \
1782-
+ $elt_prefix_len}]] > $tty_cols} {
1783-
# start last optimization pass by increasing row number until
1784-
# reaching number used for previous column number, by doing so
1785-
# this number of column may pass in terminal width, if not
1786-
# fallback to previous number of column
1787-
if {$last_round && $cur_rows == $rows} {
1788-
incr cur_cols -1
1789-
} else {
1790-
set last_round 1
1791-
}
1792-
break
1793-
}
1794-
}
1795-
# went through all elements without reaching terminal width limit so
1796-
# this number of column solution is valid, try next with a greater
1797-
# column number
1798-
if {$i == $elt_cnt} {
1799-
set cols $cur_cols
1800-
set rows $cur_rows
1801-
array set col_width [array get cur_col_width]
1802-
# number of column is fixed if last optimization round has started
1803-
# reach end also if there is only one row of results
1804-
if {!$last_round && $rows > 1} {
1805-
incr cur_cols
1806-
}
1807-
}
18081716
1809-
}
1717+
lassign [compute_output_grid $tty_cols $len_list $elt_prefix_len] rows\
1718+
cols cols_width
18101719
reportDebug list=$display_list
18111720
reportDebug "rows/cols=$rows/$cols,\
18121721
lastcol_item_cnt=[expr {int($elt_cnt % $rows)}]"
@@ -1821,7 +1730,8 @@ proc displayElementList {header sgrkey hstyle one_per_line display_idx\
18211730
}
18221731
# cannot use 'format' as strings may contain SGR codes
18231732
append displist [lindex $display_list $index][string repeat\
1824-
{ } [expr {$col_width($col) - [lindex $len_list $index]}]]
1733+
{ } [expr {[lindex $cols_width $col] - [lindex $len_list\
1734+
$index] - $elt_prefix_len}]]
18251735
}
18261736
}
18271737
append displist \n
@@ -1834,6 +1744,58 @@ proc displayElementList {header sgrkey hstyle one_per_line display_idx\
18341744
reportSeparateNextContent
18351745
}
18361746
1747+
# returns rows, cols and each col width of the output grid with most cols
1748+
proc compute_output_grid {out_width len_list prefix_len} {
1749+
set col_sepa_len 2
1750+
set elt_count [llength $len_list]
1751+
if {!$elt_count} {
1752+
return {0 0 {0}}
1753+
}
1754+
1755+
# start with max possible columns guessed from shortest element in list
1756+
set min_len [tcl::mathfunc::min {*}$len_list]
1757+
set col_min [expr {$prefix_len + $min_len + $col_sepa_len}]
1758+
# exact same output with 1 elt to print whether width is tiny or large
1759+
set max_cols [tcl::mathfunc::min $elt_count\
1760+
[expr {int(floor(double($out_width) / $col_min))}]]
1761+
1762+
for {set cols $max_cols} {$cols > 1} {incr cols -1} {
1763+
set rows [expr {int(ceil(double($elt_count) / $cols))}]
1764+
set cols_width {}
1765+
1766+
# compute width of each column (max length among elements in that col)
1767+
for {set c 0} {$c < $cols} {incr c} {
1768+
set idx_first [expr {$c * $rows}]
1769+
set idx_last [expr {$idx_first + $rows - 1}]
1770+
# stop when no more element for available columns
1771+
if {$idx_first >= $elt_count} {
1772+
break
1773+
}
1774+
set col_max [tcl::mathfunc::max {*}[lrange $len_list $idx_first\
1775+
$idx_last]]
1776+
set col_width [expr {$prefix_len + $col_max}]
1777+
# column separator suffix is not added on last column
1778+
if {($c + 1) < $cols && ($idx_last + 1) < $elt_count} {
1779+
incr col_width $col_sepa_len
1780+
}
1781+
lappend cols_width $col_width
1782+
1783+
# immediately try fewer columns if we already exceed output grid
1784+
if {[tcl::mathop::+ {*}$cols_width] > $out_width} {
1785+
break
1786+
}
1787+
}
1788+
1789+
# ff it fits, this is the maximum cols possible
1790+
if {[tcl::mathop::+ {*}$cols_width] <= $out_width} {
1791+
return [list $rows $cols $cols_width]
1792+
}
1793+
}
1794+
1795+
# fits in 1 column
1796+
return [list $elt_count 1 {0}]
1797+
}
1798+
18371799
# Report an output key to help understand what the SGR used on this output
18381800
# correspond to
18391801
proc displayKey {} {
@@ -1936,19 +1898,11 @@ proc displayKey {} {
19361898
}
19371899
}
19381900
1939-
# find largest element
1940-
set max_len 0
1941-
foreach len $len_list {
1942-
if {$len > $max_len} {
1943-
set max_len $len
1944-
}
1945-
}
1946-
19471901
if {[llength $display_list]} {
19481902
# display header
19491903
report Key:
19501904
# display key content
1951-
displayElementList noheader {} {} 0 0 0 $display_list $len_list $max_len
1905+
displayElementList noheader {} {} 0 0 0 $display_list $len_list
19521906
}
19531907
}
19541908

tcl/subcmd.tcl.in

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -927,7 +927,6 @@ proc cmdModuleSavelist {show_oneperline show_mtime search_match args} {
927927
}
928928
set display_list {}
929929
set len_list {}
930-
set max_len 0
931930
set one_per_line [expr {$show_mtime || $show_oneperline}]
932931
set show_idx [expr {!$one_per_line}]
933932
# prepare query to highlight
@@ -948,15 +947,12 @@ proc cmdModuleSavelist {show_oneperline show_mtime search_match args} {
948947
} else {
949948
lappend display_list $collsgr
950949
lappend len_list [set len [string length $coll]]
951-
if {$len > $max_len} {
952-
set max_len $len
953-
}
954950
}
955951
}
956952
}
957953

958954
displayElementList noheader {} {} $one_per_line $show_idx $start_idx\
959-
$display_list $len_list $max_len
955+
$display_list $len_list
960956
}
961957
}
962958

testsuite/example/siteconfig.tcl-1

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,11 @@ if {[info exists env(TESTSUITE_ENABLE_SITECONFIG_TRICKYLISTDISP)]} {
4444
# set a specific terminal column number to fall in tricky condition
4545
set orig_term_columns [getState term_columns]
4646
setState term_columns 80
47-
set max_len 0
4847
foreach elt [list abc/def abcdefgh/ijklmnop abc/defg abcd/ef abc/defg-hijkl.mn.op abcdefgh/ijklm.nopqrst abcdefg/hijklmnop-qr.st.uvw abcdefgh/ijklmnopqrst-u-vwxy.zA-BCD-E abcdef/ghijklm-nopq.r_st.uv abcdefgh/ijklmnop-q-rstu.vw-xyz-A abcdefg/hijklmn-op.qr.stu abcdefg/hijkl-mn.op.qrs abcd/efgh-ij.k abcdefg/hijk-lmnop_qr.st.uvw abcdef/ghijklmno-p.q.r abcdefgh/ijklmn-o-pqrs.tu abcdefgh/ijklmnop-q-rstu.vw-xyz abcdefg/hij-klm.nopq abcdefg/hij-kl.mn.opq abcdefgh/ijklmnopq_rstuv-w-xyzA.BC-DEF abcdefgh/ijklmnopqrstuv-w-xyzA.BC abcdefgh/ij-k-lmno.pq abcdefgh/ijk-l-mnop.qr-stu abcdefgh/ijklmnop-qr.st abcdef/ghijklmno-pq.rs abcdef/ghi-jklm_no_pqrstuv abcdefgh/ijk-lm-n-opqr.st abcdefgh/ijklm-n-opqr.st-uvw abcdef/ghijklm-nopq_r abcdefg/hijkl-mn.op.qzs abcdefg/hijklmn-op.qr.stz abcdefgh/ijklmn] {
4948
lappend display_list $elt
5049
lappend len_list [string length $elt]
51-
if {[string length $elt] > $max_len} {
52-
set max_len [string length $elt]
53-
}
5450
}
55-
displayElementList test {} terse 0 1 1 $display_list $len_list $max_len
51+
displayElementList test {} terse 0 1 1 $display_list $len_list
5652
setState term_columns $orig_term_columns
5753
}
5854

@@ -701,4 +697,9 @@ if {[info exists env(TESTSUITE_ENABLE_RUNENVCOMMAND_UNSET)]} {
701697
}
702698
}
703699

700+
# specific tests to improve compute_output_grid coverage
701+
if {[info exists env(TESTSUITE_ENABLE_SITECONFIG_COMPUTEOUTPUTGRID)]} {
702+
report [compute_output_grid 80 {} 3]
703+
}
704+
704705
}

testsuite/modules.00-init/120-siteconfig.exp

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -217,27 +217,38 @@ unset env(TESTSUITE_ENABLE_SITECONFIG_TERSENUM)
217217
# test displayElementList procedure with a tricky list that triggers unusual condition
218218
setenv_var TESTSUITE_ENABLE_SITECONFIG_TRICKYLISTDISP 1
219219
set ans "test:
220-
1\\) abc/def 22\\) abcdefgh/ij-k-lmno.pq
221-
2\\) abcdefgh/ijklmnop 23\\) abcdefgh/ijk-l-mnop.qr-stu
222-
3\\) abc/defg 24\\) abcdefgh/ijklmnop-qr.st
223-
4\\) abcd/ef 25\\) abcdef/ghijklmno-pq.rs
224-
5\\) abc/defg-hijkl.mn.op 26\\) abcdef/ghi-jklm_no_pqrstuv
225-
6\\) abcdefgh/ijklm.nopqrst 27\\) abcdefgh/ijk-lm-n-opqr.st
226-
7\\) abcdefg/hijklmnop-qr.st.uvw 28\\) abcdefgh/ijklm-n-opqr.st-uvw
227-
8\\) abcdefgh/ijklmnopqrst-u-vwxy.zA-BCD-E 29\\) abcdef/ghijklm-nopq_r
228-
9\\) abcdef/ghijklm-nopq.r_st.uv 30\\) abcdefg/hijkl-mn.op.qzs
229-
10\\) abcdefgh/ijklmnop-q-rstu.vw-xyz-A 31\\) abcdefg/hijklmn-op.qr.stz
230-
11\\) abcdefg/hijklmn-op.qr.stu 32\\) abcdefgh/ijklmn
231-
12\\) abcdefg/hijkl-mn.op.qrs
232-
13\\) abcd/efgh-ij.k
233-
14\\) abcdefg/hijk-lmnop_qr.st.uvw
234-
15\\) abcdef/ghijklmno-p.q.r
235-
16\\) abcdefgh/ijklmn-o-pqrs.tu
236-
17\\) abcdefgh/ijklmnop-q-rstu.vw-xyz
237-
18\\) abcdefg/hij-klm.nopq
238-
19\\) abcdefg/hij-kl.mn.opq
239-
20\\) abcdefgh/ijklmnopq_rstuv-w-xyzA.BC-DEF
240-
21\\) abcdefgh/ijklmnopqrstuv-w-xyzA.BC
220+
1\\) abc/def
221+
2\\) abcdefgh/ijklmnop
222+
3\\) abc/defg
223+
4\\) abcd/ef
224+
5\\) abc/defg-hijkl.mn.op
225+
6\\) abcdefgh/ijklm.nopqrst
226+
7\\) abcdefg/hijklmnop-qr.st.uvw
227+
8\\) abcdefgh/ijklmnopqrst-u-vwxy.zA-BCD-E
228+
9\\) abcdef/ghijklm-nopq.r_st.uv
229+
10\\) abcdefgh/ijklmnop-q-rstu.vw-xyz-A
230+
11\\) abcdefg/hijklmn-op.qr.stu
231+
12\\) abcdefg/hijkl-mn.op.qrs
232+
13\\) abcd/efgh-ij.k
233+
14\\) abcdefg/hijk-lmnop_qr.st.uvw
234+
15\\) abcdef/ghijklmno-p.q.r
235+
16\\) abcdefgh/ijklmn-o-pqrs.tu
236+
17\\) abcdefgh/ijklmnop-q-rstu.vw-xyz
237+
18\\) abcdefg/hij-klm.nopq
238+
19\\) abcdefg/hij-kl.mn.opq
239+
20\\) abcdefgh/ijklmnopq_rstuv-w-xyzA.BC-DEF
240+
21\\) abcdefgh/ijklmnopqrstuv-w-xyzA.BC
241+
22\\) abcdefgh/ij-k-lmno.pq
242+
23\\) abcdefgh/ijk-l-mnop.qr-stu
243+
24\\) abcdefgh/ijklmnop-qr.st
244+
25\\) abcdef/ghijklmno-pq.rs
245+
26\\) abcdef/ghi-jklm_no_pqrstuv
246+
27\\) abcdefgh/ijk-lm-n-opqr.st
247+
28\\) abcdefgh/ijklm-n-opqr.st-uvw
248+
29\\) abcdef/ghijklm-nopq_r
249+
30\\) abcdefg/hijkl-mn.op.qzs
250+
31\\) abcdefg/hijklmn-op.qr.stz
251+
32\\) abcdefgh/ijklmn
241252

242253
$vers_reportre"
243254
testouterr_cmd_re "sh" "-V" "OK" $ans
@@ -666,8 +677,8 @@ lappend ans { invoked from within}
666677
lappend ans { "displaySeparatorLine $header $sgrkey $extra"}
667678
lappend ans { (procedure "displayElementList" line 19)}
668679
lappend ans { invoked from within}
669-
lappend ans { "displayElementList $header $hsgrkey $hstyle $one_per_line $show_idx 1 $display_list $len_list $max_len $via"}
670-
lappend ans { (procedure "reportModules" line 155)}
680+
lappend ans { "displayElementList $header $hsgrkey $hstyle $one_per_line $show_idx 1 $display_list $len_list $via"}
681+
lappend ans { (procedure "reportModules" line 151)}
671682
lappend ans { invoked from within}
672683
lappend ans { "reportModules $args $dir mp $hstyle $show_mtime 0 $one_per_line $theader_cols hidden-loaded"}
673684
lappend ans { (procedure "cmdModuleAvail" line 43)}
@@ -766,6 +777,13 @@ lappend ans $vers_reportre
766777
testouterr_cmd_re sh -V OK [join $ans \n]
767778
unsetenv_var TESTSUITE_ENABLE_RUNENVCOMMAND_UNSET
768779

780+
setenv_var TESTSUITE_ENABLE_SITECONFIG_COMPUTEOUTPUTGRID 1
781+
set ans [list]
782+
lappend ans {0 0 \{0\}}
783+
lappend ans $vers_reportre
784+
testouterr_cmd_re sh -V OK [join $ans \n]
785+
unsetenv_var TESTSUITE_ENABLE_SITECONFIG_COMPUTEOUTPUTGRID
786+
769787
} elseif {$verbose} {
770788
send_user "\tSkip tests relying on an excepted siteconfig file installed\n"
771789
}

0 commit comments

Comments
 (0)