これは、質問のタイトルのために、部分的には冗談です。
「…への最速の方法」を探すとき、答えはほとんどの場合、特別なツールです。この「答え」は、実験できるように、そのようなツールの1つを示しています。
これは深刻な答えではありません。なぜなら、一度しかやらない、あるいはめったにしない仕事のための特別なツールを調べるべきではないからです。ご覧のとおり、実際に作業を行うよりも、ツールを探してそれらについて学ぶのに多くの時間を費やすことになります。シェルなどのユーティリティbash
とはawk
最速ではありませんが、あなたは通常、書くことができるワンライナーを秒しか費やして、仕事を達成するために。より良いスクリプト言語perl
も使用できますが、学習曲線perl
は急ですが、そのような目的のためにそれをお勧めすることをためらいます。python
一方、I / Oはかなり遅いため、やや不自由です。ただし、ギガバイトのデータをフィルタリングまたは生成する場合にのみ問題になります。
いずれの場合でも、次のC89サンプルプログラム(利用可能な場合にのみ高精度のクロックにPOSIX.1を使用)は、約100 MB / sの生成レートを達成します(LinuxでIntel i5-4200Uプロセッサを搭載したラップトップでテストし、出力をパイピングto /dev/null
)、かなり良い擬似乱数ジェネレーターを使用します。(コードはxorshift64 *と除外メソッドを使用して数字の偏りを防ぐため、出力はMatrixRankテストを除くすべてのBigCrunchテストに合格する必要があります。)
decimal-digits.c:
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <locale.h>
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
/* This program is licensed under the CC0 license,
https://creativecommons.org/publicdomain/zero/1.0/
In other words, this is dedicated to the public domain.
There are no warranties either, so if something breaks,
you only have yourself to blame.
*/
#if _POSIX_C_SOURCE-199309 >= 0
static uint64_t time_seed(void)
{
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts))
return (uint64_t)time(NULL);
return (uint64_t)ts.tv_sec
^ (((uint64_t)ts.tv_nsec) << 32);
}
#else
static uint64_t time_seed(void)
{
return (uint64_t)time(NULL);
}
#endif
/* Preferred output I/O block size.
* Currently, about 128k blocks yield
* maximum I/O throughput on most devices.
* Note that this is a heuristic value,
* and may be increased in the future.
*/
#ifndef IO_BLOCK_SIZE
#define IO_BLOCK_SIZE 262144
#endif
/* This is the Xorshift* pseudo-random number generator.
* See https://en.wikipedia.org/wiki/Xorshift#xorshift.2A
* for details. This is an incredibly fast generator that
* passes all but the MatrixRank test of the BigCrush
* randomness test suite, with a period of 2^64-1.
* Note that neither xorshift_state, nor the result of
* this function, will ever be zero.
*/
static uint64_t xorshift_state;
static uint64_t xorshift_u64(void)
{
xorshift_state ^= xorshift_state >> 12;
xorshift_state ^= xorshift_state << 25;
xorshift_state ^= xorshift_state >> 27;
return xorshift_state * UINT64_C(2685821657736338717);
}
/* This function returns a number between (inclusive)
* 0 and 999,999,999,999,999,999 using xorshift_u64()
* above, using the exclusion method. Thus, there is
* no bias in the results, and each digit should be
* uniformly distributed in 0-9.
*/
static uint64_t quintillion(void)
{
uint64_t result;
do {
result = xorshift_u64() & UINT64_C(1152921504606846975);
} while (!result || result > UINT64_C(1000000000000000000));
return result - UINT64_C(1);
}
/* This function returns a single uniformly random digit.
*/
static unsigned char digit(void)
{
static uint64_t digits_cache = 0;
static unsigned char digits_cached = 0;
unsigned char retval;
if (!digits_cached) {
digits_cache = quintillion();
digits_cached = 17; /* We steal the first one! */
} else
digits_cached--;
retval = digits_cache % (uint64_t)(10);
digits_cache /= (uint64_t)(10);
return retval;
}
static int parse_ulong(const char *src, unsigned long *to)
{
const char *end = src;
unsigned long value;
if (!src)
return errno = EINVAL;
errno = 0;
value = strtoul(src, (char **)&end, 0);
if (errno)
return errno;
if (end == src)
return errno = EINVAL;
while (*end)
if (isspace(*end))
end++;
else
return errno = EINVAL;
if (to)
*to = value;
return 0;
}
int main(int argc, char *argv[])
{
unsigned long lines, cols, line, col, seed;
/* When parsing the command-line parameters,
* use locale conventions. */
setlocale(LC_ALL, "");
/* Standard output should be fully buffered, if possible.
* This only affects output speed, so we're not too worried
* if this happens to fail. */
(void)setvbuf(stdout, NULL, _IOFBF, (size_t)IO_BLOCK_SIZE);
if (argc < 3 || argc > 4 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s COLS LINES [ SEED ]\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "This program generates random decimal digits\n");
fprintf(stderr, "0 - 9, separated by spaces, COLS per line,\n");
fprintf(stderr, "LINES lines. In total, COLS*LINES*2 bytes\n");
fprintf(stderr, "will be used.\n");
fprintf(stderr, "\n");
fprintf(stderr, "SEED is the optional seed for the Xorshift64*\n");
fprintf(stderr, "pseudo-random number generator used in this program.\n");
fprintf(stderr, "If omitted, current time is used as the seed.\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
if (parse_ulong(argv[1], &cols) || cols < 1UL) {
fprintf(stderr, "%s: Invalid number of digits per line.\n", argv[1]);
return EXIT_FAILURE;
}
if (parse_ulong(argv[2], &lines) || lines < 1UL) {
fprintf(stderr, "%s: Invalid number of lines.\n", argv[2]);
return EXIT_FAILURE;
}
if (argc > 3) {
if (parse_ulong(argv[3], &seed)) {
fprintf(stderr, "%s: Invalid Xorshift64* seed.\n", argv[3]);
return EXIT_FAILURE;
}
} else
seed = time_seed();
/* Since zero seed is invalid, we map it to ~0. */
xorshift_state = seed;
if (!xorshift_state)
xorshift_state = ~(uint64_t)0;
/* Discard first 1000 values to make the initial values unpredictable. */
for (col = 0; col < 1000; col++)
xorshift_u64();
for (line = 0UL; line < lines; line++) {
fputc('0' + digit(), stdout);
for (col = 1UL; col < cols; col++) {
fputc(' ', stdout);
fputc('0' + digit(), stdout);
}
fputc('\n', stdout);
/* Check for write errors. */
if (ferror(stdout))
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
行バッファに切り替えると、fwrite()
各桁を一度に出力する代わりに1回行えば、はるかに高速にできます。出力がブロックデバイスの場合、部分的な(2のべき乗以外の)書き込みを回避するために、ストリームを完全にバッファリングしたままにすることに注意してください。
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <locale.h>
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#if _POSIX_C_SOURCE-199309 >= 0
static uint64_t time_seed(void)
{
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts))
return (uint64_t)time(NULL);
return (uint64_t)ts.tv_sec
^ (((uint64_t)ts.tv_nsec) << 32);
}
#else
static uint64_t time_seed(void)
{
return (uint64_t)time(NULL);
}
#endif
/* Preferred output I/O block size.
* Currently, about 128k blocks yield
* maximum I/O throughput on most devices.
* Note that this is a heuristic value,
* and may be increased in the future.
*/
#ifndef IO_BLOCK_SIZE
#define IO_BLOCK_SIZE 262144
#endif
/* This is the Xorshift* pseudo-random number generator.
* See https://en.wikipedia.org/wiki/Xorshift#xorshift.2A
* for details. This is an incredibly fast generator that
* passes all but the MatrixRank test of the BigCrush
* randomness test suite, with a period of 2^64-1.
* Note that neither xorshift_state, nor the result of
* this function, will ever be zero.
*/
static uint64_t xorshift_state;
static uint64_t xorshift_u64(void)
{
xorshift_state ^= xorshift_state >> 12;
xorshift_state ^= xorshift_state << 25;
xorshift_state ^= xorshift_state >> 27;
return xorshift_state * UINT64_C(2685821657736338717);
}
/* This function returns a number between (inclusive)
* 0 and 999,999,999,999,999,999 using xorshift_u64()
* above, using the exclusion method. Thus, there is
* no bias in the results, and each digit should be
* uniformly distributed in 0-9.
*/
static uint64_t quintillion(void)
{
uint64_t result;
do {
result = xorshift_u64() & UINT64_C(1152921504606846975);
} while (!result || result > UINT64_C(1000000000000000000));
return result - UINT64_C(1);
}
/* This function returns a single uniformly random digit.
*/
static unsigned char digit(void)
{
static uint64_t digits_cache = 0;
static unsigned char digits_cached = 0;
unsigned char retval;
if (!digits_cached) {
digits_cache = quintillion();
digits_cached = 17; /* We steal the first one! */
} else
digits_cached--;
retval = digits_cache % (uint64_t)(10);
digits_cache /= (uint64_t)(10);
return retval;
}
static int parse_ulong(const char *src, unsigned long *to)
{
const char *end = src;
unsigned long value;
if (!src)
return errno = EINVAL;
errno = 0;
value = strtoul(src, (char **)&end, 0);
if (errno)
return errno;
if (end == src)
return errno = EINVAL;
while (*end)
if (isspace(*end))
end++;
else
return errno = EINVAL;
if (to)
*to = value;
return 0;
}
int main(int argc, char *argv[])
{
unsigned long lines, cols, line, col, seed;
char *oneline;
/* When parsing the command-line parameters,
* use locale conventions. */
setlocale(LC_ALL, "");
/* Standard output should be fully buffered, if possible.
* This only affects output speed, so we're not too worried
* if this happens to fail. */
(void)setvbuf(stdout, NULL, _IOFBF, (size_t)IO_BLOCK_SIZE);
if (argc < 3 || argc > 4 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s COLS LINES [ SEED ]\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "This program generates random decimal digits\n");
fprintf(stderr, "0 - 9, separated by spaces, COLS per line,\n");
fprintf(stderr, "LINES lines. In total, COLS*LINES*2 bytes\n");
fprintf(stderr, "will be used.\n");
fprintf(stderr, "\n");
fprintf(stderr, "SEED is the optional seed for the Xorshift64*\n");
fprintf(stderr, "pseudo-random number generator used in this program.\n");
fprintf(stderr, "If omitted, current time is used as the seed.\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
if (parse_ulong(argv[1], &cols) || cols < 1UL) {
fprintf(stderr, "%s: Invalid number of digits per line.\n", argv[1]);
return EXIT_FAILURE;
}
if (parse_ulong(argv[2], &lines) || lines < 1UL) {
fprintf(stderr, "%s: Invalid number of lines.\n", argv[2]);
return EXIT_FAILURE;
}
if (argc > 3) {
if (parse_ulong(argv[3], &seed)) {
fprintf(stderr, "%s: Invalid Xorshift64* seed.\n", argv[3]);
return EXIT_FAILURE;
}
} else
seed = time_seed();
/* Since zero seed is invalid, we map it to ~0. */
xorshift_state = seed;
if (!xorshift_state)
xorshift_state = ~(uint64_t)0;
/* Discard first 1000 values to make the initial values unpredictable. */
for (col = 0; col < 1000; col++)
xorshift_u64();
/* Allocate memory for a full line. */
oneline = malloc((size_t)(2 * cols + 1));
if (!oneline) {
fprintf(stderr, "Not enough memory for %lu column buffer.\n", cols);
return EXIT_FAILURE;
}
/* Set spaces and terminating newline. */
for (col = 0; col < cols; col++)
oneline[2*col + 1] = ' ';
oneline[2*cols-1] = '\n';
/* Not needed, but in case a code modification treats it as a string. */
oneline[2*cols] = '\0';
for (line = 0UL; line < lines; line++) {
for (col = 0UL; col < cols; col++)
oneline[2*col] = digit();
if (fwrite(oneline, 2*cols, 1, stdout) != 1)
return EXIT_FAILURE;
}
/* Check for write errors. */
if (ferror(stdout))
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
注:両方の例を2016-11-18に編集して、数字の均一な分布を確保します(ゼロは除外されます。たとえば、さまざまな擬似乱数ジェネレーターの比較と詳細については、こちらを参照してください)。
たとえばを使用してコンパイル
gcc -Wall -O2 decimal-digits.c -o decimal-digits
オプションで、システム全体を/usr/bin
使用してインストールします
sudo install -o root -g root -m 0755 decimal-digits /usr/bin
行ごとの桁数と行数を取ります。ので1000000000 / 100 / 2 = 5000000
(500万; 2で割った列で割った総バイト数)は、使用することができます
./decimal-digits 100 5000000 > digits.txt
digits.txt
OPが望むギガバイトサイズを生成します。
プログラム自体は、効率よりも読みやすさを重視して書かれていることに注意してください。ここでの私の意図は、コードの効率を示すことではありません—汎用Cインターフェイスではなく、とにかくPOSIX.1と低レベルI / Oを使用します。ワンライナーまたは短いシェルまたはawkスクリプトレットと比較した、専用ツールの開発とパフォーマンスの比較。
GNU Cライブラリを使用して、fputc()
すべての文字出力に対して関数を呼び出すと、非常に小さなオーバーヘッドが発生します(間接関数呼び出し、または条件付き- FILE
実際、インターフェイスはかなり複雑で多用途です)。この特定のIntel Core i5-4200Uラップトップでは、出力をにリダイレクトしますが/dev/null
、最初の(fputc)バージョンは約11秒かかりますが、1 行ごとのバージョンは1.3秒しかかかりません。
このようなプログラムやジェネレータを頻繁に書くのは、巨大なデータセットで遊ぶのが好きだからです。私はそのように奇妙です。たとえば、私はかつて、すべての有限の正のIEEE-754浮動小数点値をテキストファイルに出力するプログラムを作成しました。解析時に正確に同じ値を生成するのに十分な精度です。ファイルのサイズは数ギガバイト(おそらく4G程度)でした。float
考えるほど多くの有限の正の数はありません。これを使用して、そのようなデータを読み取り、解析する実装を比較しました。
OPが持っているような通常の使用例では、シェルスクリプトとスクリプトレット、およびワンライナーがより良いアプローチです。全体的なタスクを完了するために費やす時間が短縮されます。(毎日別のファイルが必要な場合や、別のファイルを必要とする人が多い場合を除きます。まれなケースですが、上記のような専用ツールは、努力が必要な場合があります。)