【发布时间】:2018-11-09 20:23:53
【问题描述】:
我的问题
我很惊讶在这里没有找到类似的问题。也许我需要学习如何更好地搜索。主要是,我想弄清楚我的算法的哪一部分是慢的,以及是否有其他更快的方法来实现我的目标。
我正在尝试生成一个包含 1 亿对随机字母字符串的文件。这些随机字符串可以包含空格。随机词周围有一些真实的、固定的词。我想要的输出的一个例子是
rrn TRY gy q OFTEN
ibh TRY mpdw OFTEN
bnq TRY nbjw OFTEN
tky TRY tedr OFTEN
c r TRY fdv OFTEN
cvs TRY dusr OFTEN
ppd TRY qhrc OFTEN
...
我希望能够在 Linux 脚本语言中执行此操作(我实际上使用的是 Cygwin),因此bash、python、perl 等中的答案将是首选。但是,如果我要使用 C++、Java 或其他方式大幅提高速度,我愿意。
目前,我的脚本通过
运行$ ./create_big_file.sh 100000000
bash 脚本如下。我已经改变了一些东西,以免硬编码对的数量。
我的可执行代码
#!/bin/bash
#
# @file create_big_file.sh
n_lines=$1
filename="big_file_${n_lines}.out"
n_lett_rand_str1=3 # originally had 5, tried 3 for speed
n_lett_rand_str2=4 # originally had 10, tried 4 for speed
between=" TRY "
after=" OFTEN"
while [ $n -lt $n_lines ]; do
n_lett_rand_str1=3
n_lett_rand_str2=4
random_str1=$(cat /dev/urandom | tr -dc 'a-z ' | \
fold -w $n_lett_rand_str1 | head -n 1)
random_str2=$(cat /dev/urandom | tr -dc 'a-z ' | \
fold -w $n_lett_rand_str2 | head -n 1)
echo "${random_str1}${between}${random_str2}${after}" >> "$filename"
n=$(($n+1))
done #endof: while [ $n -lt $n_lines ]
正如预期的那样,我的运行时间大致呈线性扩展,但每次迭代所花费的时间对我来说似乎很长。如果我估计纯线性时间,我计算出它需要 265.0 天。如果我使用完整的t=a*exp[x,b] 拟合,我计算出 1.016 年。如果我只想要一百万行,大约需要 2-3 天。
问题
我认为必须有一种方法来做我想做的事情,但速度更快。有谁知道在更短的时间内创建这样一个文件的方法?如果能在 4 小时内搞定,我愿意满足 100 万行,但我希望能在不到一天的时间内搞定 1 亿行。
附:这不是家庭作业;我正在做一些文本分析。
我的研究
$ time ./create_big_file.sh 10
real 0m2.487s
user 0m1.115s
sys 0m1.950s
$ time ./create_big_file.sh 100
real 0m22.356s
user 0m11.764s
sys 0m16.517s
$ tail -2 big_file_100.out
mri TRY nzeo OFTEN
hev TRY uqdf OFTEN
$ time ./create_big_file.sh 500
real 1m52.143s
user 0m57.347s
sys 1m19.405s
$ time ./create_big_file.sh 1000
real 3m50.129s
user 1m58.697s
sys 2m45.612s
在Wolfram|Alpha 中找到的时间与行数的拟合是
t(x) = 0.11905 x1.02845
我尝试替换
random_str1=$(cat /dev/urandom | tr -dc 'a-z ' | \
fold -w $n_lett_rand_str1 | head -n 1)
与
random_str1=$(echo $RANDOM | tr '[0-9]' '[a-z ]')
并对random_str2 进行了相同的更改。这给了我两个最多 5 个字母的字符串。但是,我在运行时间方面没有显着差异。
比较
我运行脚本时没有随机生成任何字符串。我注释掉了任何与随机的东西有关的东西,然后运行了 while 循环
echo "${between}${after}"
n=$(($n+1))
在里面,我的运行时编号是(格式:{n_lines, n_seconds})
{{10, 0.049},{100, 0.097},{500, 0.157},{1000,0.263}, {10000, 1.996}, {100000, 19.222}}
将增长顺序设为 (source)
t(x) = 0.000243745 x0.979367
这让我很确定是随机因素导致了问题 - 比如每个随机问题 0.2 秒。
编辑 “随机”不必非常随机。我基本上希望它不会以 10-20 行的顺序重复(除了非常罕见)。同样,它是用于文本处理的东西。感谢@zdim 要求澄清。
这可能只会在六个月内运行一次或两次。
【问题讨论】:
-
嗨,当 bash 变慢时,我已经多次将 bash 脚本重写为 Perl。性能大大提高。那我就不评论算法本身了,只评论实现语言。你可以看看这个blog.famzah.net/2016/02/09/…
-
是的,最大的消费者将是“随机的东西”,以及所有这些额外的过程(管道!)。下一个消费者将是 I/O,最后我怀疑 shell 的选择。所以:尝试一些简单的随机性(重要:这需要有多好?)。并使用不同的工具。鉴于您希望我会说脚本语言应该这样做,但当然 C/C++ 应该更好。另外:你能预先计算一个准随机序列吗?这是否需要重复运行,经常......?
-
我只是用 Perl 重写了它,用 3 和 4 创建 100_000_000 行大约需要 3 秒。
-
@zdim :感谢您的帮助。我确实找到了使用准随机序列的解决方案。 (我用
base64替换了cat,写了两个包含100 万个字符串的数组,然后将它们读回。)这行得通,但花了20 多分钟。我似乎记得bash有一个基于底层 RAM 的数组大小限制。 This link 说 1.3 GB 内存导致大约 1800 万的限制。 @simbabque 有一个更快的解决方案,但欢迎您发布您的想法作为完整性的答案。 -
@zdim :我还有一个 n00b 问题。 “管道!”是什么意思?
标签: python bash perl random file-io