Tk Library Source Code

View Ticket
Login
Ticket UUID: 458446
Title: BWidget: faster ListBox inserts
Type: Patch Version: None
Submitter: bach Created on: 2001-09-04 16:52:03
Subsystem: bwidget Assigned To: hobbs
Priority: 5 Medium Severity:
Status: Closed Last Modified: 2001-12-29 08:33:24
Resolution: Fixed Closed By: hobbs
    Closed on: 2001-12-29 01:33:24
Description:
Motivation:

The BWidget ListBox insert command isn't a speed demon.
This is not
too noticable for small lists, but it gets rather slow
for big ones
E.g. inserting 500 entries with just -text and -image
attributes set 
     takes approx. 1 second on a Pentium III 550MHz.

Problem analysis:

The bulk of the time in ListBox::insert gets eaten by a
call to
Widget::init. In Widget::init, most of the time is used
by
initializing and parsing from standard options
database. The example
from above uses ~1250msec for building up the std.
options and
~250msec to apply specific options for the item.

Proposed patch:

Two functions to speed up inserts of many items that
are all almost
equal. 

First, Widget::copyinit. This works almost like the
normal init, but
saves time by copying standard option initialization
from a given
template. It then applies specific item options (just
like init).

Second, ListBox::multipleinsert. This also works like
its pendant
ListBox::insert, but knows about the aforementioned
Widget::copyinit
function. I shortly thought of changing the original
'insert', but the
effects of a change there would not be backward
compatible.

Sideeffects:
None to existing code. A widget function more to
document for the user
(ListBox::multipleinsert).

Results:
The speedup of using multipleinsert is a factor of 2
when used almost
exactly like a multiple call to insert.
That is:

    set l
    foreach n {name1 name2 name3 ... namen} {
        lappend l $n "-text $n -image something.gif"
    }
    $list multipleinsert end $l

is 2 times faster than its pendant

    foreach n {name1 name2 name3 ... namen} {
        $list insert end $n -text $n -image
something.gif
    }

Assuming that, e.g. all images used are the same (as in
the example
above, the speedup is factor 3 by using:
    set l
    foreach n {name1 name2 name3 ... namen} {
        if {[llength $l] == 0} {
            lappend l $n "-text $n -image
something.gif"
} else {
            lappend l $n "-text $n"
        }
    $list multipleinsert end $l


Code:

In listbox.tcl:

# Bastien Chevreux ([email protected])
# The multipleinsert command performs inserts several
items at once into
#  the list. It is faster than calling insert multiple
times as it uses the
#  Widget::copyinit command for initializing all items
after the 1st. The 
#  speedup factor is between 2 and 3 for typical usage,
but could be higher
#  for inserts with many options.
#
# Syntax: path and index are as in the insert command
#         args is a list of even numbered elements
where the 1st of each pair
#          corresponds to the item of 'insert' and the
second to args of 'insert'.
#
------------------------------------------------------------------------------
#  Command ListBox::multipleinsert
#
------------------------------------------------------------------------------
proc ListBox::multipleinsert { path index args } {
    variable $path
    upvar 0  $path data

    # If we got only one list as arg, take the first
element as args
    # This enables callers to use 
    #   $list multipleinsert index $thelist
    # instead of
    #   eval $list multipleinsert index $thelist

    if {[llength $args] == 1} {
set args [lindex $args 0]
    }

    set count 0
    foreach {item iargs} $args {
if { [lsearch $data(items) $item] != -1 } {
    return -code error "item \"$item\" already exists"
}

if {$count==0} {
    Widget::init ListBox::Item $path.$item $iargs
    set firstpath $path.$item
} else {
    Widget::copyinit ListBox::Item $firstpath
$path.$item $iargs
}

if { ![string compare $index "end"] } {
    eval lappend data(items) $item
} else {
    set data(items) [linsert $data(items) $index
$item]
}
set data(upd,create,$item) $item

incr count
    }

    _redraw_idle $path 2
    return $item
}

       

In widget.tcl:

# Bastien Chevreux ([email protected])
#
# copyinit performs basically the same job as init, but
it uses a
#  existing template to initialize its values. So,
first a perferct copy
#  from the template is made just to be altered by any
existing options
#  afterwards.
# But this still saves time as the first initialization
parsing block is
#  skipped.
# As additional bonus, items that differ in just a few
options can be
#  initialized faster by leaving out the options that
are equal.

# This function is currently used only by
ListBox::multipleinsert, but other
#  calls should follow :)

#
------------------------------------------------------------------------------
#  Command Widget::copyinit
#
------------------------------------------------------------------------------
proc Widget::copyinit { class templatepath path options
} {
    upvar 0 ${class}::opt classopt
    upvar 0 ${class}::$path:opt  pathopt
    upvar 0 ${class}::$path:mod  pathmod
    upvar 0 ${class}::$path:init pathinit

    upvar 0 ${class}::$templatepath:opt  
templatepathopt
    upvar 0 ${class}::$templatepath:mod  
templatepathmod
    upvar 0 ${class}::$templatepath:init 
templatepathinit

    if { [info exists pathopt] } {
unset pathopt
    }
    if { [info exists pathmod] } {
unset pathmod
    }

    # We use the template widget for option db copying,
but it has to exist!
    array set pathmod  [array get templatepathmod]
    array set pathopt  [array get templatepathopt]
    array set pathinit [array get templatepathinit]

    set Widget::_class($path) $class
    foreach {option value} $options {
        if { ![info exists classopt($option)] } {
            unset pathopt
            unset pathmod
            return -code error "unknown option
\"$option\""
        }
        set optdesc $classopt($option)
        set type    [lindex $optdesc 0]
        if { ![string compare $type "Synonym"] } {
            set option  [lindex $optdesc 1]
            set optdesc $classopt($option)
            set type    [lindex $optdesc 0]
        }
        set pathopt($option)
[$Widget::_optiontype($type) $option $value [lindex
$optdesc 3]]
set pathinit($option) $pathopt($option)
    }
}
User Comments: hobbs added on 2001-12-29 08:33:24:
Logged In: YES 
user_id=72656

added to 1.4.0.cvs

patthoyts added on 2001-10-26 15:07:12:
Logged In: YES 
user_id=202636

Reset category from tkchat to bwidget as this is a bwidget
patch.

bach added on 2001-09-04 23:56:26:

File Added - 10369: lb_patch.txt

Logged In: YES 
user_id=98533

*sigh* I should've learned by now to attach files and not to
paste. Properly formated code is attached separately.

Attachments: