Rasgele sayılar içeren 1 GB metin dosyası oluşturmanın en hızlı yolu nedir?


52

Bir bash betiği denedim, ancak basit bir 1 MB dosya oluşturmak çok uzun sürdü. Sanırım cevap /dev/randomya kullanıyor, ya da /dev/urandomburadaki diğer gönderiler sadece bunları kullanarak bir dosyaya her türlü veriyi nasıl ekleyebileceğimi gösteriyor, ama sadece sayı eklemek istiyorum.

Peki, sadece 0 ile 9 arasında sayılar içeren 1 GB boyutunda rastgele bir dosya oluşturmak için kullanabileceğim bir komut var mı?

Düzenleme: Çıktının böyle bir şey olmasını istiyorum

0 1 4 7 ..... 9
8 7 5 8 ..... 8
....
....
8 7 5 3 ..... 3

Aralık 0 - 9 arasında yalnızca 0, 1, 2, 3, 4, 5, 6, 7, 8 ve 9 sayıları anlamına gelir. Ayrıca, ayrılan nsatırlara ve satır başına 100'e kadar boşluk bırakmalarına ihtiyacım var . Bu n umrumda değil, son boyumun 1 GB olmasını istiyorum.

Düzenleme: Ubuntu 16.04 LTS kullanıyorum



21
Muhtemelen "rastgele" derken ne demek istediğinizi söylemelisiniz - kriptografik kuvvet rastgele mi yoksa sahte rastgele bir sıra yeterli mi?
Toby Speight

4
@posixKing: Cevabım kesinlikle yanak dili olmasına rağmen - Aslında böyle bir görev için bir C programı yazmayı önermiyorum! - rutin olarak bu tür büyük veri kümeleri oluşturuyorsanız veya sık sık oluşturursanız, yaklaşım size zaman kazandırabilir. (Dizüstü bilgisayarımda, yaklaşık on saniyede 1 GB boşlukla ayrılmış basamaklar oluşturuyor.) Ancak, bu bir kerelikse, bunun için bir C programı yazmayı düşünmeyin bile (programlamadan hoşlanmıyorsanız ve uygulama veya böyle); Kabuk komutları ve yardımcı programları, görevi daha az toplam zaman ve harcanan çaba ile gerçekleştirir.
Nominal Hayvan

7
Bu oldukça hızlı ve RFC 1149.5 uyumlu:yes 4 | tr '\n' ' ' | fold -w 200 | head -c1G
Matthew Crumley

Yanıtlar:


38

Bu, sorunun başlığından dolayı, kısmen yanak dilli bir cevaptır.

İçin baktığınızda "için ... en hızlı şekilde" , cevap hemen hemen her zaman bazı özel bir araçtır. Bu "cevaplar" böyle bir araç gösterir, sadece deneyebilirsiniz.

Bu ciddi bir cevap değil, çünkü yalnızca bir kez veya çok nadiren yaptığınız işler için özel araçlara bakmamalısınız. Görüyorsunuz, gerçekte bir şeyler yapmak yerine, araçları aramak ve onlar hakkında bilgi edinmek için daha fazla zaman harcamak zorunda kalacaksınız. En hızlı olan bashve awkolmayan kabuklar ve yardımcı programlar , ancak işi gerçekleştirmek için genellikle tek bir yazı yazabilir , yalnızca birkaç saniye harcayabilirsiniz. Bunun gibi daha iyi betik dilleri perlde kullanılabilir, bununla birlikte öğrenme eğrisi perldiktir ve bu tür amaçlar için tavsiye etmekte tereddüt ederim, çünkü korkunç perl projeleri yüzünden travma geçirdim. pythonÖte yandan, oldukça yavaş G / Ç tarafından hafifçe engellendi; Ancak, yalnızca gigabayt veri filtrelemeniz veya oluşturduğunuzda bu bir sorundur.

Her durumda, aşağıdaki C89 örnek programı (yalnızca varsa daha yüksek doğrulukta saat için POSIX.1 kullanan) yaklaşık 100 MB / s üretim hızına ulaşmalıdır (Intel i5-4200U işlemcili bir dizüstü bilgisayarda Linux'ta test edilmiş ve çıkışını tamamlamalı) için /dev/null), oldukça iyi bir sözde rasgele sayı üreteci kullanarak. (Kod, xorshift64 * kodunu kullandığından, MatrixRank testi hariç tüm BigCrunch testlerini geçmelidir, çünkü rakamları bastırmamak için dışlama yöntemi kullanılır.)

ondalık-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;
}

Bir satır arabelleğine fwrite()geçersek her seferinde her basamağı basmak yerine bir kez daha hızlı hale getirebiliriz . Çıktı bir blok cihaz ise kısmi (ikisinin gücü olmayan) yazmayı önlemek için akışı tamamen tamponlanmış halde tuttuğumuzu unutmayın.

#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;
}

Not: Rakamların eşit dağılımını sağlamak için 2016-11-18 tarihinde düzenlenen her iki örnek (sıfır hariçtir; örneğin karşılaştırma yapmak ve çeşitli sözde rasgele sayı üreteçleriyle ilgili ayrıntılar için buraya bakınız ).

Örneğin kullanarak derleyin

gcc -Wall -O2 decimal-digits.c -o decimal-digits

ve isteğe bağlı olarak /usr/binkullanmak için sistem genelinde yükleyin

sudo install -o root -g root -m 0755 decimal-digits /usr/bin

Satır başına rakam ve satır sayısını alır. Çünkü 1000000000 / 100 / 2 = 5000000(beş milyon; toplam bayt 2'ye bölünen sütunlara bölünmüş), kullanabilirsiniz

./decimal-digits 100 5000000 > digits.txt

digits.txtOP tarafından istenen gigabayt büyüklüğünde üretmek .

Programın kendisinin verimden ziyade okunabilirlikle yazıldığını unutmayın. Buradaki amacım kodun verimliliğini göstermek değil - genel C arayüzleri yerine yine de POSIX.1 ve düşük seviye G / Ç kullanırım - ama harcanan çabayla ne tür bir denge olduğunu kolayca görmenizi sağlamak için tek gömleklere veya kısa kabuk veya awk scriptlet'lere kıyasla performanslarına yönelik özel araçlar geliştirirken.

GNU C kütüphanesini kullanarak, fputc()her karakter çıktısının işlevini çağırmak çok küçük bir ek yüke neden olur (dolaylı bir işlev çağrısı veya koşullamalar - FILEarayüz aslında oldukça karmaşık ve çok yönlüdür). Bu belirli Intel Core i5-4200U dizüstü bilgisayarda, çıkışı /dev/nullilk kez yönlendiren (fputc) sürüm yaklaşık 11 saniye sürüyor, oysa bir satırdaki sürüm yalnızca 1.3 saniye sürüyor.

Sık sık bu tür programları ve jeneratörleri yazmamın nedeni yalnızca büyük veri kümeleriyle oynamayı sevmemdir. Bu şekilde tuhafım. Örneğin, bir kez sonlu pozitif IEEE-754 kayan nokta değerlerini, bir metin dosyasına yazdırmak için bir program yazdım, ayrıştırıldığında aynı değeri elde etmek için yeterli hassasiyetle. Dosya boyutu birkaç gigabayt'tı (belki 4G ya da öylesine); İnsanların floatdüşündüğü kadar sonlu pozitifler yok . Bunu, bu verileri okuyan ve ayrıştıran uygulamaları karşılaştırmak için kullandım.

OP'nin sahip olduğu gibi normal kullanım durumlarında, kabuk komut dosyaları ve komut dosyaları ve tek gömlekler daha iyi bir yaklaşımdır. Genel görevi başarmak için daha az zaman harcandı. (Her gün ya da öylesine farklı bir dosyaya ihtiyaçları varsa veya başka bir dosyaya ihtiyaç duyan birçok insan varsa, ki bu - nadiren - yukarıdaki gibi özel bir araç harcanan çabayı garanti eder.)


Evet, muhtemelen mmap()en iyi G / Ç hızına giden en kolay yol - ancak herhangi bir talepte bulunmadan önce kıyaslama!
Toby Speight

@TobySpeight: Linux'ta, düşük seviyeli I / O, yani kullanmak write(), genellikle daha hızlıdır mmap(). fwrite()çok yavaş değil. Evet, bunu (sadece bu özel örnek için değil) kıyasladım; write()büyük parçalarda (262144, 524288 veya 1048576 byte) diğer metotlardan daha iyi performans gösterme eğilimindedir. fputc()GNU C kütüphanesinde uygulanmakta olan versiyonu ( aynı zamanda kapsamlı bir şekilde kıyaslamıştım), birkaç nedenden dolayı yavaştır; bilhassa, eklenen her karakter için koşullu sıçramalar veya dolaylı çağrılar yapmak zorunda olan uygulama; Ortaya çıkan hafif havai sık sık ekler.
Nominal Hayvan

Sadece ilgisiz - diğer cevaplarla performans karşılaştırması yaptınız mı?
Dijital Travma

2
@DigitalTrauma: Çıktısını yönlendirerek sadece sizin için koştum /dev/null. Stéphane Chazelas'ın senaryosu yaklaşık 52 saniye sürer; perl snippet'i ( headfiltreleme dahil ) yaklaşık 58 saniye; senin shufpasajı; yaklaşık 69 saniye sürer (doğru zamanlama ile sadece alışkanlık artık almak macunu varsayarak shuf zamanı ölçmek). James Hollis ' C ++ 11 her seferinde bir satır programı 14 saniye sürüyor. Yukarıdaki program 10 saniye sürer.
Nominal Hayvan

3
(Yukarıdaki düşünce trenimi kaybettim, üzgünüm.) Önemli olan, doğru algoritmayı - buradaki yeterince rasgele fakat çok hızlı PRNG'yi seçmek - neredeyse bir büyüklük (10 ×) hız artışı emri verdi. (Programlarımın son sürümü kabuk veya perl parçacıklarından yaklaşık 40 kat daha hızlı.) Bu tipik bir durum. Belki de yukarıdaki cevabımda bir program yazarken doğru algoritmayı seçmeyi vurgulamalıydım ? (Öte yandan, bu bir programlama sorusu değil, çok sayıda hanenin nasıl üretileceğine ilişkin bir Unix / Linux sorusudur.)
Nominal Hayvan

81

Bu:

 LC_ALL=C tr '\0-\377' \
             '[0*25][1*25][2*25][3*25][4*25][5*25][6*25][7*25][8*25][9*25][x*]' \
    < /dev/urandom |
    tr -d x |
    fold -w 1 |
    paste -sd "$(printf '%99s\\n')" - |
    head -c1G

( headdestekleyen bir uygulama varsayarak -c) sistemimde oldukça hızlı görünüyor.

trTüm bayt aralığını çevirir (sekizde 0 ila 255, 0 ila 0377): 25 ilk 0 bayt, sonraki 25 1 ... 10 259 geri kalan (250 ila 255) "x" e tr -d xTekdüze bir dağıtım istediğimizden (birlikte tekdüze bir dağılıma /dev/urandomsahip olduğunu varsayarsak) atın (ve bununla birlikte ), bazı rakamlara bir önyargı vermeyin.

Bu baytların% 97'si için bir rakam üretiyor /dev/urandom. fold -w 1satır başına bir basamak yapar. paste -s99 satır karakterinden ve bir yeni satır karakterinden oluşan bir ayırıcı listesiyle çağrılır, böylece her satırda 100 boşluk ayıran rakam bulunur.

head -c1GBunun ilk GiB (2 30 ) alacak . Son satırın kesileceğini ve kullanılmayacağını unutmayın. 2 30 -1 değerine kadar kısabilir ve eksik yeni satırı elle ekleyebilir veya bunun yerine 200 baytlık satırın 50 milyonu olan 10 9 baytı kesebilirsiniz ( head -n 50000000ayrıca standart / taşınabilir bir komut da olabilir).

Bu zamanlamalar ( zshdört çekirdekli bir sistemde elde edilir ), CPU zamanının nerede harcandığını gösterir:

LC_ALL=C tr '\0-\377'  < /dev/urandom  0.61s user 31.28s system 99% cpu 31.904 total
tr -d x  1.00s user 0.27s system 3% cpu 31.903 total
fold -w 1  14.93s user 0.48s system 48% cpu 31.902 total
paste -sd "$(printf '%99s\\n')" -  7.23s user 0.08s system 22% cpu 31.899 total
head -c1G > /dev/null  0.49s user 1.21s system 5% cpu 31.898 total

İlki trşişe boynudur, çoğu zaman çekirdek içinde harcanan zamandır (Sanırım rasgele sayı üretimi için). Zamanlama kabaca bayt /dev/uramdomalabildiğim hıza paraleldir (yaklaşık 19MiB / sn ve burada 32MiB / sn hızında her 0.97 bayt / dev / urandom için 2 bayt üretiyoruz). foldHer bayttan sonra yeni bir satır karakteri eklemek için makul olmayan miktarda CPU zamanı (15s) harcıyor gibi görünüyor ama bu durum benim durumumda farklı bir CPU üzerinde çalıştığı için toplam süreyi etkilemiyor ( -bseçeneğin eklenmesi çok daha fazla yapıyor) verimli, dd cbs=1 conv=unblockdaha iyi bir alternatif gibi görünüyor).

Sen uzakta yapabilirsiniz head -c1Gve (dosya boyutu sınırlaması ayarlayarak birkaç saniye traş limit filesize 1024mile zshveya ulimit -f "$((1024*1024))"(dahil diğer çoğu kabuklu zsh)) yerine bir kabuktaki.

Her bir bayt için 2 rakam çıkarırsak daha iyi olabilirdi, ancak bunun için farklı bir yaklaşıma ihtiyacımız var. Yukarıdakiler çok etkilidir çünkü tr256 byte'lık bir dizideki her bir byte'a bakar. Bir seferde 2 bayt için bunu yapamaz ve hexdump -e '1/1 "%02u"'bunun gibi şeyleri kullanarak daha karmaşık algoritmalar kullanarak bir baytın metin gösterimini hesaplar, rastgele sayı üretmenin kendisinden daha pahalı olurdu. Yine de, benim durumumda olduğu gibi, zamanını boşa harcayan CPU çekirdeğiniz varsa, yine de birkaç saniye tıraş olabilir:

İle:

< /dev/urandom LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]' |
  tr -d x |
  hexdump -n250000000 -ve '500/1 "%02u" "\n"' |
  fold -w1 |
  paste -sd "$(printf '%99s\\n')" - > /dev/null

Anladım (ancak burada 1.073.741.824'ün aksine 1.000.000.000 bayt olduğunu unutmayın):

LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]' < /dev/urandom  0.32s user 18.83s system 70% cpu 27.001 total
tr -d x  2.17s user 0.09s system 8% cpu 27.000 total
hexdump -n250000000 -ve '500/1 "%02u" "\n"'  26.79s user 0.17s system 99% cpu 27.000 total
fold -w1  14.42s user 0.67s system 55% cpu 27.000 total
paste -sd "$(printf '%99s\\n')" - > /dev/null  8.00s user 0.23s system 30% cpu 26.998 total

Genel olarak daha fazla CPU zamanı, ancak 4 CPU çekirdeğim arasında daha iyi dağılmış olması sayesinde duvar saati azalıyor. Darboğaz şimdi hexdump.

ddSatır tabanlı yerine kullanırsak fold, hexdumpyapılması gereken iş miktarını azaltabilir ve CPU'lar arasındaki iş dengesini iyileştirebiliriz:

< /dev/urandom LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]' |
  tr -d x |
  hexdump -ve '"%02u"' |
  dd bs=50000 count=10000 iflag=fullblock status=none cbs=1 conv=unblock |
  paste -sd "$(printf '%99s\\n')" -

(Burada GNU'yu varsayarak ddonun için iflag=fullblockve status=noneveren):

LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]' < /dev/urandom  0.32s user 15.58s system 99% cpu 15.915 total
tr -d x  1.62s user 0.16s system 11% cpu 15.914 total
hexdump -ve '"%02u"'  10.90s user 0.32s system 70% cpu 15.911 total
dd bs=50000 count=10000 iflag=fullblock status=none cbs=1 conv=unblock  5.44s user 0.19s system 35% cpu 15.909 total
paste -sd "$(printf '%99s\\n')" - > /dev/null  5.50s user 0.30s system 36% cpu 15.905 total

Rasgele sayı nesline geri dönen darboğaz.

Şimdi, @OleTange tarafından belirtildiği gibi, eğer opensslyardımcı programınız varsa, daha hızlı (özellikle AES komutuna sahip işlemcilerde) sözde rastgele bayt üreteci elde etmek için kullanabilirsiniz.

</dev/zero openssl enc -aes-128-ctr -nosalt -pass file:/dev/urandom

sistemimde saniyede 15 kez bayttan fazla yayılıyor /dev/urandom. ( Kullanım durumunuz için geçerliyse , kriptografik olarak güvenli rastgelelik kaynağı açısından nasıl karşılaştırıldığı hakkında yorum yapamam ).

</dev/zero openssl enc -aes-128-ctr -nosalt -pass file:/dev/urandom 2> /dev/null | 
  LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]' |
  tr -d x |
  hexdump -ve '"%02u"' |
  dd bs=50000 count=10000 iflag=fullblock status=none cbs=1 conv=unblock |
  paste -sd "$(printf '%99s\\n')" -

Şimdi verir:

openssl enc -aes-128-ctr -nosalt -pass file:/dev/urandom < /dev/zero 2>   1.13s user 0.16s system 12% cpu 10.174 total
LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]'  0.56s user 0.20s system 7% cpu 10.173 total
tr -d x  2.50s user 0.10s system 25% cpu 10.172 total
hexdump -ve '"%02u"'  9.96s user 0.19s system 99% cpu 10.172 total
dd bs=50000 count=10000 iflag=fullblock status=none cbs=1 conv=unblock  4.38s user 0.20s system 45% cpu 10.171 total
paste -sd "$(printf '%99s\\n')" - > /dev/null

hexdumpdarboğaz olmak için geri .

Hala kalacak CPU'larım olduğu için, bunlardan 3 tanesini hexdumpparalel olarak çalıştırabilirim .

</dev/zero openssl enc -aes-128-ctr -nosalt -pass file:/dev/urandom 2> /dev/null | 
  LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]' |
  tr -d x |
  (hexdump -ve '"%02u"' <&3 & hexdump -ve '"%02u"' <&3 & hexdump -ve '"%02u"') 3<&0 |
  dd bs=50000 count=10000 iflag=fullblock status=none cbs=1 conv=unblock |
  paste -sd "$(printf '%99s\\n')" -

( arka planda çalıştırıldığında bu komutların 'stdin on / dev / null' <&3dışındaki zshkomutları için gereklidir ).

Şimdi 6.2 saniyeye düştü ve CPU'larım neredeyse tamamen kullanıldı.


3
Daha önceki cevabımı sildim ve buna oy verdim. İhtiyaçların bir kısmını anlamadım. Güzel cevap btw.
Marcelo

3
neden her geçişte birden fazla rakam üretmiyorsunuz? Bayt bayt olarak okursanız bile, her seferinde 2 basamak üretmeye devam edebilirsiniz
phuclv

@ LưuVĩnhPhúc, perlzaten önemli ölçüde yavaş olan varyantını kaldırdım . Bu tr | fold | paste yaklaşımıyla bayt başına 2 hane bulamıyorum.
Stéphane Chazelas

Afk ya da bunu kendim denerdim, fakat kullanarak bir seferde 42 bayt 100-102 hane dönüştürmeyi deneyebilirsiniz bc(sonra 0, 1 veya 2 en önemli haneyi bırakın).
Eric Towers


23

Elinizde shufmevcutsa (yeni GNU coreutils yapar) bunu yapabilirsiniz:

time shuf -r -n $((512*1024*1024)) -i 0-9 | paste -sd "$(printf '%99s\\n')" -

Sanal makinemde bu, Stéphane'nin cevabından 3: 4 faktöre göre biraz daha yavaş.


shufbenim şirket PC yok üzerinde -r, fmtyok -gçok
phuclv

2
@ LưuVĩnhPhúc Yep - YMMV. Çekirdek-utils sürüm 8.25 bu var ama 8,4 yok buldum. Hangi sürümü kullanıyorsun?
Dijital Travma


@ StéphaneChazelas zeki paste/ printfhüner - teşekkürler. Cevabınız şimdi görünüşte daha hızlı.
Dijital Travma

17

Eğer çok yüksek kalitede rastlantısallığa ihtiyacınız yoksa ve tek biçime yakın dağılım yeterli ise , özellikle SSE2 veya AVX2 ile x86 gibi verimli SIMD tamsayı vektörleri içeren modern bir CPU'da çok hızlı gidebilirsiniz .

Bu, @ NominalAnimal'in cevabı gibi, çünkü ikimiz de aynı fikre sahiptik , ancak x86 için elle vektörelendik. (Ve daha kötü kalitede rastgele sayılarla, ancak muhtemelen birçok kullanım durumu için hala yeterince iyi.) Bu, 2.5GHz Intel Haswell'deki ~ 13GB / sn ASCII çıkışında @ Nominal kodundan yaklaşık 15 veya 30 kat daha hızlı çalışır AVX2 ile CPU. Bu hala teorik maksimum ana hafıza bant genişliğinden daha az (çift kanallı DDR3-1600 yaklaşık 25.6 GB / sn'dir), ancak / dev / null'a yazarken zamanlama yapıyordum, bu yüzden aslında sadece önbellekte sıcak kalan bir tamponu yeniden yazıyordum. Skylake bu kodu Haswell'den önemli ölçüde daha hızlı çalıştırmalıdır (bu cevabın sonuna bakınız).

Bunu bir diske veya boruya sokmak için gerçekten G / Ç üzerinde bir darboğaz bulunduğunu varsayarsak, hızlı bir uygulama CPU'nuzun rölantiden daha yüksek saatlere sahip olması gerekmediği anlamına gelir. Sonuç üretmek için çok daha az toplam enerji kullanır. (Pil ömrü / ısı / küresel ısınma.)

Bu o kadar hızlı ki muhtemelen diske yazmak istemiyorsunuz. İhtiyaç duyduğunuzda tekrar oluşturun (aynı veriyi tekrar istiyorsanız, aynı tohumdan). Tüm işlemcileri kullanabilen çok iş parçacıklı bir prosese beslemek isteseniz bile, bunu veriyi yönlendirmek için çalıştırmak, L3 önbelleğinde (ve bunu yazılan çekirdekte L2 önbelleğinde) sıcak tutacak ve çok kullanacak az işlemci zamanı. (Ancak, boruların yazıya çok fazla ek yük eklediğine dikkat edin /dev/null. Skylake i7-6700k'de, wc -cya da sadece girişini okuyan + okuyan başka bir programa ya da boruya yazmak/dev/null , yazmaktan 8 kat daha yavaştır ve sadece% 70’ini kullanır. CPU: Ama hala 3.9GHz işlemcide 4.0GB / sn.

Yeniden üretme, hızlı bir PCIe bağlantılı SSD'den bile yeniden okumaktan daha hızlıdır, ancak IDK daha güç verimli ise (vektör-tam sayı çarpanı oldukça meşgul tutulur ve muhtemelen diğer AVX2 ile birlikte güçle açtır) 256b vektör ALU'lar). OTOH, diskten okuma işleminin ne kadar zaman harcadığını bilmiyorum, bu girdiyi işleyen tüm çekirdekleri maksimize eden bir şey. 128k parçalarda yeniden oluşturulacak bir bağlam anahtarının dosya sistemi / pagecache kodunu çalıştırmak ve diskten veri okumak için sayfa tahsis etmekle rekabet edebileceğini tahmin ediyorum. Tabii ki, eğer sayfa önbelleğinde sıcaksa, basitçe not edin. OTOH, biz zaten memcpy kadar hızlı yazıyoruz! (ana hafıza bant genişliğini okuma ve yazma arasında bölmek zorundadır). (Ayrıca şunu da not edin.rep movsb( Andy Glew'in bunu P6'da (Pentium Pro) uygulamasından beri RFO'dan kaçınan mikro kodda optimize edilmiş memcpy ve memset ).


Şimdiye kadar bu sadece bir konsept kanıtı ve yeni hat kullanımı sadece yaklaşık olarak doğru. 2 gücünün arabelleğinin uçlarında yanlış. Daha fazla gelişme zamanı ile. Ayrıca, tam olarak doğru olan yeni satırları eklemek için daha etkili bir yol bulabileceğime inanıyorum (en az bu kadar düşük gider) (yalnızca boşluklar çıkarmakla karşılaştırıldığında). Bunun% 10 ila% 20 gibi bir şey olduğunu düşünüyorum. Ben sadece cilalı bir sürümüne sahip olmakla değil, bu koşuyu ne kadar hızlı yapabileceğimizi bilmekle ilgileniyorum, bu yüzden bu bölümü okuyucu için bir alıştırma olarak bırakacağım.


DDG3-1600MHz RAM ile 2.5GHz max turbo bir Haswell i5, 100GiB üreten zamanlanmış ama küçültülmüş. (Win10'daki cygwin64'te gcc5.4 ile zaman aşımına uğradı -O3 -march=native, -funroll-loopsçünkü bu ödünç alınan dizüstü bilgisayarda iyi zamanlama çalışmaları yapmak için yeterince zor zamanlar geçirdim.

aksi belirtilmedikçe / dev / null yazar.

  • James Hollis'in: (test edilmedi)
  • Nominal'in fwrite versiyonu: ~ 2.21s
  • bu (SSE2): ~ 0.142s (ölçeklenmemiş zamanlar = gerçek = 14.232s, kullanıcı = 13.999s, sys = 0.187s).
  • bu (AVX-128): ~ 0.140s
  • bu (AVX2): ~ 0.073s (ölçeklenmemiş: real = 0m7.291s, kullanıcı = 0m7.125s, sys = 0m0.155s).
  • bu (AVX2) cygwin boru wc -chattına 128kiB tampon boyutuyla: 2.38GHz'de CPU ile 0.32s (maksimum çift çekirdekli turbo). (ölçeklenmemiş zamanlar: gerçek = 32.466s kullanıcı = 11.468s sys = 41.092s, hem de bu hem de wc). Yine de verinin sadece yarısı kopyalandı, çünkü benim aptal programım yazmanın tüm arabellekleri yaptığını varsayıyor, bu durum böyle olmasa da ve cygwin write () bir çağrıya sadece 64k yazıyor.

Yani SSE2 ile bu, @Nominal Animal'in skaler kodundan yaklaşık 15 kat daha hızlıdır. AVX2 ile, yaklaşık 30 kat daha hızlı. Nominal'in sadece write()bunun yerine kullandığı bir versiyonunu denemedim fwrite(), ama muhtemelen büyük tamponlar için stdio çoğunlukla yoldan çıkıyor. Verileri kopyalıyorsa, bu çok yavaşlama anlamına gelir.


Core2Duo E6600 (Merom 2.4GHz, 32kiB özel L1, 4MiB paylaşımlı L2 önbellek), 64-bit Linux 4.2'de (Ubuntu 15.10) DDR2-533MHz'de 1GB veri üretme zamanı . Hala write () için bir 128kiB arabellek boyutu kullanmak, bu boyutu araştıramadı.

aksi belirtilmedikçe / dev / null yazar.

  • (SSE2) bunu yeni satır işleme ve rastgele baytların her bir vektöründen 4 basamak hanesiyle gösterir : 0.183s (18.3'lerde 100GiB yaparken zamanlanmış, ancak 1GiB çalışması için benzer sonuçlar). 1.85 devir başına talimat.
  • (SSE2) bu, borulara wc -c: 0.593s (ölçeklenmemiş: gerçek = 59.266s kullanıcı = 20.148s sys = 1m6.548s, wc'nin CPU zamanı dahil). Aynı sayıda write () sistemi cygwin'de olduğu gibi çağrıda bulunur, ancak Linux bütün 128k'lik bir yazıyı () bir yöneltme ile işlediğinden aslında tüm verileri birleştirir.
  • NominalAnimal'in fwrite()sürümü (gcc5.2 -O3 -march=native), aşağıdakilerle çalışır ./decdig 100 $((1024*1024*1024/200)) > /dev/null: 3.19s +/-% 0.1, döngü başına 1.40 komut. -Funroll-Döngüler belki küçük bir fark yarattı. clang-3.8 -O3 -march=native: 3.42s +/-% 0.1
  • Nominal fwriteborular wc -c: real = 3.980s kullanıcı = 3.176s sys = 2.080s
  • James Hollis'in her seferinde bir hat sürümü ( clang++-3.8 -O3 -march=native): 22885s +/-% 0.07, her bir döngü için 0.84 talimat. (g ++ 5.2, biraz yavaştı: 22.98s). Bir seferde sadece bir satır yazmak muhtemelen ciddi şekilde yaralandı.
  • Stéphane Chazelas's tr < /dev/urandom | ...: gerçek = 41.430s kullanıcı = 26.832s sys = 40.120s. trCPU çekirdeğinin tamamını çoğu zaman kendisine çekiyordu, çekirdeğinde neredeyse tüm zamanını rasgele baytlar üretip bunları bir boruya kopyalayarak geçiriyordu. Bu çift çekirdekli makinedeki diğer çekirdek, boru hattının geri kalanını çalıştırıyordu.
  • time LC_ALL=C head -c512M </dev/urandom >/dev/null: yani sadece boru kullanmadan o kadar rasgele olduğunu okumak: real = 35.018s user = 0.036s sys = 34.940s.
  • Lĩu Vĩnh Phúc'in perl programı (Ubuntu15.10'daki v5.20.2 perl)
    LANG=en_CA.UTF-8: real = 4m32.634s kullanıcısı = 4m3.288s sys = 0m29.364.
    LC_ALL=C LANG=C: real = 4m18.637s kullanıcı = 3m50.324s sys = 0m29.356s. Hala çok yavaş.

  • (SSE2) bunu , yeni satır kullanımı olmadan ve her bir rasgele bayt vektöründen 3 veya 4 basamak hanesi (hemen hemen tamamen aynı hızda: dig3 = v%10adım bu HW'de bile eşitlikle ilgilidir): 0.166s (döngü başına 1.82 talimat) . Bu, temel olarak, mükemmel verimli yeni hat kullanımıyla yaklaşabileceğimiz şeyler için alt sınırdır.

  • (SSE2) Bunun yeni sürümü, yeni satır kullanımı olmadan, ancak uint16_t öğesi başına yalnızca bir rakam elde ederek v%10, 0.222 saniye +/-% 0.4, döngü başına 2.12 komut kullanarak . (Gcc5.2 ile derlenmiştir., Unroll loop'lar -march=native -O3 -funroll-loopsbu donanımdaki bu kod için yardımcı olur. Bunu özellikle büyük programlar için, kör kullanmayın).
  • (SSE2) Bunun eski sürümü, bir dosyaya yazıyor (3 hızlı manyetik sabit sürücünün RAID10f2'sinde, yazma için çok iyi optimize edilmemiş): ~ 4 saniye. Write () bloklarından önce çok daha kirli verilere izin vermek için çekirdek G / Ç arabellek ayarlarını değiştirerek daha hızlı gidebilir. "Sistem" süresi hala ~ 1.0 saniyedir, "kullanıcı" saatinden çok daha yüksektir. Yavaş DDR2-533 RAM içeren bu eski sistemde, çekirdeğin verileri sayfa önbelleğine alması ve XFS işlevlerini çalıştırması, çekirdeğimin sıcak kaldığı bir arabellekte yerinde yeniden yazmasını sağlamak için benim döngümden daha uzun sürüyor önbelleği.

Nasıl yapılır

Hızlı bir PRNG açıkçası esastır. xorshift128 + vektörleştirilebilir, böylece bir SIMD vektörünün öğelerinde paralel olarak iki veya dört 64 bit jeneratörünüz olur. Her adım tam bir rastgele bayt vektörü üretir. ( Intel intrinics ile birlikte 256b AVX2 uygulaması ). Nominal'in xorshift * seçiminden seçtim, çünkü 64-bit vektör tamsayı çarpımı yalnızca SSE2 / AVX2'de genişletilmiş hassasiyetli tekniklerle mümkün .


Rastgele baytlık bir vektör verildiğinde, her 16 bitlik öğeyi birden çok ondalık basamağa bölebiliriz. Her biri ASCII basamak + ASCII alanı olan 16 bitlik öğelerin çoklu vektörlerini üretiyoruz . Bunu doğrudan çıktı tamponumuza saklıyoruz.

Orijinal versiyonum sadece x / 6554bir vektörün her uint16_t elemanından rastgele bir rakam elde etmek için kullanılır . Her zaman 0 ile 9 arasında, dahil. Önyargılı 9, çünkü (2^16 -1 ) / 6554sadece 9.99923. (6554 = tavan ((2 ^ 16-1) / 10), ki bu bölümün daima <10 olmasını sağlar.)

x/6554bir "sihirli" sabit ( sabit nokta karşılıklı ) ve yüksek yarı sonucun sağa kayması ile çarpılarak hesaplanabilir . Bu, sabit bir bölünme için en iyi durumdur; bazı bölenler daha fazla işlem yapar ve imzalı bölüm daha fazla iş gerektirir. x % 10benzer önyargıya sahiptir ve hesaplanması ucuz değildir. (gcc'nin asm çıktısı eşdeğerdir x - 10*(x/10), yani modüler bir çarpma tersini kullanarak bölmenin üstüne ekstra çarpma ve çıkarma.) Ayrıca, xorshift128 + 'ın en düşük ucu, yüksek bitlerden entropi almak için ayırmanın daha iyi olması anlamına gelir ( düşük hızdan entropi almak için modülodan daha hızlı ve kaliteli).

Bununla birlikte, @ Nominal'in digit()işlevi gibi düşük ondalık basamaklara bakarak her uint16_t içindeki entropinin daha fazlasını kullanabiliriz . Maksimum performans için, düşük 3 ondalık basamağı almaya karar verdim ve x/6554bir PMULLW ve PSUBW (ve muhtemelen bazı MOVDQA) 'ı kurtarmaya karar verdim, buna karşılık 4 düşük ondalık basamağı almada daha yüksek kalite seçeneği var. x / 6554, düşük 3 ondalık basamaktan hafifçe etkilenir; bu nedenle, aynı öğeden gelen basamaklar arasında bazı korelasyonlar vardır (ASCII çıkışında vektör genişliğine bağlı olarak 8 veya 16 basamak ayırma).

Bence gcc art arda 10'a bölünen daha uzun bir zincirden ziyade 100'e ve 1000'e bölünüyor, bu yüzden muhtemelen her PRNG çıktısından 4 sonuç üreten döngü taşımayan bağımlılık zincirinin uzunluğunu önemli ölçüde kısaltmıyor. port0 (vektör çarpma ve kayma), modüler çarpma inversiyonları ve xorshift + içindeki kaymalar nedeniyle tıkanıklıktır, bu nedenle bir vektör çarpma işleminin kaydedilmesi kesinlikle yararlıdır.

xorshift + o kadar hızlıdır ki, her 16'dan (yani% 20 verimlilik) yalnızca ~ 3.3 bit rasgelelık kullanmak bile, onu birden fazla ondalık basamağa bölmekten çok daha yavaş değildir. Sadece homojen dağılıma yaklaşıyoruz, çünkü kalite çok kötü olmadığı sürece bu cevap hıza odaklanıyor.

Değişken sayıda öğeyi tutan her türlü koşullu davranış çok daha fazla iş gerektirecektir. (Ama yine de SIMD sol paketleme teknikleri kullanılarak daha verimli bir şekilde yapılabilir . Ancak, bu küçük eleman boyutları için daha az verimli olur; dev karıştırma maskesi arama tabloları uygun değildir ve 32'den küçük olan AVX2 şerit geçişi karıştırması yoktur. 128b PSHUFB sürümü BMI2 PEXT / PDEP ile anında daha büyük öğeler içeren AVX2 için yapabileceğiniz gibi bir maske oluşturabilir , ancak 64 bitlik bir tamsayı yalnızca 8 bayt tutabildiği için zor olabilir. Bu cevabın üzerinde, yüksek element sayıları için işe yarayabilecek bazı kodlar vardır.)


RNG'nin gecikmesi bir tıkanıklıksa, iki jeneratör vektörünü paralel olarak çalıştırarak hangisini kullandığımızı değiştirerek daha da hızlı gidebiliriz. Derleyici, kayıtlardaki her şeyi kontrolsüz bir döngüde kolayca tutabilir ve bu iki bağımlılık zincirinin paralel çalışmasını sağlar.

Şu anki sürümde PRNG'nin çıktısını keserek, PRNG gecikme yerine 0 numaralı çıkış noktasında geriledik, bu yüzden buna gerek yok.


Kod: AVX2 sürümü

Godbolt derleyici explorer hakkında daha fazla yorum ile tam sürüm .

Çok düzenli değil, üzgünüm uyumak zorundayım ve bunu yayınlamak istiyorum.

SSE2 sürümü almak, için s/_mm256/_mm, s/256/128/, s/v16u/v8u/ve değişim vector_size(32)de 4 * 16 4 * 8'den satır artışını değiştirmek 16'ya. (Dediğim gibi, kod dağınık ve iki sürümü derlemek için iyi ayarlanmamış. Başlangıçta bir AVX2 sürümü hazırlamayı planlamıyordum, ancak daha sonra gerçekten erişimime sahip olduğum bir Haswell CPU üzerinde test etmek istedim.)

#include <immintrin.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
//#include <string.h>

// This would work equally fast 128b or 256b at a time (AVX2):
// https://stackoverflow.com/questions/24001930/avx-sse-version-of-xorshift128
struct rngstate256 {
    __m256i state0;
    __m256i state1;
};

static inline __m256i xorshift128plus_avx2(struct rngstate256 *sp)
{
    __m256i s1 = sp->state0;
    const __m256i s0 = sp->state1;
    sp->state0 = s0;
    s1 = _mm256_xor_si256(s1, _mm256_slli_epi64(s1, 23));
    __m256i state1new = _mm256_xor_si256(_mm256_xor_si256(_mm256_xor_si256(s1, s0),
                            _mm256_srli_epi64(s1, 18)),
                      _mm256_srli_epi64(s0, 5));
    sp->state1 = state1new;
    return _mm256_add_epi64(state1new, s0);
}



// GNU C native vectors let us get the compiler to do stuff like %10 each element
typedef unsigned short v16u __attribute__((vector_size(32)));

__m256i* vec_store_digit_and_space(__m256i vec, __m256i *restrict p)
{
    v16u v = (v16u)vec;
    v16u ten = (v16u)_mm256_set1_epi16(10);

    v16u divisor = (v16u)_mm256_set1_epi16(6554);  // ceil((2^16-1) / 10.0)
    v16u div6554 = v / divisor;      // Basically the entropy from the upper two decimal digits: 0..65.
    // Probably some correlation with the modulo-based values, especially dig3, but we do this instead of
    // dig4 for more ILP and fewer instructions total.

    v16u dig1 = v % ten;
    v /= ten;
    v16u dig2 = v % ten;
    v /= ten;
    v16u dig3 = v % ten;
    //  dig4 would overlap much of the randomness that div6554 gets

    const v16u ascii_digitspace = (v16u)_mm256_set1_epi16( (' '<<8) | '0');

    v16u *vecbuf = (v16u*)p;
    vecbuf[0] = div6554 | ascii_digitspace;
    vecbuf[1] = dig1    | ascii_digitspace;
    vecbuf[2] = dig2    | ascii_digitspace;
    vecbuf[3] = dig3    | ascii_digitspace;
    return p + 4;  // always a constant number of full vectors
}


void random_decimal_fill_buffer(char *restrict buf, size_t len, struct rngstate256 *restrict rngstate)
{
    buf = __builtin_assume_aligned(buf, 32);

    // copy to a local so clang can keep state in register, even in the non-inline version
    // restrict works for gcc, but apparently clang still thinks that *buf might alias *rngstate
    struct rngstate256 rng_local = *rngstate;

    __m256i *restrict p = (__m256i*restrict)buf;
    __m256i *restrict endbuf = (__m256i*)(buf+len);
    static unsigned newline_pos = 0;
    do {
        __m256i rvec = xorshift128plus_avx2(&rng_local);
        p = vec_store_digit_and_space(rvec, p);  // stores multiple ASCII vectors from the entropy in rvec

#if 1
        // this is buggy at the end or start of a power-of-2 buffer:
        // usually there's a too-short line, sometimes a too-long line
        const unsigned ncols = 100;
        newline_pos += 4*16;
        if (newline_pos >= ncols) {
            newline_pos -= ncols;
            char *cur_pos = (char*)p;
            *(cur_pos - newline_pos*2 - 1) = '\n';
        }
#endif
        // Turning every 100th space into a newline.
        // 1) With an overlapping 1B store to a location selected by a counter.  A down-counter would be more efficient
        // 2) Or by using a different constant for ascii_digitspace to put a newline in one element

        // lcm(200, 16) is 400 bytes, so unrolling the loop enough to produce two full lines makes a pattern of full vectors repeat
        // lcm(200, 32) is 800 bytes
        // a power-of-2 buffer size doesn't hold a whole number of lines :/
        // I'm pretty sure this can be solved with low overhead, like maybe 10% at worst.
    } while(p <= endbuf-3);

    *rngstate = rng_local;
}



#define BUFFER_SIZE (128 * 1024)
const static size_t bufsz = BUFFER_SIZE;
__attribute__((aligned(64))) static char static_buf[BUFFER_SIZE];

int main(int argc, char *argv[])
{
    // TODO: choose a seed properly.  (Doesn't affect the speed)
    struct rngstate256 xorshift_state = {
      _mm256_set_epi64x(123, 456, 0x123, 0x456),
      _mm256_set_epi64x(789, 101112, 0x789, 0x101112)
    };

    for (int i=0; i < 1024ULL*1024*1024 / bufsz * 100; i++) {
        random_decimal_fill_buffer(static_buf, bufsz, &xorshift_state);
        size_t written = write(1, static_buf, bufsz);
        (void)written;
        //fprintf(stderr, "wrote %#lx of %#lx\n", written, bufsz);
    }

}

Gcc, clang veya ICC ile derleyin (ya da C99'un GNU C lehçesini ve Intel'in özünü anlayan herhangi bir derleyici). GNU C vektör uzantıları, derleyicinin bölüm / modülo için sihirli sayıları modüler çarpma tersini kullanarak üretmesini sağlamak için son derece kullanışlıdır ve zaman zaman __attribute__kullanışlıdır.

Bu taşınabilir bir şekilde yazılabilir, ancak daha fazla kod alacaktır.


Performans notları:

Yeni çizgiler eklemek için üst üste binen mağazanın, nereye yerleştirileceğine karar vermek için önemli bir ek yükü vardır (şube yanlış tahminleri ve Çekirdek2 üzerindeki ön darboğazlar), ancak mağazanın performans üzerinde hiçbir etkisi yoktur. Sadece derleyicinin deposundaki mağaza talimatının yorumlanması (tüm dalların aynı bırakılması), Core2'deki performansı tamamen değişmeden bıraktı ve tekrarlanan işlemler aynı zamanda% 1'den daha az +/- verdi. Bu yüzden ben mağaza tampon / önbellek sadece iyi idare ettiği sonucuna varıyorum.

Yine de, ascii_digitspaceherhangi bir sayaç / dallanmanın ortadan kalkması için yeterince açılırsa, yeni bir çizgiye sahip bir elemanlı bir tür döner pencere kullanmak daha da hızlı olabilir.


/ Dev / null öğesinin yazılması temel olarak no-op'tur, bu nedenle tampon L2 önbelleğinde sıcak kalır (Haswell'de çekirdek başına 256kiB). 128b vektörlerden 256b vektörlere kadar mükemmel hız bekleniyor: Ekstra talimat yok ve her şey (mağazalar dahil) genişliğin iki katı ile oluyor. Yeni satır ekleme dalı, yine de iki kez sık alınır. Maalesef Haswell cygwin kurulumuma o kısımda zaman ayıramadım #ifdef.

2.5GHz * 32B / 13.7GB / s = Haswell'de AVX2 mağazası başına 5.84 döngü. Bu oldukça iyi, ama daha hızlı olabilirdi. Belki cygwin sistem çağrısında düşündüğümden daha fazla yük var. Derleyicinin asm çıktısındakileri yorumlamayı denemedim (bu hiçbir şeyin optimize edilmemesini sağlayacaktı.)

L1 önbellek, saat başına bir 32B deposunu koruyabilir ve L2 çok düşük bant genişliği değildir (yine de daha yüksek gecikme süresi).

Birkaç versiyondan önce IACA'ya baktığımda (yeni hatlar için dallanma olmadan, ancak RNG vektörü başına sadece bir ASCII vektör elde ettim), 4 veya 5 saat başına bir 32B vektör mağazası gibi bir şey öngörüyordu.

Agner Fog'un rehberlerini ve SO x86 etiketi wiki'deki bağlantılarını eklediğim diğer optimizasyon kaynaklarını göz önünde bulundurarak, her RNG sonucundan daha fazla veri çıkarmaktan daha fazla hız almayı umuyordum .)

Muhtemelen , vektör tamsayı çarpımı ve kaymanın Haswell'e kıyasla iki kat daha fazla (p0 / p1) çalıştırılabildiği Skylake'de (sadece p0) çok daha hızlı olacaktır. xorshift ve rakam çıkartma işlemlerinde çok fazla kayma ve çarpma kullanılır. ( Güncelleme: Skylake 3.02 IPC'de çalışıyor, bize 32-byte AVX2 mağazası başına 3.77 devir veriyor , 1GB iterasyon başına /dev/null0.030 s'de, 3.7GHz'de i7-6700k'de Linux 4.15'e yazıyor.


İyi çalışması için 64 bit mod gerektirmez . SSE2 sürümü, derlendiğinde çok hızlıdır -m32, çünkü çok fazla vektör kaydına ihtiyaç duymaz ve tüm 64-bit matematik, genel amaçlı kayıtlara değil, vektörlerde yapılır.

Core2'de 32-bit modunda aslında biraz daha hızlı, çünkü karşılaştır / dal makro-füzyonu sadece 32-bit modunda çalışıyor, bu nedenle sıra dışı çekirdek için daha az sayıda uop var (18.3s (Saatte 1.85 Talimatlar) vs 16.9s (2.0 IPC)). REX öneklerine sahip olmamaktan daha küçük kod boyutu Core2'nin kod çözücülerini de yardımcı olur.

Ayrıca, bazı reg-reg vektör hareketleri yüklerle değiştirilir, çünkü tüm sabitler artık regs vektörlerinde sabitlenmez. L1 önbellekten gelen yük verimi bir darboğaz olmadığından, bu gerçekten yardımcı olur. (örneğin, sabit bir vektör ile çarparak set1(10): movdqa xmm0, xmm10/ pmullw xmm0, xmm1dönüşür movdqa xmm0, [constant]/ ' pmullw xmm0, xmm1.) reg-reg MOVDQA yana ALU portu gerektirir, bu gerçek iş yapılan ile rekabet, ancak MOVDQA yük yalnızca ön uç kod çözme bant genişliği için rekabet eder. (Pek çok talimatın içinde 4 baytlık bir adres olması REX öneklerinin kaydedilmesinden elde edilen kazancı ortadan kaldırır.

ALU MOVDQA cihazlarını kurtarmanın, gerçek kazanımların geldiği yer olması durumunda şaşırmam, çünkü ön uç ortalama 2.0 IPC'ye oldukça iyi uyuyor olmalıydı.

Tüm bu farklılıklar, geri döngü tamponu değilse, her şeyin kodu çözülmüş-uop önbellekten çalışması gereken Haswell'de kaybolur. ALU + branş makro füzyonu, Nehalem'den bu yana her iki modda da çalışır.


6
Sadece "canavar modu" konusuna nasıl gittiğini seviyorum ! :) Daha da önemlisi, eldeki donanımın çok düşük seviyeli bilgisinden yararlanarak, maksimum performansa gerçekten ihtiyaç duymak veya sıkmak istemeniz durumunda ne tür kazanımların mevcut olduğuna dair mükemmel bir örnek. Artı, burada sadece bir iplik kullanıyoruz; en güncel masaüstü ve sunucu Intel / AMD işlemciler (ve hafif tabletlerde ve SBC'lerde bile ARM olanlar) birden fazla çekirdeğe sahiptir, bu nedenle hala gerçek zamanlı olarak dünya çapında daha fazla hızlananlar mevcuttur. Ve nihayet, soruların "en hızlı yol" un ne kadar pratik olduğu, ilgili gayretten dolayı.
Nominal Hayvan

1
@NominalAnimal: Evet, yavaş bir ARM dörtlü veya sekizgen çekirdek bile NEON ile aynı şeyi yaparak ana bellek bant genişliğini kolayca doyurabilir (64 bit tam sayı SIMD eklemesi ve vardiyası varsa, hızlı çift kanallı DDR3'e bağlı olsalar bile) . NEON'un ses çalışması için 16 bitlik öğe boyutu çarpanları olduğunu varsayıyorum. Talimat-programlama, sıralı bir ARM için çok daha fazla iş olacaktır, çünkü döngü taşıma bağımlılık zincirinin (xorshift128 +) her yinelemesi, onu kesmek ve hafızaya almak için birkaç bağımsız bağımlılık zincirini besler ...
Peter Kordonlar

... Sıra dışı çalıştırma bunu kahvaltıda yiyor, çünkü her şey ROB'a sığacak kadar kısa. (HSW IIRC'de 192 uops). (yani, sıra dışı yürütmenin gördüğü talimatların "penceresi" birden fazla yineleme içerir). Böylece CPU 2 veya 3 kez tekrarlanan final mağazasını bitirirken mevcut yinelemenin başlangıcına da başlayabilir. Bu, bağımsız zincirlerin gecikmesini gizler, bu nedenle yalnızca iş hacmi önemlidir. Sıralı bir çekirdekte, bunun için yazılım boru hattına ihtiyaç duyulur ...
Peter Cordes

... İyi bir ARM derleyici, içsel (veya her şey için GNU C yerel vektör sözdizimini, ilk başta yapmam gerektiği gibi) ile yazarsanız, sizin için bir kısmını yapmalıdır. Bunu gerçek anlamda yapma konusunda hiçbir tecrübem yok, bu nedenle döngünüze masaj yapmanız ve belki de kaynak bulmak için kaynakta bazı manuel açma / yazılım boru hattı işlemleri yapmanız gerekebilir. (Üst düzey telefonlarda bulunan bazı sıra dışı ARM çekirdeği vardır, ancak Haswell gibi sıra dışı bir pencereleri yoktur. OTOH, daha düşük tepe verimine sahiptir, bu yüzden daha az olur. daha fazla ILP bulmaktan kazanç sağlamak için).
Peter Cordes,

1
@NominalAnimal: Ayrıca, sorunun aptallığı konusunda anlaştım. Rastgele kalite konusunda herhangi bir kısıtlama olmaksızın "en hızlı" aptalca ... BTRFS ile, diskteki aynı veriler birden çok kez bir dosyanın parçası olabilir ( 4.2'deki EXTENT_SAME bölümüne bakın ). Böylece rastgele bir 4kiB veya 1MB oluşturabilir ve tekrarlayabilirsiniz. Kısa süreli rastgeledir, ancak yine de rastgeledir ve yalnızca meta veri G / Ç'ye mal olur. (Aslında, bloğun yeni bir satırla bitmesi gerekir. Lcm (4096, 4096 * 200) = 4096 * 200 = 819200 = 800kiB, bu yüzden tekrar bloğunuz bunun herhangi bir katı.)
Peter Cordes

14

İşte umarım bir çözümdür anlamak kolaydır:

od -An -x /dev/urandom | tr -dc 0-9 | fold -w100 | awk NF=NF FS= | head -c1G
  • odden onaltılık basamakların düzgün bir akışını oluşturur /dev/random.
  • trHarflerden kurtulur, yalnızca 0-9rakamları tutar
  • fold Satır başına 100 hane olmasını sağlar
  • awk çizgilerin içine boşluk ekler
  • head girişi 1 gigabayt olarak keser

2
Bu hala eşit bir dağılıma sahipken / dev / random byte ile birden fazla rakam üretmenin güzel bir alternatif yoludur, ortalama olarak her 256 byte / dev / urandom için 320 hane üretir (byte <200 modulo dönüştürdüğünüzden daha az) 100 ile ondalık arasında olan bu sayı, her 256 byte için 400 basamak verir.
Stéphane Chazelas

6

Bunun için jotkomutu kullanabilirsiniz :

jot -r 50000000 0 9 | fmt -w 200 > output.txt

1
@DigitalTrauma Benim sürümünde fmtbir hedef genişliği seçeneği yok. Neyse, kesin olacak çünkü tüm rakamlar bir sütunu kapsıyor!
gardenhead

Kayıt için fmtversiyonum fmt (GNU coreutils) 8.25(Ubuntu 16.04)
Digital Trauma

2
yarım gb için doğru sayı: 1024 * 1024 * 1024/2 =536870912
Olivier Dulac

1
@OlivierDulac Hangi gigabayttan bahsettiğinize bağlı. Bazı insanlar teknik olarak yanlış olsalar bile 1 Gb yerine 2 ^ 30 yerine 10 ^ 9 kullanırlar. Artı ben güzel yuvarlak sayıları seviyorum :)
gardenhead

6
Gardenhead, gittikçe daha fazla insan artık IEC standart tanımı olarak Gigabyte == 1e9 ve Gibibyte == 2 ^ 30'a geçme eğilimindedir. Vikipedi'ye bakınız . Gb kendisi oldukça Giga- olacağını unutmayın biraz .
Stéphane Chazelas

6

Bu Stéphane Chazelas'ın yöntemine benziyor, ancak performansı artırmak için bir kerede 64 bit okudum. Dağılım hala tekdüze fakat şimdi daha önce olduğu gibi en iyi durumda sadece 8 yerine her 8 bayt için 19 rakam alıyorsunuz

perl -nle 'BEGIN{$/=\8; $,=" "}
           $n = unpack("Q");
           next if $n >= 10000000000000000000;
           $s = sprintf("%019u", $n);
           push @a, (split //, $s);
           if (@a >= 100) {print (splice @a, 0, 100);}' < /dev/urandom | head -c1G

32-bit platformda, her seferinde 19 yerine 9 rakam okunacak.


Sisteminiz 64 bit tam sayıyı desteklemiyorsa veya perldörtlü destekle derlenmediyse, bu durum istisna olabilir .
cuonglm

@cuonglm evet dediğim gibi perl bu sistemde 64 bit değilse, o zaman program next if $n >= 1000000000; $s = sprintf("%09u", $n);sadece 9 basamak alacak şekilde değiştirilmelidir
phuclv

Yapamazsınız, program $n = unpack("Q")desteklenmiyorsa program çökecektir.
cuonglm

1
@cuonglm değişim için BEGIN{$/=\4; $,=" "} $n = unpack("L");de
phuclv

1
Maalesef, bu, 8 baytlık girişin 19 hanesini , zamanın yalnızca% 54.2'sini alıyor ve geri kalanını hiçbiri girmiyor , giriş baytı başına ortalama 1.29 basamak. Eğer daha çok Stephane gibi kullanıyorsanız <16e18ve 16'ya bölüyorsanız, 1.95 dpB için 18 rakam% 86.7 alırsınız. 32bit ile, <4e9 /42.10 dpB için 9 rakam% 93.1 alır. Ancak 5 bayt (onaltılık (H10) olarak) <1e122.18 dpB için 12 basamak% 90.9 verir ya da altıgen ikiye böler ve her yarıyı <1e6 yapmak 2.29 dpB için 6 basamak% 95.4 verir; bu log_10 (256) = 2.41 sınırına yaklaşır.
dave_thompson_085 19:16

3

Hız gerekiyorsa Nominal Animal ile derlenmiş bir programlama dili kullanmayı kabul ediyorum. Bununla birlikte, kendi RNG kodunuzu C'ye yazmak zorunda değilsiniz. C ++ 11, standart kütüphanesinin bir parçası olarak mükemmel Mersenne Twister'i sunar.

#include <time.h>
#include <random>
#include <iostream>
using namespace std;

int main() {
    mt19937 gen(time(0)); 
    uniform_int_distribution<> dist(0,9);

    for(int j=0; j<5000000; j++){
        for (int i = 0; i < 99; i++) {  
            cout << dist(gen) << " ";
        }  
        cout << dist(gen) << endl;
    }
    return 0;
}

Yukarıdaki kod oldukça basittir ve çıktıyı bir dosyaya aktardığımda yaklaşık bir dakika sürer. 100 basamak için yeterince büyük bir dize oluşturarak ve rakamları hackleyerek çok daha hızlı gidebiliriz. Bu, her basamaktan ziyade her satırı cout olarak çağırmamızı sağlar

#include <time.h>
#include <random>
#include <iostream>
using namespace std;

int main() {
    mt19937 gen(time(0)); 
    uniform_int_distribution<> dist(0,9);

    char line[201];
    for(int i=1; i<199; i++)
        line[i] = ' ';
    line[199] = '\n';
    line[200] = 0;

    for(int j=0; j<5000000; j++){
        for (int i = 0; i < 199; i += 2) {  
            line[i] = dist(gen)+'0';
        }  
        cout << line;
    }
    return 0;
}

Bu kod makinemi yaklaşık altı saniye sürer. Bunun standart çıktı olduğunu unutmayın, bu nedenle bir dosyaya aktarın.

Birkaç tane sorumluluk reddi var. İlk önce, bunu bir Windows PC'de yazıyorum. Kütüphanelerin hepsinin Linux'ta bulunduğunu düşünüyorum, ancak hatalıysam, dikkatinizi çektiğinizden emin olun.

Ayrıca, aslında teknik olarak bir gigabayt olan ancak belki tam olarak istediğinizi değil, yarım milyar boşlukla ayrılmış basamaklar üretir. 5 milyon hat, hat başına 100 hane üretir. Fark önemliyse, satır sayısını artırabilirsiniz. Windows kutumda, dosya 10 ^ 9 bayttan biraz daha büyük görünüyor, bu da fazladan yeni satır karakterleriyle ilgisi olduğunu düşünüyorum.


2
Hey, eleştiri pek adil değil! :) Programımın çoğu komut satırı parametresi ayrıştırma. Ayrıca yorum yapmam, hata kontrolleri yapmam ve sütunların ve satırların çıktısını kodlamamı zorlaştırırsam , kodunuzun boyutunun iki katından daha azını yapabilirim - çok az canlandırıcı . :) Şaka bir yana: Evet, kütüphaneler çoğu Linux dağıtımında mevcuttur. Dizüstü bilgisayarımda, her seferinde satırınız yaklaşık 14 saniye sürüyor, oysaki satırdaki sürümüm sadece 1,3 saniye sürüyor. Fark sadece PRNG'den kaynaklanıyor: Mersenne Twister, Xorshift64'ten * daha yavaş.
Nominal Hayvan

1
Kaçırdığınızı belirtmek istediğim bir pratik şey var, ama umarım olumsuzluk olarak düşünmüyorsunuz, sadece düşünülmesi gereken bir şey var: Cevabımda dediğim gibi, tek seferlik programlar nadiren buna değer yazmak için zaman ayırdılar. Bu nedenle komut satırı ayrıştırma ve yardım kullanım metni eklemek neredeyse her zaman faydalı olacaktır. Çok sayıda bu tür faydalı programlarım var ve her birinin ne yaptığını bulmak için kaynaklarını avlamak yerine, sadece onları çalıştırdım, bana söyleyecekler; ve davranışlarını birden fazla ihtiyaca uyacak şekilde değiştirebilirim. Amortisman geliştirme maliyeti.
Nominal Hayvan

@NominalAnimal bir başka önemli şey, çıktıyı /dev/nullgerçek bir dosyaya yazmaktan çok daha hızlı olacak olan çıktıyı
aktarmanızdır

@ LưuVĩnhPhúc: Pek sayılmaz. Bu lappy Samsung 128GB SSD'ye sahip, ~ 500 MB / s sıralı okuma ve yazma özelliğine sahip. Bir Linux yazılımı-RAID0 konfigürasyonuna dört tane koyun ve bir gigabayttan daha fazlasını elde edersiniz. Bu büyük veri kümelerini oluştururken bir saniye okur ve yazar (Sanırım ~ 1.75 TB / s). Linux sw-RAID0 ile 12 SATA diskle (SSD'ler bile değil dönen plakalar) 1 GB / sn'ye ulaşıldı. (Not: bayt / sn, bit / sn değil.) Elbette, "normal" bir makine için aptalca geliyor, ancak büyük veri kümeleriyle oynayanlar bunu faydalı buluyor - yaptığınız her şeyde zaman ayırıyorsunuz (büyük veri kümeleriyle) bu taraftan.
Nominal Hayvan

1
@NominalAnimal ve Lu'u: Daha da önemlisi, yeterli RAM'iniz varsa, tüm veriler diskte olmadan programdan iyi bir şekilde çıkabilirsiniz. Büyük bir write()sistem çağrısındaki işlerin çoğu, yalnızca çekirdeğin daha fazla arabellek alanı ayırmak yerine çekmeye karar vermesi durumunda engelleme yapan, sayfa önbellekte saklanan bir nottur. Bu program yalnızca disk belleği G / Ç'sinde, bellek yetersiz olduğunda veya sayfa önbelleğini atlamak için O_DIRECT kullandıysa tıkanmalıdır. Eğer write()önbellek boyutundan daha küçük parçalar, umarım sadece bir kez ana belleğe gider verileri ve L2 veya L3 cache sıcak kalır yerinde yeniden yazılmış oluyor tamponu içinde.
Peter Cordes

1

Bu sizin "rastgele" tanımınıza bağlıdır. Kriptografik olarak rasgele demek istiyorsan, iyi bir kütüphane almak ve kurşunu ısırmak zorundasın, çalışmasını bekle.

Sadece rastgele görünen bir şeye ihtiyacınız olursa, işte size kolay bir yol:

  1. Birkaç Gb uzunluğunda bir dosya alın. En sevdiğin film iyi olacak.
  2. Gzip, tekrarlanan kalıpları sıkmak için kolay bir yol
  3. Dosya bir kerede bir naylon (yarım bayt) gidin. Her değer 0 ile 15 arasında olacaktır. 1'den az veya 10'dan daha büyük olanları atın. İlk milyarda kalanların her birinden 1'i çıkarın ve rakam olarak yazın.

Yavaş bir makinede çalıştırmak bir saat sürebilir; yeterince hızlı ve çoğu amaç için yeterince rasgele.


9
/dev/urandomgziphem hız hem de rasgelelikten daha iyi olması muhtemeldir .
Stig Hemmer

Get a file that is several Gb long1GB dosya almak için bir dosyaya ** en az 8Gb` ihtiyacınız olacak
phuclv

1
#!/bin/bash
FILE_CREAT='/tmp/testfile'
MAX_SIZE=$(( 1 * 1024 * 1024 ))
rm -rf ${FILE_CREAT}
while true
do
    STRING=''
    for (( i = 0 ; i < 100 ; i++ ))
    do
        NUM_RAN=$(cat /dev/urandom | tr -dc 0-9 | head -c 1)
        if [ $i -eq 0 ]
        then
            STRING=${NUM_RAN}
        else
            STRING=${STRING}' '${NUM_RAN}
        fi
    done
    echo ${STRING} >> $FILE_CREAT
    FILE_SIZE=$(du -s ${FILE_CREAT} | awk '{print $1}')
    if [ ${FILE_SIZE} -ge ${MAX_SIZE} ]
    then
        break
    fi
done
exit $1

1
Siteye Hoşgeldiniz! Profil sayfamdaki bağlantıları gör. Burada neredeyse evrensel olarak kabuk betiklerinde gördüğüm birçok sorun var, ancak bu onları doğru yapmıyor.
Joker

2
@Wildcard: hiçbir cat file | trzaman sadece yapabilirsiniz tr <file. IIRC, hatta yapabilirsiniz <file tr. Sadece bu kabuk betiğinden bahsediyorsunuz, sanki du | awkboyutu kontrol etmek için her satırdan sonra olduğu gibi tıknaz ve yavaş görünüyordu ve döngünün dışına yönlendirmek yerine her satırı eklemek için dosyayı yeniden açıyorsunuz.
Peter Cordes,

2
@PeterCordes, evet. Metni işlemek için neden bir kabuk halkası kullanılmıyor? özellikle alakalı - bu senaryo Bash'in C gibi bir programlama dili olmadığı fikrine dayanıyor. Ancak, \ @NamNT, umarım bu siteye sadık kalırsınız çünkü çok mantıklı bir zihniniz olduğu açıktır. :)
Wildcard

4
@PeterCordes cat /dev/urandom | busy-cmd, işlemciler arasında rastgele nesli ve meşgul cmd'yi bölebileceği için mantıklı olabileceği ender durumlardan biridir. odÖrneğin tr için fazla değil ama Sam için bir fark yaratıyor .
Stéphane Chazelas

1
@ StéphaneChazelas: Ah doğru !! Evet, read () sistem çağrısı, RNG CPU zamanının harcandığı yerdir.
Peter Cordes,
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.