파일 크기가 커질수록 Linux 리디렉션 ">>"이 느려지나요?

파일 크기가 커질수록 Linux 리디렉션 ">>"이 느려지나요?

일부 분석을 수행하고 별도의 파일에서 실행할 일부 명령을 준비(예: 쓰기)하는 쉘 스크립트가 있습니다.

그래서 나는 다음과 같은 것을 가지고 있습니다 :

echo my_command_to_run >> /tmp/file_command_to_run.txt

프로그램이 점점 느려지는 것 같아요. 파일이 더 크면(~3M 라인) 프로그램이 더 오래 걸릴 수 있습니까?

또한 메모리에 무언가를 저장하고 있으므로 이것이 문제의 원인일 수도 있지만 출력을 다른 파일로 리디렉션해야 하는지 알고 싶습니다. (예: 2000줄 파일 여러 개 작성)


편집: 내 스크립트는 ~64M(수백만)개의 파일을 더 나은 스키마로 이동할 준비를 하고 있습니다. 그래서 저는 다양한 "구조화된" 폴더를 모두 살펴보고 이동할 준비를 했습니다.

메모리에 다음과 같은 배열이 있습니다.

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

따라서 여러 항목(최대 ~4'000)이 있기 때문에 내 배열도 더 커집니다.

그렇지 않은 경우 작업 실행은 항상 동일합니다. 내 배열과 파일만 커지고 있습니다.


EDIT2: 아래는 내 스크립트입니다.

노트:

  • 최대 100,000개의 파일을 포함하는 여러 폴더가 있습니다.
  • 다음을 가질 수 있습니다: 폴더1 -> (source1__description1, source1__description2, source2__description3)

목표: 다음과 같은 것을 가지세요:

  • 소스1/폴더1 -> (소스1__ 설명1, 소스1__ 설명2)
  • 소스2/폴더1 -> (소스2__설명3, ...)

현재 공연:

14시간 만에 약 900,000개의 행이 삽입되었습니다. <=> 모든 이동 명령을 준비하는 데 약 40일이 소요됩니다.

#!/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"

그리고 파티션:

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

마지막 수정:

추가 분석 후 /var/files/html_files/가 /files/html_files/에 대한 심볼릭 링크이므로 소스와 대상이 실제로 동일한(원격) 서버라는 것을 발견했습니다. "원격" 서버에서 스크립트를 실행했는데 훨씬 빠른 것 같습니다.

귀하의 도움과 흥미로운 의견에 감사드립니다!

답변1

출력을 다른 파일로 리디렉션해야 하는지 궁금합니다. (예: 2000줄 파일 여러 개 작성)

더 많은 수의 파일로 분할한다고 해서 반드시 실행 속도가 빨라지는 것은 아닙니다. 세 가지 간단한 테스트 사례가 이 점을 보여줍니다. 이 세 가지 경우는 각각 300만 줄을 인쇄합니다. 실행 속도가 가장 빠른 것부터 가장 느린 것 순으로 나열되어 있습니다.

  1. 루프 외부에서 한 번의 리디렉션

    for i in $(seq $((3000000/2000))); do seq 2000; done > file
    
  2. 루프 내부에서 동일한 파일에 추가

    for i in $(seq $((3000000/2000))); do seq 2000 >> file; done
    
  3. 출력을 여러 파일로 분할

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

이후 명령은 항상 이전 명령보다 더 많은 사용자 및 시스템 시간을 소비합니다.

이것으로부터 우리는 이 간단한 경우에 더 많은 파일로 분할하는 것이 성능 향상을 보장하지 않는다는 결론을 내릴 수 있습니다. 사실은 그 반대입니다.

I/O 피연산자

성능은 파일 크기뿐 아니라 IO 작업 수에 따라 달라집니다. 추가( >>)하면 파일의 끝을 찾기 위해 더 많은 I/O 호출이 발생합니다.

첫 번째 스크립트는 i/o 작업을 수행합니다( >>).외부주기 for:

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

반면에 스크립트는 >>각 반복마다 i/o 작업( )을 수행합니다.~에주기 for:

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

실행하고 비교하여 작업자 위치가 >>성능에 어떤 영향을 미치는지 확인하세요.

$ 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

장소 리디렉션 연산자외부내 시스템에서 500,000개의 행을 쓸 때 루프는 성능을 두 배로 늘립니다.

관련 정보