é o redirecionamento do linux “” mais lento com arquivos maiores?

2

Eu tenho um script de shell que faz algumas análises e prepara (por exemplo, escreve) alguns comandos para executar em um arquivo separado.

Então, eu tenho algo assim:

echo my_command_to_run >> /tmp/file_command_to_run.txt

Tenho a sensação de que o programa é mais lento e lento. É possível que o programa demore mais quando o arquivo é maior (~ 3M de linhas)?

Eu também estou armazenando algumas coisas na memória, então isso também é provavelmente uma fonte do meu problema, mas eu só quero saber se eu preciso redirecionar a saída em arquivos diferentes. (por exemplo: escrever vários arquivos de 2000 linhas)

EDITAR: Meu script está preparando a movimentação de arquivos de ~ 64M (milhões) para uma arquitetura muito melhor. Então eu passo por todas as diferentes pastas "estruturadas" e preparo o movimento.

Eu tenho essa matriz na memória:

topic1 -> /path/to/my/folder
topic1_number_of_files -> nb

então minha matriz também está ficando maior porque eu tenho várias entradas (max ~ 4'000).

Caso contrário, são sempre os mesmos OPs que são executados. Apenas minha matriz e meu arquivo estão ficando maiores.

EDIT2: Abaixo está o meu script

Nota:

  • Eu tenho várias pastas contendo no máximo 100.000 arquivos.
  • eu posso ter: folder1 - > (source1__description1, source1__description2, source2__description3)

Objetivo: ter algo assim:

  • source1 / folder1 - > (source1__description1, source1__description2)
  • source2 / folder1 - > (source2__description3, ...)

Atuais:

~ 900'000 linhas inseridas em 14 horas < = > Isso levará cerca de 40 dias para preparar todos os comandos de movimento

#!/bin/bash

argument=$1

if [[ -n "$argument" ]] && [[ -e $argument ]]; then
    html_folder=$argument
    echo "We will move [folder]/files from your parameters: '$html_folder'"
else
    html_folder="/var/files/html_files/"
    echo "NO PARAMETER (or folder does not exist) - We will move [folder]/files from $html_folder"
fi

######################## create the list ########################
filename="/var/files/html_files/list_folder.txt" # list generated with  ls -1 -f (this doesn't take everything in memory)
ls -1 -fp $html_folder |  grep '/$' |  grep 'folder'> "$filename"
#################### END create the list ########################


echo " "


# --------------------------------------------------------------
# -------------- Global variables for moving part --------------
# --------------------------------------------------------------
    # Variables for storing the folder/files tree
    declare -A folder_array         # array of folder '/files/publisher_html/10.3390' => 4 (i.e.: 4 folders for mdpi)
    declare -A folder_files_array   # array of files in last folder '/files/publisher_html/10.3390' => 51 (i.e.: 51 files in the 4th folders for mdpi)
    storageFolder="/files/publisher_html/"
    nb_limit=100000 # max number of file per folder
    file_nb=0
    current_folder=""
# --------------------------------------------------------------
# --------------------------------------------------------------
# --------------------------------------------------------------


# --------------------------------------------------------------
# -------------- Global functions for moving part --------------
# --------------------------------------------------------------

    countNumberOfFilesPerFolder () {
        nb=0
        if [[ -e $1 ]]; then        
            nb=$(ls -1fp $1 |  grep -v '/$' | wc -l )        
        fi
        echo $nb
    }

    createFolderIfNeeded () {
        # $1  # first arg (/path/to/htmlfiles/10.3390)
        tmp_folder=""
        nb_folder=1
        nb_files=0

        if [[ ! -e $1 ]]; then # if folder doesn't exist
            sudo mkdir -p "$1/folder$nb_folder" ; # create the folders if don't exist
        else
            #echo "THE FOLDER $tmp_folder ALREADY EXISTED...BE AWARE!!!"
            if [[ -e ${folder_array[$1]} ]]; then
                nb_folder=${folder_array[$1]} # take the value from memory if available
            else
                nb_folder=$(ls -1f $1 | grep folder | wc -l )
            fi

            if (($nb_folder==0)); then # if no subfolder for the publisher folder
                nb_folder=1
                nb_files=0
                sudo mkdir -p "$1/folder$nb_folder" # simply create the first folder
            else
                # if [[ -e ${folder_files_array[$1]} ]]; then
                if [[ ${folder_files_array[$1]} ]]; then
                    nb_files=${folder_files_array[$1]}  # value from memory
                    #echo "value from MEEEEEM: $1 => $nb_files"
                else
                    nb_files='countNumberOfFilesPerFolder "$1/folder$nb_folder"'
                    #echo "value from COOOOOOUNT: $1 => $nb_files"
                fi

                if (($nb_files >= $nb_limit)); then # create a new folder + reset memory value
                    ((nb_folder++))
                    nb_files=0
                    sudo mkdir -p "$1/folder$nb_folder"
                    #'createFolderIfNeeded "$1/folder$nb_folder"' # NO CORRECT -> will create a subfolder
                fi            
            fi       
        fi

        #((nb_files++))
        folder_files_array[$1]=$nb_files
        folder_array[$1]=$nb_folder

        current_folder="$1/folder$nb_folder" # change the global variable
    }

    extractPrefix() {
        whotest[0]='test' || (echo 'Failure: arrays not supported in this version of
    bash.' && exit 2)
        array=(${1//__/ })
        prefix=${array[0]}
        echo $prefix
    }
# --------------------------------------------------------------
# --------------------------------------------------------------
# --------------------------------------------------------------


toMoveFolder=$html_folder"toMove/"
toMoveFileIndex=1
toMoveCmdNumber=0
maxCmdInFile=2000
if [[ ! -e $toMoveFolder ]]; then # if folder doesn't exist
    sudo mkdir -p $toMoveFolder ; # create the folders
fi

cd $html_folder

while read -r folder # for each folder
do
    if [[ -e $folder ]]; then        
        echo "Will manage folder: $folder"


# ---------------------------------------------------------------------------------------------------
# -------------------------------------- MOVE INDIVIDUAL FILES --------------------------------------
# ---------------------------------------------------------------------------------------------------

    argument=$html_folder$folder
    cpt=0
    #argument=$1

    if [[ -n "$argument" ]] && [[ -e $argument ]]; then
        html_files_folder=$argument
    else
        html_files_folder="/var/files/html_files/html_files/"
    fi

    ######################## create the list ########################

    htmlList="/var/files/html_files/list_html.txt" # list generated with  ls -1 -f (this doesn't take everything in memory)
    ls -1f $html_files_folder > "$htmlList" # no need to exclude the "." and ".." (we exclude from the foreach)
    #################### END create the list ########################

    echo " "


    current_folder=$storageFolder # probably useless

    while read -r line
    do
        name=$line

        if [[ $name != "." ]] && [[ $name != ".." ]]; then # don't take the folder itself
            prefix='extractPrefix $name'
            if [ -n $prefix ]; then
                # change the global $current_folder
                # + create new subfolder if needed
                # + increment nb of files in folder
                createFolderIfNeeded $storageFolder$prefix
                ((cpt++))

                if(( $toMoveCmdNumber >= $maxCmdInFile )); then
                    toMoveCmdNumber=0
                    ((toMoveFileIndex++))
                fi

        echo "sudo mv $html_files_folder$name $current_folder/$name" | sed -r 's/[\(\)]+/\&/g' >> $toMoveFolder"command_"$toMoveFileIndex".txt"
                ((toMoveCmdNumber++))

                ((folder_files_array[$storageFolder$prefix]++))

                if (( $cpt % 50 == 0 ));then
                    echo ""
                    echo "Remind: folder -> $current_folder/"
                    echo "${#folder_array[@]} publishers in memory!"
                fi

                echo "#$cpt - $name (${folder_files_array[$storageFolder$prefix]} files)"
            else
                echo "ERROR -> $name has not been moved as expected"
            fi        
       fi

    # >> $toMoveFolder"file"$toMoveFileIndex".txt" # <== does not take the toMoveFileIndex variation in consideration
    done < "$htmlList" # useful if we use the while

    echo "Folder $html_files_folder has been processed"
    echo " "


# ---------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------------------


    else  # END  if [[ -e $folder ]]; then
        echo " "; echo "ERROR -> folder $folder does NOT exist!"; echo " "
        continue
    fi

done < "$filename" # useful if we use the while

echo "The script to prepare the move of the html files FROM FOLDER in other folders finished!"
echo " "
echo " "
echo "FOLDER ARRAY AT THE END: "
    for i in "${!folder_array[@]}"; do echo "folder  : $i => nb_folder: ${folder_array[$i]} / nb__file in last folder: ${folder_files_array[$i]}"; done

echo " "
echo " "
echo "This is the end of the script"

E as partições:

$df -h
/dev/sdb1         2.0T  370G  1.7T  19% /var/files
X.X.X.X:/files     11T  2.8T  7.2T  28% /files

LAST EDIT:

Após análise posterior, descobri que / var / files / html_files / era um link simbólico para / files / html_files / Portanto, a origem e o destino eram na verdade o mesmo servidor (remoto). Eu coloquei meu script para rodar no servidor "remoto", e parece ser muito mais rápido.

Obrigado pela sua ajuda e comentários interessantes!

    
por Bast 28.12.2015 / 08:19

1 resposta

3

I just want to know if I need to redirect the output in different files. (e.g.: write several files of 2000 lines)

A divisão em um número maior de arquivos não será necessariamente igual à execução mais rápida. Três casos de teste simples ilustram isso. Estes três casos imprimem 3 milhões de linhas cada. Estes são listados na ordem de velocidade de execução, mais rápida para mais lenta.

  1. Um redirecionamento fora do loop

    for i in $(seq $((3000000/2000))); do seq 2000; done > file
    
  2. Anexando ao mesmo arquivo, dentro do loop

    for i in $(seq $((3000000/2000))); do seq 2000 >> file; done
    
  3. Dividindo a saída para vários arquivos

    for i in $(seq $((3000000/2000))); do seq 2000 > file$i; done
    

Os últimos comandos consomem constantemente mais tempo do usuário e do sistema do que os comandos anteriores.

A partir disso, podemos concluir que a divisão em um número maior de arquivos não garante o aumento do desempenho neste caso simples. O oposto é verdadeiro.

Número de operações de E / S

O desempenho não depende apenas do tamanho do arquivo, mas também do número de operações de E / S. Ao anexar ( >> ), mais chamadas de E / S ocorrem para buscar o final do arquivo.

Este primeiro script executa as operações de E / S ( >> ) fora do for loop:

$ cat outloop.sh
#!/bin/sh
>file
for i in $(seq 1 ${1:?})
do
    echo $i
done >> file

Este script, por outro lado, executa as operações de E / S ( >> ) em cada iteração, dentro do for loop:

$ cat inloop.sh
#!/bin/sh
>file
for i in $(seq 1 ${1:?})
do
    echo $i >> file
done

Executar e comparar, veja como a localização do operador >> afeta o desempenho:

$ x=500000; time sh outloop.sh $x; time sh inloop.sh $x; 

real    0m1.227s
user    0m0.389s
sys     0m0.859s

real    0m2.996s
user    0m0.809s
sys     0m2.197s

Colocando o operador de redirecionamento fora o loop dobra o desempenho ao escrever 500000 linhas (no meu sistema).

    
por 28.12.2015 / 10:33