如何提高以下代码的性能

如何提高以下代码的性能

下面是我的脚本,它有很多性能问题

#!/usr/bin/ksh
while read i
do
  x=`echo $i |cut -d"|" -f2`
  rem=`expr $x % 62`
  echo "reminder is " $rem
  quo=`expr $x / 62`
  echo "quotiont is " $quo

  grp_rem=" "
  if [[ ${#quo} -ge 2 ]]
  then
    while [ $quo -ge 62 ]
    do
      sub_rem=`expr $quo % 62`
      quo=`expr $quo / 62`
      grp_rem=`echo $sub_rem" "$grp_rem`
    done
  fi
  echo $i"|"$quo" "$grp_rem" "$rem >> base62_while.out
done < base62_while.txt

无论如何,我可以使用上面的脚本提高性能吗?

示例输入:

1|5147634738948389685

样本输出

1|5147634738948389685|6 8 16 13 46 17 20 35 9 49 43

答案1

您不需要调用任何外部工具:ksh 可以进行算术运算。我还使用数组来存储余数

#!/usr/bin/ksh
div=62
while IFS='|' read -r n x; do
    rem=$(( x % div ))
    quo=$(( x / div ))
    echo "reminder is  $rem" >&2
    echo "quotiont is  $quo" >&2

    remainders=( $rem )
    while (( quo >= div )); do
        sub_rem=$(( quo % 62 ))
        quo=$(( quo / 62 ))
        echo "reminder is  $sub_rem" >&2
        echo "quotiont is  $quo" >&2
        remainders=( $sub_rem "${remainders[@]}" )
    done
    echo "$n|$x|$quo ${remainders[*]}"

    x=$quo
    for r in "${remainders[@]}"; do
        x=$(( x * div + r ))
    done
    echo Verification: $x
done <<END
1|5147634738948389685
END

答案2

这应该会快得多

#!/usr/bin/ksh
#
while IFS='|' read n x
do
    base62="$(echo "obase=62; $x" | bc | sed -re 's/ 0/ /g' -e 's/^ //')"
    printf "%d|%s|%s\n" $n "$x" "$base62"
done <base62_while.txt >>base62_while.out

base62行用于bc将十进制源数字转换为等效的 62 基数。它输出两位十进制对,我们从中去掉任何前导零(即02重写为2,但45保持不变)。

输入

1|5147634738948389685

输出

1|5147634738948389685|6 8 16 13 46 17 20 35 9 49 43

答案3

有几件事可以做(并提高速度):

  • 原版 1000 个号码
    35.023 秒
  • 将所有 expr 命令替换为算术扩展 $((x%62))
    14.473
  • 转换grp_rem=`echo $sub_rem" "$grp_rem`grp_rem="$sub_rem $grp_rem"
    3.131
  • 避免使用 cut ( set IFS='|'; set -f; 并使用 shell split with set -- $1)
    • 或使用IFS='|' read a x <<<"$i"(虽然<<<创建一个临时文件)
    • 由于一个读取已被使用,因此替换该读取。
      0.454
  • 减少到只有一个循环(删除 if)并删除末尾的尾随空格
    0.207
  • 使循环更紧密 连接两者$((...))
    0.113
    ---- shell:变化比 35.023 秒快约 300 倍。
    ++++ 这可能是使用 shell 脚本可以完成的最好的事情。
  • 更改为 awk 0.123
    ---- awk:总共更改速度快了约 280 倍

结果脚本:

#!/usr/bin/ksh
while IFS='|' read a b             # read both values split on '|'
do
    x=$b                           # set value of x (quotient)
    grp_rem=""                     # clear value of group
    while (( rem=x%62 , x/=62 ))   # do both math expressions.
    do
        grp_rem="$rem $grp_rem"    # concatenate resulting values
    done
    grp_rem=${grp_rem%?}           # remove one character (an space)
    echo "$a|$b|$rem $grp_rem" 
done  < base62_while.txt  >> base62_while.out

相当于 awk 脚本。我不知道这是否是更快的 awk 脚本,但工作正常。比 shell 快 10k 行以上。 笔记:这是使用带有(任意精度)选项的 GNU awk,-M这是按照您提供的 19 位数字顺序处理数字所必需的。它可以处理更长的数字,我没有检查多长时间,但我很确定限制相当高。 :-) 请注意,awk 必须在包含该选项的情况下进行编译(使用 进行检查awk 'BEGIN{ print( PROCINFO["gmp_version"], PROCINFO["prec_max"]) }'

awk -MF'|' '{ x=$2; grp_rem="";
              while(x>0){
                          rem=x%62;
                          x=int(x/62);
                          grp_rem=rem" "grp_rem
                        }
              printf("%-22s|%s\n",$0,grp_rem)
            }
           ' <base62_while.txt >>base62_while.out

答案4

dc

sed 's/.*|\(.*\)/[&|]P\1p/;1s/^/62o/' base62_while.txt | dc > base62_while.out

或者bc(请注意, 的历史实现bc实际上是 的包装器dc):

sed 's/.*|\(.*\)/"&|";\1/;1s/^/obase=62;/' base62_while.txt | bc > base62_while.out

请注意,dc并对bc长行输出进行换行。使用 GNU 实现,您可以将DC_LINE_LENGTHBC_LINE_LENGTH环境变量设置为 0 以避免这种情况。

$ echo '1|167883826163764944817996215305490271305728' | sed 's/.*|\(.*\)/[&|]P\1p/;1s/^/62o/' | dc
1|167883826163764944817996215305490271305728| 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\
 00
$ echo '1|167883826163764944817996215305490271305728' | sed 's/.*|\(.*\)/[&|]P\1p/;1s/^/62o/' | DC_LINE_LENGTH=0 dc
1|167883826163764944817996215305490271305728| 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

相关内容