Command Performance

Problem this snippet solves:

The article Ten Steps to iRules Optimization illustrates some ways to optimize your iRules. I took a look at the control statements and built a little iRule that will test those assertions and generate performance graphs using Google Charts to present the findings.

How to use this snippet:

Dependencies

This iRule relies on external Class files for the test on the "class match" command. The class names should be in the form of "class_xxx" where xxx is the list size you want to test. Include xxx number of entries with values from 0 to xxx-1. For a list size of 10, the class should look like this:

# Snippet in bigip.conf
class calc_10 {
   "0"
   "1"
   "2"
   "3"
   "4"
   "5"
   "6"
   "7"
   "8"
   "9"
}

I used perl to generate larger classes of size 100, 1000, 5000, and 10000 for my tests.

Usage

Assign the iRule to a virtual server and then browse to the url http://virtualserver/calccommands. I've included query string arguments to override the default test parameters as follows

  • ls=nnn - List Size. You will need a class defined titled calc_10 for a value of ls=10.
  • i=nnn - Number of iterations. This will be how many times the test is performed for each list size.
  • gw=nnn - Graph Width (default value of 300)
  • gh=nnn - Graph Height (default value of 200)
  • ym=nnn - Graph Y Max value (default 500)

An example usage is: http://virtualserver/calccommands?ls=1000&i=500. This will work on a list size of 1000 with 500 iterations per test.

Code :

when HTTP_REQUEST {

  #--------------------------------------------------------------------------
  # read in parameters
  #--------------------------------------------------------------------------
  set listsize    [URI::query [HTTP::uri] "ls"];
  set iterations  [URI::query [HTTP::uri] "i"];
  set graphwidth  [URI::query [HTTP::uri] "gw"];
  set graphheight [URI::query [HTTP::uri] "gh"];
  set ymax        [URI::query [HTTP::uri] "ym"];

  #--------------------------------------------------------------------------
  # set defaults
  #--------------------------------------------------------------------------
  if { ("" == $iterations) || ($iterations > 10000) } { set iterations 500; }
  if { "" == $listsize } { set listsize 5000; }
  if { "" == $graphwidth } { set graphwidth 300; }
  if { "" == $graphheight } { set graphheight 200; }
  if { "" == $ymax } { set ymax 500; }
  
  set modulus [expr $listsize / 5];
  set autosize 0;
  
  #--------------------------------------------------------------------------
  # build lookup list
  #--------------------------------------------------------------------------
  set matchlist "0";
  for {set i 1} {$i < $listsize} {incr i} {
    lappend matchlist "$i";
  }

  set luri [string tolower [HTTP::path]]

  switch -glob $luri {
    
    "/calccommands" {
      
      #----------------------------------------------------------------------
      # check for existence of class file.  If it doesn't exist
      # print out a nice error message.  Otherwise, generate a page of
      # embedded graphs that route back to this iRule for processing
      #----------------------------------------------------------------------
      
      if { [catch { class match "1" equals calc_$listsize } ] } {
        
        
        # error
        set content "<CENTER>BIG-IP Version $static::tcl_platform(tmmVersion)"
        append content "<H1 id="community-286142-toc-hId-1039316030"><FONT color="red">ERROR: class file 'calc_$listsize' not found</FONT></H1>";
        append content "";
        
      } else {
        
        # Build the html and send requests back in for the graphs...
        set content "<CENTER>BIG-IP Version $static::tcl_platform(tmmVersion)"
        append content "<P>List Size: ${listsize}</P><P></P><HR size="3" width="75%" /><P>"
        
        set c 0;
        foreach item $matchlist {
          set mod [expr $c % $modulus];
          if { $mod == 0 } {
            append content "<IMG src="$luri/$item"
            append content "?ls=${listsize}&i=${iterations}&gw=${graphwidth}&gh=${graphheight}&ym=${ymax}" />";
          }
          incr c;
        }
        append content "</P></CENTER>";
      }
      HTTP::respond 200 content $content;
      
    }

    "/calccommands/*" {
      
      #----------------------------------------------------------------------
      # Time various commands (switch, switch -glob, if/elseif, matchclass, 
      # class match) and generate redirect to a Google Bar Chart
      #----------------------------------------------------------------------
      
      set item [getfield $luri "/" 3]
      set labels "|"
      set values ""
      
      #----------------------------------------------------------------------
      # Switch
      #----------------------------------------------------------------------

      set expression "set t1 \[clock clicks -milliseconds\]; \n"
      append expression "for { set y 0 } { \$y < $iterations } { incr y } { "
      append expression "switch $item {"
      foreach i $matchlist {
        append expression "\"$i\" { } ";
      }
      append expression " } "
      append expression " } \n"
      append expression "set t2 \[clock clicks -milliseconds\]";
      
      eval $expression;
      
      set duration [expr {$t2 - $t1}]
      if { [expr {$duration < 0}] } { log local0. "NEGATIVE TIME ($item, matchclass: $t1 -> $t2"; }
      append labels "s|";
      if { $values ne "" } { append values ","; }
      append values "$duration";
      
      if { $autosize && ($duration > $ymax) } { set ymax $duration }
      
      #----------------------------------------------------------------------
      # Switch -glob
      #----------------------------------------------------------------------

      set expression "set t1 \[clock clicks -milliseconds\]; \n"
      append expression "for { set y 0 } { \$y < $iterations } { incr y } { "
      append expression "switch -glob $item {"
      foreach i $matchlist {
        append expression "\"$i\" { } ";
      }
      append expression " } "
      append expression " } \n"
      append expression "set t2 \[clock clicks -milliseconds\]";
      
      eval $expression;

      set duration [expr {$t2 - $t1}]
      if { [expr {$duration < 0}] } { log local0. "NEGATIVE TIME ($item, matchclass: $t1 -> $t2"; }
      append labels "s-g|";
      if { $values ne "" } { append values ","; }
      append values "$duration";
      
      if { $autosize && ($duration > $ymax) } { set ymax $duration }
      
      #----------------------------------------------------------------------
      # If/Elseif
      #----------------------------------------------------------------------
      set z 0;
      set y 0;

      set expression "set t1 \[clock clicks -milliseconds\]; \n"
      append expression "for { set y 0 } { \$y < $iterations } { incr y } { "
      foreach i $matchlist {
        if { $z > 0 } { append expression "else"; }
        append expression "if { $item eq \"$i\" } { } ";
        incr z;
      }
      append expression " } \n";
      append expression "set t2 \[clock clicks -milliseconds\]";

      eval $expression;
      
      set duration [expr {$t2 - $t1}]
      if { [expr {$duration < 0}] } { log local0. "NEGATIVE TIME ($item, matchclass: $t1 -> $t2"; }
      append labels "If|";
      if { $values ne "" } { append values ","; }
      append values "$duration";
      
      if { $autosize && ($duration > $ymax) } { set ymax $duration }
      
      #----------------------------------------------------------------------
      # Matchclass on list
      #----------------------------------------------------------------------
      
      set expression "set t1 \[clock clicks -milliseconds\]; \n"
      append expression "for { set y 0 } { \$y < $iterations } { incr y } { "
      append expression "if { \[matchclass $item equals \$matchlist \] } { }"
      append expression " } \n";
      append expression "set t2 \[clock clicks -milliseconds\]";
      
      eval $expression;
      
      set duration [expr {$t2 - $t1}]
      if { [expr {$duration < 0}] } { log local0. "NEGATIVE TIME ($item, matchclass: $t1 -> $t2"; }
      append labels "mc|";
      if { $values ne "" } { append values ","; }
      append values "$duration";
      
      if { $autosize && ($duration > $ymax) } { set ymax $duration }
      
      #----------------------------------------------------------------------
      # class match (with class)
      #----------------------------------------------------------------------
      
      set expression "set t1 \[clock clicks -milliseconds\]; \n"
      append expression "for { set y 0 } { \$y < $iterations } { incr y } { "
      append expression "if { \[class match $item equals calc_$listsize \] } { }"
      append expression " } \n";
      append expression "set t2 \[clock clicks -milliseconds\]";

      log local0. $expression;
      
      eval $expression;
      
      set duration [expr {$t2 - $t1}]
      if { [expr {$duration < 0}] } { log local0. "NEGATIVE TIME ($item, matchclass: $t1 -> $t2"; }
      append labels "c|";
      if { $values ne "" } { append values ","; }
      append values "$duration";

      if { $autosize && ($duration > $ymax) } { set ymax $duration }
      
      #----------------------------------------------------------------------
      # build redirect for the google chart and issue a redirect
      #----------------------------------------------------------------------
      
      set mod [expr $item % 10]
      set newuri "http://${mod}.chart.apis.google.com/chart?chxl=0:${labels}&chxr=1,0,${ymax}&chxt=x,y"
      append newuri "&chbh=a&chs=${graphwidth}x${graphheight}&cht=bvg&chco=A2C180&chds=0,${ymax}&chd=t:${values}"
      append newuri "&chdl=(in+ms)&chtt=Perf+(${iterations}-${item}/${listsize})&chg=0,2&chm=D,0000FF,0,0,3,1"
      
      HTTP::redirect $newuri;
    }
  }
}

 

Updated Aug 20, 2025
Version 2.0

2 Comments

  • Would it be possible to update this code for the newer Google chart apis?

  • Hi, after requesting an updated script, I took upon myself to make the modifications. I switched the charting engine to quickchart.io, which should be stable(ish) for the next several years. I also inserted some addition remarks with use examples.

     

    😀