Follow-up posts

More Cscope benchmarks

The last post reports some performance numbers for cscope. There's another, similar tool that I've been curious about: GNU global. It's like cscope in many ways. It doesn't have some of the nicer cscope search types (caller, callee, assignment, etc), and cscope works fine so I've never felt the need to move. Since I just ran some cscope benchmarks, I thought it'd be interesting to run the exact same tests with GNU global. Here I use the gtags-cscope frontend. This is a compatibility layer in GNU global that has an identical interface to cscope (among other things this makes it trivial to use xcscope.el with GNU global).

Test description

The test conditions are the same as before. The testing in this and the previous post was performed by a script, which appears at the end of this post. gtags-cscope doesn't have a separate inverted-index mode, so just a single test appears here.

Here I'm using GNU global 6.2.10 built from source (upstream is in some sort of fight with the Debian maintainer, so the packages are ancient). Cscope is 15.8a.

Results

All timings in seconds. Timings from the previous post are re-iterated for easy comparison.

Cold disk cache

  Normal mode/ext3 Kernel mode/ext3 GNU Global/ext3 Normal mode/tmpfs Kernel mode/tmpfs GNU Global/tmpfs
Initial database build 45.9 80.2 84.1 14.0 44.2 14.0
Database re-build after touching a file 10.4 48.9 26.8 3.2 30.1 0.7
Initial search 7.5 3.0 23.3 0.8 31.2 0.2
Re-search after touching a file 12.7 43.7 28.4 3.5 32.1 0.7
Initial no-db-update search 5.3 0.8 0.1 0.8 0.8 0.0
No-db-update re-search after touching a file 5.1 0.8 0.1 0.7 0.8 0.0

Warm disk cache

  Normal mode/ext3 Kernel mode/ext3 GNU Global/ext3 Normal mode/tmpfs Kernel mode/tmpfs GNU Global/tmpfs
Initial database build 13.8 49.6 18.0 12.9 44.4 13.7
Database re-build after touching a file 3.5 35.5 1.3 2.7 30.8 0.6
Initial search 0.8 0.1 0.4 0.8 30.8 0.2
Re-search after touching a file 4.0 33.5 1.3 3.5 31.9 0.6
Initial no-db-update search 0.7 0.0 0.0 0.7 0.7 0.0
No-db-update re-search after touching a file 0.7 0.0 0.0 0.7 0.7 0.0

Conclusions

During normal use, we'd have a warm cache and we'd be using a real hard disk. This is the bottom-left area of the timing tables. Those timings indicate that GNU Global is much faster than cscope. Search performance appears to be on-par with with an inverted-index-enabled cscope, but database build times only suffer a little bit. This is interesting, and maybe would be worth switching to at some point.

Benchmark script

All the timings were performed with the following zsh script. It uses some zsh-isms, but could be converted to bash if somebody cares to do it.

#!/bin/zsh

# needed in cleandb()
setopt nonomatch

function dropcaches() {
    if [[ $warmcold == "cold" ]]; then
        sync ;
        sudo sysctl -w vm.drop_caches=3;
    fi
    sleep 2;
}

function cleandb() {
    # requires nonomatch option to ignore missing globs
    rm -f cscope.out* G*;
}

function touchfile() {
    sleep 2; # very important. cscope needs this to see the file update
    touch include/drm/drm_edid.h;
}

TIMEFMT='%E'

awktally='
BEGIN {
  skip = ENVIRON["skip"]
}

/^[0-9\.]+s$/ {
  gsub("s","");
  str = str " " $1
  if( n >= skip )
  {
    sum += $1;
  }
  n++;
}

END {
  print ENVIRON["name"] ": skipping: " skip " all: " str " mean: " sum/(n-skip)
}'

typeset -A skipcounts
skipcounts=(cold 2 warm 2)

typeset -A modeoptions
modeoptions=(normal "" kernel "-k -q")

cscope-indexer -l -r

Nrepeat=8

for mode (normal kernel global)
{
    if [[ $mode == "global" ]]; then
        cmd="gtags-cscope";
    else
        cmd="cscope $modeoptions[$mode]";
    fi

    for dotouch (0 1)
    {
        for warmcold (cold warm)
        {
            export name="$warmcold initial build; $mode mode; touching: $dotouch";
            export skip=$skipcounts[$warmcold];
            repeat $(($Nrepeat + $skip)) {
                if (($dotouch)); then
                    touchfile;
                else
                    cleandb;
                fi
                dropcaches;
                time ${(z)cmd} -b;
            } |& awk $awktally
        }
    }

    for dotouch (0 1)
    {
        for warmcold (cold warm)
        {
            export name="$warmcold initial search; $mode mode; touching: $dotouch";
            export skip=$skipcounts[$warmcold];
            repeat $(($Nrepeat + $skip)) {
                if (($dotouch)); then
                    touchfile;
                fi
                dropcaches;
                time ${(z)cmd} -L0 main > /dev/null;
            } |& awk $awktally
        }
    }

    for dotouch (0 1)
    {
        for warmcold (cold warm)
        {
            export name="$warmcold initial no-db search; $mode mode; touching: $dotouch";
            export skip=$skipcounts[$warmcold];
            repeat $(($Nrepeat + $skip)) {
                if (($dotouch)); then
                    touchfile;
                fi
                dropcaches;
                time ${(z)cmd} -d -L0 main > /dev/null;
            } |& awk $awktally
        }
    }
}