bashrandomawkpassphrasemawk

Why calling the same function many times with different arguments is slower


I have put up a simple bash script that generates 4 words random passphrases from a list of thousands of words. Now I am not sure if it is really secure or efficient being for my personal use, you guys let me know if you think about any improvements. But that is not the main point. Check it out ->

So When I run it in my laptop, the input and output looks like this:

time sh genpass
astrology cringe tingling massager

real    0m0.319s
user    0m0.267s
sys     0m0.077s

A second time:

$ time sh genpass
prankish askew siren fritter

real    0m0.318s
user    0m0.266s
sys     0m0.077s

Can be quite funny sometimes.

Anyway, this is the script:

# EDITABLES ###########################################
target="/path/to/my/wordList.txt" 
# END EDITABLES #######################################

getWordList() {
  case $1 in
    "verb")  mawk '/ing$|ed$|en$/ {print $2}' $target ;;
    "adjective")  mawk '/y$|ish$/ {print $2}' $target ;;
    "noun")  mawk '!/ing$|ed$|en$|y$|ish$/ {print $2}' $target ;; 
    *) printf "%s" "'${1}' is an invalid argument." && echo && exit 1
  esac
}

pickRandomLineNumber() {
  # Get the list in an array
  declare -a list_a=("${!1}")
  # How many items in the list
  local length="${#list_a[@]}"
  # Generate a random number between 1 and the number of items in the list
  local number=$RANDOM 
  let "number %= $length"
  # Print the word at random line
  printf "%s\n" ${list_a[@]} | mawk -v line=$number 'NR==line {print}' 
}

read -ra verbList <<< $( getWordList verb )
verb=$(pickRandomLineNumber verbList[@])

read -ra adjectiveList <<< $( getWordList adjective )
adjective=$(pickRandomLineNumber adjectiveList[@])

read -ra nounList <<< $( getWordList noun )
noun1=$(pickRandomLineNumber nounList[@])
noun2=$(pickRandomLineNumber nounList[@])

printf "%s %s %s %s\n" "${adjective}" "${noun1}" "${verb}" "${noun2}"

See where I have to create an array for each type of word? 3 types, 3 arrays. Well I thought about getting that code in a function so I'd just have to call that function 4 times, one for each of my 4 words, with a different argument. I really thought it would be faster.

Here is the code change:

# EDITABLES ###########################################
target="/path/to/my/wordList.txt"  
# END EDITABLES #######################################

getWordList() {
  case $1 in
    "verb")  mawk '/ing$|ed$|en$/ {print $2}' $target ;;
    "adjective")  mawk '/y$|ish$/ {print $2}' $target ;;
    "noun")  mawk '!/ing$|ed$|en$|y$|ish$/ {print $2}' $target ;; 
    *) printf "%s" "'${1}' is an invalid argument." && echo && exit 1
  esac
}

pickRandomLineNumber() {
  # Get the list in an array
  declare -a list_a=("${!1}")
  # How many items in the list
  local length="${#list_a[@]}"
  # Generate a random number between 1 and the number of items in the list
  local number=$RANDOM 
  let "number %= $length"
  # Print the word at random line
  printf "%s\n" ${list_a[@]} | mawk -v line=$number 'NR==line {print}' 
}

#### CHANGE ####
getWord() {
  read -ra list <<< $( getWordList $1)
  local word=$(pickRandomLineNumber list[@])
  printf "%s" "${word}"
}

printf "%s %s %s %s\n" $(getWord adjective) $(getWord noun) $(getWord verb) $(getWord noun)

Now here is the input/output:

$ time sh genpass
overstay clench napping palace

real    0m0.403s
user    0m0.304s
sys     0m0.090s

And again:

$ time sh genpass
gainfully cameo extended nutshell

real    0m0.369s
user    0m0.304s
sys     0m0.090s

The differences in timing are not that big of a deal, even though overall, I thought it could definitely be faster.

So do you have any idea why the second script is slower than the first?


Solution

  • You have more code doing more stuff, all of it unnecessary. Here's how to do what you are trying to do:

    $ cat tst.awk
    function grw(arr) {     # Get Random Word
        return arr[int(rand() * length(arr)) + 1]
    }
    
    {
        if ( /(ing|ed|en)$/ ) verbs[++numVerbs] = $0
        else if ( /(y|ish)$/ ) adjectives[++numAdjectives] = $0
        else nouns[++numNouns] = $0
    }
    
    END {
        srand()
        printf "%s %s %s %s\n", grw(adjectives), grw(nouns), grw(verbs), grw(nouns)
    }
    
    $ awk -f tst.awk words
    overstay clench siren clench
    $ awk -f tst.awk words
    prankish nutshell tingling cameo
    $ awk -f tst.awk words
    astrology clench tingling palace
    

    The above was run against this "words" file created from the sample output you provided in your question:

    $ cat words
    askew
    astrology
    cameo
    clench
    cringe
    extended
    fritter
    gainfully
    massager
    napping
    nutshell
    overstay
    palace
    prankish
    siren
    tingling