diff mbox series

[2/4] populate: remove file creation loops that take forever

Message ID 167400103070.1915094.18012675472928079868.stgit@magnolia (mailing list archive)
State New, archived
Headers show
Series fstests: filesystem population fixes | expand

Commit Message

Darrick J. Wong Jan. 18, 2023, 12:44 a.m. UTC
From: Darrick J. Wong <djwong@kernel.org>

Replace the file creation loops with a perl script that does everything
we want from a single process.  This reduces the runtime of
_scratch_xfs_populate substantially by avoiding thousands of execve
overhead.  On my system, this reduces the runtime of xfs/349 (with scrub
enabled) from ~140s to ~45s.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
 common/populate |   61 ++++++++++++++++++-----------------------------
 src/popdir.pl   |   72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 96 insertions(+), 37 deletions(-)
 create mode 100755 src/popdir.pl

Comments

Zorro Lang Jan. 18, 2023, 3:09 p.m. UTC | #1
On Tue, Jan 17, 2023 at 04:44:02PM -0800, Darrick J. Wong wrote:
> From: Darrick J. Wong <djwong@kernel.org>
> 
> Replace the file creation loops with a perl script that does everything
> we want from a single process.  This reduces the runtime of
> _scratch_xfs_populate substantially by avoiding thousands of execve
> overhead.  On my system, this reduces the runtime of xfs/349 (with scrub
> enabled) from ~140s to ~45s.
> 
> Signed-off-by: Darrick J. Wong <djwong@kernel.org>
> ---

Looks good to me,

Reviewed-by: Zorro Lang <zlang@redhat.com>

>  common/populate |   61 ++++++++++++++++++-----------------------------
>  src/popdir.pl   |   72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 96 insertions(+), 37 deletions(-)
>  create mode 100755 src/popdir.pl
> 
> 
> diff --git a/common/populate b/common/populate
> index 84f4b8e374..180540aedd 100644
> --- a/common/populate
> +++ b/common/populate
> @@ -11,6 +11,7 @@ _require_populate_commands() {
>  	_require_xfs_io_command "falloc"
>  	_require_xfs_io_command "fpunch"
>  	_require_test_program "punch-alternating"
> +	_require_test_program "popdir.pl"
>  	case "${FSTYP}" in
>  	"xfs")
>  		_require_command "$XFS_DB_PROG" "xfs_db"
> @@ -54,55 +55,50 @@ __populate_fragment_file() {
>  
>  # Create a large directory
>  __populate_create_dir() {
> -	name="$1"
> -	nr="$2"
> -	missing="$3"
> +	local name="$1"
> +	local nr="$2"
> +	local missing="$3"
> +	shift; shift; shift
>  
>  	mkdir -p "${name}"
> -	seq 0 "${nr}" | while read d; do
> -		creat=mkdir
> -		test "$((d % 20))" -eq 0 && creat=touch
> -		$creat "${name}/$(printf "%.08d" "$d")"
> -	done
> +	$here/src/popdir.pl --dir "${name}" --end "${nr}" "$@"
>  
>  	test -z "${missing}" && return
> -	seq 1 2 "${nr}" | while read d; do
> -		rm -rf "${name}/$(printf "%.08d" "$d")"
> -	done
> +	$here/src/popdir.pl --dir "${name}" --start 1 --incr 2 --end "${nr}" --remove "$@"
>  }
>  
>  # Create a large directory and ensure that it's a btree format
>  __populate_xfs_create_btree_dir() {
>  	local name="$1"
>  	local isize="$2"
> -	local missing="$3"
> +	local dblksz="$3"
> +	local missing="$4"
>  	local icore_size="$(_xfs_get_inode_core_bytes $SCRATCH_MNT)"
>  	# We need enough extents to guarantee that the data fork is in
>  	# btree format.  Cycling the mount to use xfs_db is too slow, so
>  	# watch for when the extent count exceeds the space after the
>  	# inode core.
>  	local max_nextents="$(((isize - icore_size) / 16))"
> -	local nr=0
> +	local nr
> +	local incr
> +
> +	# Add about one block's worth of dirents before we check the data fork
> +	# format.
> +	incr=$(( (dblksz / 8) / 100 * 100 ))
>  
>  	mkdir -p "${name}"
> -	while true; do
> -		local creat=mkdir
> -		test "$((nr % 20))" -eq 0 && creat=touch
> -		$creat "${name}/$(printf "%.08d" "$nr")"
> +	for ((nr = 0; ; nr += incr)); do
> +		$here/src/popdir.pl --dir "${name}" --start "${nr}" --end "$((nr + incr - 1))"
> +
>  		# Extent count checks use data blocks only to avoid the removal
>  		# step from removing dabtree index blocks and reducing the
>  		# number of extents below the required threshold.
> -		if [ "$((nr % 40))" -eq 0 ]; then
> -			local nextents="$(xfs_bmap ${name} | grep -v hole | wc -l)"
> -			[ "$((nextents - 1))" -gt $max_nextents ] && break
> -		fi
> -		nr=$((nr+1))
> +		local nextents="$(xfs_bmap ${name} | grep -v hole | wc -l)"
> +		[ "$((nextents - 1))" -gt $max_nextents ] && break
>  	done
>  
>  	test -z "${missing}" && return
> -	seq 1 2 "${nr}" | while read d; do
> -		rm -rf "${name}/$(printf "%.08d" "$d")"
> -	done
> +	$here/src/popdir.pl --dir "${name}" --start 1 --incr 2 --end "${nr}" --remove
>  }
>  
>  # Add a bunch of attrs to a file
> @@ -224,9 +220,7 @@ _scratch_xfs_populate() {
>  
>  	# Fill up the root inode chunk
>  	echo "+ fill root ino chunk"
> -	seq 1 64 | while read f; do
> -		$XFS_IO_PROG -f -c "truncate 0" "${SCRATCH_MNT}/dummy${f}"
> -	done
> +	$here/src/popdir.pl --dir "${SCRATCH_MNT}" --start 1 --end 64 --format "dummy%u" --file-mult 1
>  
>  	# Regular files
>  	# - FMT_EXTENTS
> @@ -261,7 +255,7 @@ _scratch_xfs_populate() {
>  
>  	# - BTREE
>  	echo "+ btree dir"
> -	__populate_xfs_create_btree_dir "${SCRATCH_MNT}/S_IFDIR.FMT_BTREE" "$isize" true
> +	__populate_xfs_create_btree_dir "${SCRATCH_MNT}/S_IFDIR.FMT_BTREE" "$isize" "$dblksz" true
>  
>  	# Symlinks
>  	# - FMT_LOCAL
> @@ -340,14 +334,7 @@ _scratch_xfs_populate() {
>  	local rec_per_btblock=16
>  	local nr="$(( 2 * (blksz / rec_per_btblock) * ino_per_rec ))"
>  	local dir="${SCRATCH_MNT}/INOBT"
> -	mkdir -p "${dir}"
> -	seq 0 "${nr}" | while read f; do
> -		touch "${dir}/${f}"
> -	done
> -
> -	seq 0 2 "${nr}" | while read f; do
> -		rm -f "${dir}/${f}"
> -	done
> +	__populate_create_dir "${dir}" "${nr}" true --file-mult 1
>  
>  	# Reverse-mapping btree
>  	is_rmapbt="$(_xfs_has_feature "$SCRATCH_MNT" rmapbt -v)"
> diff --git a/src/popdir.pl b/src/popdir.pl
> new file mode 100755
> index 0000000000..dc0c046b7d
> --- /dev/null
> +++ b/src/popdir.pl
> @@ -0,0 +1,72 @@
> +#!/usr/bin/perl -w
> +
> +# Copyright (c) 2023 Oracle.  All rights reserved.
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Create a bunch of files and subdirs in a directory.
> +
> +use Getopt::Long;
> +use File::Basename;
> +
> +$progname=$0;
> +GetOptions("start=i" => \$start,
> +	   "end=i" => \$end,
> +	   "file-mult=i" => \$file_mult,
> +	   "incr=i" => \$incr,
> +	   "format=s" => \$format,
> +	   "dir=s" => \$dir,
> +	   "remove!" => \$remove,
> +	   "help!" => \$help,
> +	   "verbose!" => \$verbose);
> +
> +
> +# check/remove output directory, get filesystem info
> +if (defined $help) {
> +  # newline at end of die message suppresses line number
> +  print STDERR <<"EOF";
> +Usage: $progname [options]
> +Options:
> +  --dir             chdir here before starting
> +  --start=num       create names starting with this number (0)
> +  --incr=num        increment file number by this much (1)
> +  --end=num         stop at this file number (100)
> +  --file-mult       create a regular file when file number is a multiple
> +                    of this quantity (20)
> +  --remove          remove instead of creating
> +  --format=str      printf formatting string for file name ("%08d")
> +  --verbose         verbose output
> +  --help            this help screen
> +EOF
> +  exit(1) unless defined $help;
> +  # otherwise...
> +  exit(0);
> +}
> +
> +if (defined $dir) {
> +	chdir($dir) or die("chdir $dir");
> +}
> +$start = 0 if (!defined $start);
> +$end = 100 if (!defined $end);
> +$file_mult = 20 if (!defined $file_mult);
> +$format = "%08d" if (!defined $format);
> +$incr = 1 if (!defined $incr);
> +
> +for ($i = $start; $i <= $end; $i += $incr) {
> +	$fname = sprintf($format, $i);
> +
> +	if ($remove) {
> +		$verbose && print "rm $fname\n";
> +		unlink($fname) or rmdir($fname) or die("unlink $fname");
> +	} elsif ($file_mult == 0 or ($i % $file_mult) == 0) {
> +		# create a file
> +		$verbose && print "touch $fname\n";
> +		open(DONTCARE, ">$fname") or die("touch $fname");
> +		close(DONTCARE);
> +	} else {
> +		# create a subdir
> +		$verbose && print "mkdir $fname\n";
> +		mkdir($fname, 0755) or die("mkdir $fname");
> +	}
> +}
> +
> +exit(0);
>
diff mbox series

Patch

diff --git a/common/populate b/common/populate
index 84f4b8e374..180540aedd 100644
--- a/common/populate
+++ b/common/populate
@@ -11,6 +11,7 @@  _require_populate_commands() {
 	_require_xfs_io_command "falloc"
 	_require_xfs_io_command "fpunch"
 	_require_test_program "punch-alternating"
+	_require_test_program "popdir.pl"
 	case "${FSTYP}" in
 	"xfs")
 		_require_command "$XFS_DB_PROG" "xfs_db"
@@ -54,55 +55,50 @@  __populate_fragment_file() {
 
 # Create a large directory
 __populate_create_dir() {
-	name="$1"
-	nr="$2"
-	missing="$3"
+	local name="$1"
+	local nr="$2"
+	local missing="$3"
+	shift; shift; shift
 
 	mkdir -p "${name}"
-	seq 0 "${nr}" | while read d; do
-		creat=mkdir
-		test "$((d % 20))" -eq 0 && creat=touch
-		$creat "${name}/$(printf "%.08d" "$d")"
-	done
+	$here/src/popdir.pl --dir "${name}" --end "${nr}" "$@"
 
 	test -z "${missing}" && return
-	seq 1 2 "${nr}" | while read d; do
-		rm -rf "${name}/$(printf "%.08d" "$d")"
-	done
+	$here/src/popdir.pl --dir "${name}" --start 1 --incr 2 --end "${nr}" --remove "$@"
 }
 
 # Create a large directory and ensure that it's a btree format
 __populate_xfs_create_btree_dir() {
 	local name="$1"
 	local isize="$2"
-	local missing="$3"
+	local dblksz="$3"
+	local missing="$4"
 	local icore_size="$(_xfs_get_inode_core_bytes $SCRATCH_MNT)"
 	# We need enough extents to guarantee that the data fork is in
 	# btree format.  Cycling the mount to use xfs_db is too slow, so
 	# watch for when the extent count exceeds the space after the
 	# inode core.
 	local max_nextents="$(((isize - icore_size) / 16))"
-	local nr=0
+	local nr
+	local incr
+
+	# Add about one block's worth of dirents before we check the data fork
+	# format.
+	incr=$(( (dblksz / 8) / 100 * 100 ))
 
 	mkdir -p "${name}"
-	while true; do
-		local creat=mkdir
-		test "$((nr % 20))" -eq 0 && creat=touch
-		$creat "${name}/$(printf "%.08d" "$nr")"
+	for ((nr = 0; ; nr += incr)); do
+		$here/src/popdir.pl --dir "${name}" --start "${nr}" --end "$((nr + incr - 1))"
+
 		# Extent count checks use data blocks only to avoid the removal
 		# step from removing dabtree index blocks and reducing the
 		# number of extents below the required threshold.
-		if [ "$((nr % 40))" -eq 0 ]; then
-			local nextents="$(xfs_bmap ${name} | grep -v hole | wc -l)"
-			[ "$((nextents - 1))" -gt $max_nextents ] && break
-		fi
-		nr=$((nr+1))
+		local nextents="$(xfs_bmap ${name} | grep -v hole | wc -l)"
+		[ "$((nextents - 1))" -gt $max_nextents ] && break
 	done
 
 	test -z "${missing}" && return
-	seq 1 2 "${nr}" | while read d; do
-		rm -rf "${name}/$(printf "%.08d" "$d")"
-	done
+	$here/src/popdir.pl --dir "${name}" --start 1 --incr 2 --end "${nr}" --remove
 }
 
 # Add a bunch of attrs to a file
@@ -224,9 +220,7 @@  _scratch_xfs_populate() {
 
 	# Fill up the root inode chunk
 	echo "+ fill root ino chunk"
-	seq 1 64 | while read f; do
-		$XFS_IO_PROG -f -c "truncate 0" "${SCRATCH_MNT}/dummy${f}"
-	done
+	$here/src/popdir.pl --dir "${SCRATCH_MNT}" --start 1 --end 64 --format "dummy%u" --file-mult 1
 
 	# Regular files
 	# - FMT_EXTENTS
@@ -261,7 +255,7 @@  _scratch_xfs_populate() {
 
 	# - BTREE
 	echo "+ btree dir"
-	__populate_xfs_create_btree_dir "${SCRATCH_MNT}/S_IFDIR.FMT_BTREE" "$isize" true
+	__populate_xfs_create_btree_dir "${SCRATCH_MNT}/S_IFDIR.FMT_BTREE" "$isize" "$dblksz" true
 
 	# Symlinks
 	# - FMT_LOCAL
@@ -340,14 +334,7 @@  _scratch_xfs_populate() {
 	local rec_per_btblock=16
 	local nr="$(( 2 * (blksz / rec_per_btblock) * ino_per_rec ))"
 	local dir="${SCRATCH_MNT}/INOBT"
-	mkdir -p "${dir}"
-	seq 0 "${nr}" | while read f; do
-		touch "${dir}/${f}"
-	done
-
-	seq 0 2 "${nr}" | while read f; do
-		rm -f "${dir}/${f}"
-	done
+	__populate_create_dir "${dir}" "${nr}" true --file-mult 1
 
 	# Reverse-mapping btree
 	is_rmapbt="$(_xfs_has_feature "$SCRATCH_MNT" rmapbt -v)"
diff --git a/src/popdir.pl b/src/popdir.pl
new file mode 100755
index 0000000000..dc0c046b7d
--- /dev/null
+++ b/src/popdir.pl
@@ -0,0 +1,72 @@ 
+#!/usr/bin/perl -w
+
+# Copyright (c) 2023 Oracle.  All rights reserved.
+# SPDX-License-Identifier: GPL-2.0
+#
+# Create a bunch of files and subdirs in a directory.
+
+use Getopt::Long;
+use File::Basename;
+
+$progname=$0;
+GetOptions("start=i" => \$start,
+	   "end=i" => \$end,
+	   "file-mult=i" => \$file_mult,
+	   "incr=i" => \$incr,
+	   "format=s" => \$format,
+	   "dir=s" => \$dir,
+	   "remove!" => \$remove,
+	   "help!" => \$help,
+	   "verbose!" => \$verbose);
+
+
+# check/remove output directory, get filesystem info
+if (defined $help) {
+  # newline at end of die message suppresses line number
+  print STDERR <<"EOF";
+Usage: $progname [options]
+Options:
+  --dir             chdir here before starting
+  --start=num       create names starting with this number (0)
+  --incr=num        increment file number by this much (1)
+  --end=num         stop at this file number (100)
+  --file-mult       create a regular file when file number is a multiple
+                    of this quantity (20)
+  --remove          remove instead of creating
+  --format=str      printf formatting string for file name ("%08d")
+  --verbose         verbose output
+  --help            this help screen
+EOF
+  exit(1) unless defined $help;
+  # otherwise...
+  exit(0);
+}
+
+if (defined $dir) {
+	chdir($dir) or die("chdir $dir");
+}
+$start = 0 if (!defined $start);
+$end = 100 if (!defined $end);
+$file_mult = 20 if (!defined $file_mult);
+$format = "%08d" if (!defined $format);
+$incr = 1 if (!defined $incr);
+
+for ($i = $start; $i <= $end; $i += $incr) {
+	$fname = sprintf($format, $i);
+
+	if ($remove) {
+		$verbose && print "rm $fname\n";
+		unlink($fname) or rmdir($fname) or die("unlink $fname");
+	} elsif ($file_mult == 0 or ($i % $file_mult) == 0) {
+		# create a file
+		$verbose && print "touch $fname\n";
+		open(DONTCARE, ">$fname") or die("touch $fname");
+		close(DONTCARE);
+	} else {
+		# create a subdir
+		$verbose && print "mkdir $fname\n";
+		mkdir($fname, 0755) or die("mkdir $fname");
+	}
+}
+
+exit(0);