यादृच्छिक अंकों वाले 1 जीबी टेक्स्ट फ़ाइल उत्पन्न करने का सबसे तेज़ तरीका क्या है?


52

मैंने एक बैश स्क्रिप्ट की कोशिश की, लेकिन एक साधारण 1 एमबी फ़ाइल बनाने में बहुत लंबा समय लगा। मुझे लगता है कि उत्तर का उपयोग करने में निहित है /dev/randomया /dev/urandom, लेकिन यहां अन्य पोस्ट केवल यह दिखाती हैं कि इन चीजों का उपयोग करके किसी फ़ाइल में सभी प्रकार के डेटा को कैसे जोड़ा जाए, लेकिन मैं केवल संख्याओं को जोड़ना चाहता हूं।

तो, क्या कोई कमांड है जिसका उपयोग मैं 0 और 9 के बीच केवल संख्याओं वाले 1 जीबी आकार की एक यादृच्छिक फ़ाइल बनाने के लिए कर सकता हूं?

संपादित करें: मैं चाहता हूं कि आउटपुट कुछ इस तरह का हो

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

सीमा 0 - 9 का अर्थ है केवल संख्याएँ 0, 1, 2, 3, 4, 5, 6, 7, 8 और 9। इसके अलावा, मुझे उन्हें अलग करने की आवश्यकता है और 100 प्रति पंक्ति, nलाइनों की संख्या तक। यह n ऐसी चीज है जिसकी मुझे कोई परवाह नहीं है, मैं चाहता हूं कि मेरा अंतिम आकार 1 जीबी हो।

संपादित करें: मैं Ubuntu 16.04 LTS का उपयोग कर रहा हूं



21
आपको शायद कहना चाहिए कि "यादृच्छिक" से आपका क्या मतलब है - क्रिप्टोग्राफिक ताकत यादृच्छिक, या एक छद्म यादृच्छिक अनुक्रम पर्याप्त है?
टोबे स्पाइट नाइट

4
@posixKing: ध्यान दें कि हालांकि मेरा जवाब निश्चित रूप से जीभ-इन-गाल है - मैं वास्तव में इस तरह के कार्य के लिए सी प्रोग्राम लिखने का सुझाव नहीं दे रहा हूं! -, यदि आप नियमित रूप से ऐसे विशाल डेटासेट उत्पन्न करते हैं, या आप उन्हें अक्सर उत्पन्न करते हैं, तो दृष्टिकोण आपके समय को बचा सकता है। (मेरे लैपटॉप पर, यह लगभग दस सेकंड में 1GB का अंतरिक्ष-पृथक अंक उत्पन्न करता है।) हालांकि, अगर यह एक-बंद है, तो इसके लिए C प्रोग्राम लिखने के बारे में भी न सोचें (जब तक आपको प्रोग्रामिंग पसंद न हो, और इस पर विचार करें। अभ्यास या ऐसे); शेल कमांड और यूटिलिटीज कम कुल समय और प्रयास में कार्य को प्राप्त करते हैं।
नाममात्र पशु

7
यह बहुत तेज़ है और RFC 1149.5 अनुपालन है:yes 4 | tr '\n' ' ' | fold -w 200 | head -c1G
मैथ्यू

जवाबों:


38

यह आंशिक रूप से एक जीभ-इन-गाल उत्तर है, क्योंकि सवाल का शीर्षक है।

जब आप "सबसे तेज़ तरीका ..." खोजते हैं, तो उत्तर लगभग हमेशा कुछ विशेष उपकरण होता है। यह "उत्तर" एक ऐसा उपकरण दिखाता है, बस इसलिए आप प्रयोग कर सकते हैं।

यह एक गंभीर जवाब नहीं है, क्योंकि आपको उन नौकरियों के लिए विशेष साधनों पर ध्यान नहीं देना चाहिए जो आप केवल एक बार करते हैं, या बहुत कम ही। आप देखते हैं, आप वास्तव में सामान करने की तुलना में उपकरणों की तलाश में और उनके बारे में सीखने में अधिक समय व्यतीत करेंगे। गोले और उपयोगिताओं की तरह bashऔर awkसबसे तेज़ नहीं हैं, लेकिन आप आमतौर पर केवल कुछ सेकंड खर्च करते हुए, नौकरी हासिल करने के लिए एक-लाइनर लिख सकते हैं । बेहतर स्क्रिप्टिंग भाषाओं perlका भी उपयोग किया जा सकता है, हालांकि सीखने की अवस्था perlबहुत ही कठिन है, और मैं इस तरह के उद्देश्यों के लिए इसकी सिफारिश करने में संकोच करता हूं, क्योंकि मैं भयानक पर्ल प्रोजेक्ट्स द्वारा आघात कर चुका हूं। pythonदूसरी ओर इसके I / O की धीमी गति से थोड़ी बाधा है; हालाँकि यह केवल एक मुद्दा है जब आप गीगाबाइट डेटा को फ़िल्टर या जनरेट करते हैं, हालाँकि।

किसी भी स्थिति में, निम्न C89 उदाहरण कार्यक्रम (जो उच्च सटीकता की घड़ी के लिए POSIX.1 का उपयोग करता है केवल तभी उपलब्ध है) लगभग 100 एमबी / पीढ़ी दर प्राप्त करना चाहिए (एक लैपटॉप पर लिनक्स में इंटेल i5-4200U प्रोसेसर के साथ आउटपुट को पाइप करना करने के लिए /dev/null), एक बहुत अच्छी छद्म यादृच्छिक संख्या जनरेटर का उपयोग कर। (मैट्रिक्स मैट्रिक्स टेस्ट को छोड़कर, सभी बिग क्रंच टेस्ट पास करने चाहिए, क्योंकि कोड्स को बायपास करने से बचने के लिए कोड xorshift64 * और अपवर्जन विधि का उपयोग करता है ।)

दशमलव-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()यह एक बार में प्रत्येक अंक के आउटपुट के बजाय एक बार होता है , तो हम इसे बहुत तेज़ बना सकते हैं । ध्यान दें कि हम अभी भी स्ट्रीम को पूरी तरह से बफ़र रखते हैं, आंशिक (नॉन-पॉवर-ऑफ़-टू) से बचने के लिए लिखते हैं कि क्या आउटपुट एक ब्लॉक डिवाइस है।

#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(पांच मिलियन; कुल बाइट्स 2 से विभाजित स्तंभों से विभाजित), आप उपयोग कर सकते हैं

./decimal-digits 100 5000000 > digits.txt

digits.txtओपी द्वारा वांछित के रूप में गीगाबाइट आकार उत्पन्न करने के लिए ।

ध्यान दें कि कार्यक्रम को ध्यान में दक्षता की तुलना में पठनीयता के साथ अधिक लिखा गया है। यहां मेरा इरादा कोड की दक्षता का प्रदर्शन नहीं करना है - मैं सामान्य सी इंटरफेस के बजाय POSIX.1 और निम्न-स्तर I / O का उपयोग करूंगा - लेकिन आपको आसानी से यह देखने के लिए कि किस तरह का संतुलन बिताए प्रयास के साथ है। एक-लाइनर या छोटे खोल या awk स्क्रिप्टलेट की तुलना में उनके प्रदर्शन बनाम समर्पित उपकरण विकसित करने में।

जीएनयू सी लाइब्रेरी का उपयोग करते हुए, fputc()प्रत्येक वर्ण आउटपुट के लिए फ़ंक्शन को कॉल करना एक बहुत ही छोटे ओवरहेड (अप्रत्यक्ष फ़ंक्शन कॉल, या सशर्त - FILEइंटरफ़ेस वास्तव में बहुत जटिल और बहुमुखी है, जो आप देखते हैं) में होता है। इस विशेष इंटेल कोर i5-4200U लैपटॉप पर, आउटपुट को पुनर्निर्देशित करते हुए /dev/null, पहले (fputc) संस्करण में लगभग 11 सेकंड लगते हैं, जबकि लाइन-ऑन-ए-टाइम संस्करण में केवल 1.3 सेकंड लगते हैं।

मैं अक्सर ऐसे कार्यक्रम और जनरेटर केवल इसलिए लिखता हूं क्योंकि मुझे विशाल डेटासेट के साथ खेलना पसंद है। मैं इस तरह अजीब हूँ। उदाहरण के लिए, मैंने एक बार टेक्स्ट फ़ाइल में सभी परिमित सकारात्मक IEEE-754 फ्लोटिंग-पॉइंट मानों को प्रिंट करने के लिए एक प्रोग्राम लिखा था, जिसमें पार्स होने पर सटीक समान मान प्राप्त करने के लिए पर्याप्त सटीकता के साथ। फ़ाइल आकार में कुछ गीगाबाइट थी (शायद 4 जी या तो); ऐसा नहीं है कि कई परिमित सकारात्मक floatएस एक के रूप में सोच सकते हैं। मैंने इसका उपयोग ऐसे डेटा को पढ़ने और पार्स करने वाले कार्यान्वयन की तुलना करने के लिए किया।

सामान्य उपयोग के मामलों के लिए, जैसे ओपी हो, शेल स्क्रिप्ट और स्क्रिप्ट और वन-लाइनर्स बेहतर दृष्टिकोण हैं। कुल कार्य को पूरा करने के लिए कम समय। (सिवाय इसके कि अगर उन्हें हर दिन एक अलग फाइल की जरूरत है, या ऐसे कई लोग हैं जिन्हें एक अलग फाइल की जरूरत है, जिसमें - दुर्लभ - मामला, ऊपर जैसा समर्पित उपकरण, खर्च किए गए प्रयास को वारंट कर सकता है।)


हाँ, शायद mmap()सबसे अच्छा I / O गति का सबसे आसान मार्ग है - लेकिन कोई भी दावा करने से पहले बेंचमार्क!
टोबे स्पीट

@TobySpeight: लिनक्स में, निम्न-स्तर I / O, का उपयोग करना write(), आमतौर पर की तुलना में तेज है mmap()fwrite()ज्यादा धीमी नहीं है। हां, मैंने बेंचमार्क किया है कि (इस विशेष उदाहरण के लिए नहीं); write()बड़े विखंडू में (262144, 524288, या 1048576 बाइट्स) अन्य विधियों को समझने में मदद करता है। fputc()जीएनयू सी लाइब्रेरी में लागू किया गया संस्करण (जो मैंने बड़े पैमाने पर बेंचमार्क किया है) कई कारणों से धीमा है; विशेष रूप से, क्रियान्वित करने के लिए या तो सशर्त कूदता है या हर वर्ण के लिए अप्रत्यक्ष कॉल करता है; इतना मामूली ओवरहेड अक्सर जोड़ा जाता है।
नाममात्र पशु

बस ब्याज से बाहर - क्या आपने अन्य उत्तरों के साथ प्रदर्शन की तुलना की है?
डिजिटल ट्रॉमा

2
@ डाइग्लिट ​​ट्रॉमा: मैंने सिर्फ आउटपुट के लिए आपको रीडायरेक्ट करते हुए उन्हें आपके लिए चलाया था /dev/nullस्टीफन चेज़लस की पटकथा में लगभग 52 सेकंड लगते हैं; head58 सेकंड के बारे में पर्ल स्निपेट ( फ़िल्टरिंग सहित ); आपका shufस्निपेट (सही समय के साथ; आप केवल शफ़ टाइम को मापते हैं, यह मानते हुए कि पेस्ट को अधिक समय नहीं लगेगा) लगभग 69 सेकंड लगते हैं। जेम्स हॉलिस ' सी ++ 11 एक लाइन-ऑन-ए-टाइम कार्यक्रम में 14 सेकंड लगते हैं। उपरोक्त कार्यक्रम में 10 सेकंड लगते हैं।
सांकेतिक पशु

3
(ऊपर विचार की मेरी ट्रेन को खो दिया, क्षमा करें।) बिंदु सही एल्गोरिथ्म उठा रहा है - यहां पर्याप्त रूप से यादृच्छिक लेकिन बहुत तेज PRNG -, लगभग एक परिमाण (10 ×) की गति में वृद्धि हुई है। (मेरे कार्यक्रमों का उत्तरार्द्ध संस्करण शेल या पर्ल स्निपेट्स की तुलना में लगभग 40 गुना तेज है।) यह विशिष्ट है। शायद मुझे ऊपर दिए गए मेरे उत्तर में प्रोग्राम लिखते समय सही एल्गोरिदम चुनने पर जोर देना चाहिए था ? (दूसरी ओर, यह एक प्रोग्रामिंग सवाल नहीं है, बल्कि एक यूनिक्स / लिनक्स प्रश्न है, बहुत सारे अंक कैसे उत्पन्न करें।)
नाममात्र पशु

81

इस:

 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

(यह मानते हुए कि एक headकार्यान्वयन जो समर्थन करता है -c) मेरे सिस्टम पर काफी तेजी से प्रतीत होता है।

trसंपूर्ण बाइट रेंज (0 से 255, 0 से 0377 ऑक्टल में) का अनुवाद करता है: 25 पहले बाइट्स के रूप में 0, 25 अगले वाले 1 के रूप में ... फिर 25 9 बाकी (250 से 255) "एक्स" जो हम तब त्याग (साथ tr -d x) के रूप में हम एक समान वितरण चाहते हैं (यह मानते हुए /dev/urandomकि एक समान वितरण है) और इसलिए कुछ अंकों को पूर्वाग्रह न दें।

यह बाइट्स के 97% के लिए एक अंक पैदा करता है /dev/urandomfold -w 1इसे प्रति पंक्ति एक अंक बनाता है। paste -sको विभाजकों की एक सूची के साथ कहा जाता है जिसमें 99 अंतरिक्ष वर्ण और एक नया वर्ण होता है, इसलिए प्रत्येक पंक्ति पर 100 अलग-अलग अंक होते हैं।

head -c1Gका पहला GiB (2 30 ) मिलेगा । ध्यान दें कि अंतिम पंक्ति को छोटा और हटा दिया जाएगा। आप 2 30 -1 से कम कर सकते हैं और हाथ से लापता नई रेखा को जोड़ सकते हैं, या 10 9 बाइट्स को काट सकते हैं, जो उन 200 बाइट लाइनों का 50 मिलियन है ( head -n 50000000यह भी एक मानक / पोर्टेबल कमांड बना देगा)।

ये समय ( zshएक क्वाड-कोर सिस्टम पर प्राप्त ), एक संकेत देते हैं कि सीपीयू समय कहाँ खर्च होता है:

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

पहली trबोतल गर्दन है, कर्नेल में बिताया गया अधिकांश समय (मुझे यादृच्छिक संख्या पीढ़ी के लिए लगता है)। समय मोटे तौर पर उस दर के अनुरूप है जिसे मैं /dev/uramdom19MiB / s से बाइट्स प्राप्त कर सकता हूं और यहां हम 32MBB / s की दर से प्रत्येक 0.97 बाइट / dev / urandom के लिए 2 बाइट्स का उत्पादन करते हैं। foldप्रतीत होता है कि हर बाइट के बाद एक नई पंक्ति वर्ण डालने के लिए सीपीयू समय (15s) की अनुचित राशि खर्च की जा रही है, लेकिन यह समग्र समय को प्रभावित नहीं करता है क्योंकि यह मेरे मामले में एक अलग सीपीयू पर काम करता है ( -bविकल्प को जोड़ने से यह बहुत कम हो जाता है कुशल, dd cbs=1 conv=unblockएक बेहतर विकल्प की तरह लगता है)।

आप के साथ भाग कर सकते हैं head -c1Gऔर फ़ाइल आकार (पर एक सीमा निर्धारित करके कुछ ही सेकंड बंद दाढ़ी limit filesize 1024mके साथ zshया ulimit -f "$((1024*1024))"(सहित अधिकांश अन्य गोले के साथ zsh)) के बजाय एक subshell में।

यदि हम प्रत्येक बाइट के लिए 2 अंक निकालते हैं, तो इसमें सुधार किया जा सकता है, लेकिन हमें इसके लिए एक अलग दृष्टिकोण की आवश्यकता होगी। उपरोक्त बहुत ही कुशल है क्योंकि trकेवल 256 बाइट सरणी में प्रत्येक बाइट को देखता है। यह एक समय में 2 बाइट्स के लिए ऐसा नहीं कर सकता है, और इस तरह की चीजों hexdump -e '1/1 "%02u"'का उपयोग करके बाइट का टेक्स्ट प्रतिनिधित्व अधिक जटिल एल्गोरिदम का उपयोग करके यादृच्छिक संख्या पीढ़ी की तुलना में अधिक महंगा होगा। फिर भी, अगर मेरे मामले की तरह, आपके पास सीपीयू कोर है जिसका समय समाप्त हो गया है, तो यह अभी भी कुछ सेकंड से दाढ़ी बनाने का प्रबंधन कर सकता है:

साथ में:

< /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

मुझे पता है (हालांकि यह १,०००,००० बाइट्स 1,073,741,824 के विपरीत है)

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

कुल मिलाकर अधिक सीपीयू समय, लेकिन मेरे 4 सीपीयू कोर के बीच बेहतर वितरित किया गया है, इसलिए यह कम दीवार-घड़ी का समय ले रहा है। अड़चन अब है hexdump

यदि हम ddलाइन-आधारित के बजाय उपयोग करते हैं fold, तो हम वास्तव में hexdumpसीपीयू के बीच काम के संतुलन को करने और सुधारने की आवश्यकता को कम कर सकते हैं :

< /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')" -

(यहाँ ddइसके लिए GNU मानकर ) iflag=fullblockऔर status=noneजो देता है:

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

रैंडम-नंबर जेनरेशन की अड़चन है।

अब, जैसा कि @OleTange द्वारा बताया गया है, यदि आपके पास opensslउपयोगिता है, तो आप इसका उपयोग तेज (विशेष रूप से प्रोसेसर पर एईएस निर्देश) बाइट्स के छद्म-यादृच्छिक जनरेटर पाने के लिए कर सकते हैं।

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

मेरे सिस्टम पर 15 बार प्रति सेकंड कई बाइट्स के रूप में फैल गया /dev/urandom। ( अगर यह आपके उपयोग के मामले पर लागू होता है, तो यादृच्छिकता के क्रिप्टोग्राफिक रूप से सुरक्षित स्रोत के संदर्भ में इसकी तुलना में मैं टिप्पणी नहीं कर सकता )।

</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')" -

अब देता है:

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

वापस hexdumpअड़चन है।

जैसा कि मेरे पास अभी भी सीपीयू है, मैं उनमें से 3 को hexdumpसमानांतर में चला सकता हूं ।

</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')" -

( पृष्ठभूमि में चलाए जाने पर उस बंद कमांड के स्टीन पर / देव / अशक्त के <&3अलावा अन्य गोले के लिए आवश्यक है zsh)।

अब नीचे 6.2 सेकंड और मेरे CPU लगभग पूरी तरह से उपयोग किया।


3
मैंने अभी अपना पिछला उत्तर हटा दिया है और इसके लिए मतदान किया है। मुझे कुछ आवश्यकताएँ नहीं मिलीं। अच्छा जवाब btw।
मार्सेलो

3
आप प्रत्येक पास के लिए कई अंक क्यों नहीं उत्पन्न करते हैं? यहां तक ​​कि अगर आप बाइट-बाय-बाइट में पढ़ते हैं, तो आप हर बार 2 अंक उत्पन्न करने में सक्षम होते हैं
phuclv

@ LưuV LnhPhúc, मैंने उस perlसंस्करण को हटा दिया है जो वैसे भी काफी धीमा था। मैं उस बाइट के साथ 2 अंक प्रति बाइट नहीं पा सकता हूं।
स्टीफन चेज़लस

मैं afk हूँ या मैं खुद यह कोशिश करूँगा, लेकिन आप एक बार में 42 बाइट्स को 100-102 अंकों में बदलने की कोशिश कर सकते हैं bc(फिर 0, 1, या 2 सबसे महत्वपूर्ण अंकों को छोड़ दें)।
एरिक टावर्स

gitlab.com/ole.tange/tangetools/tree/master/rand प्रति सेकंड (AES गुणवत्ता) में 1-4 GB स्यूडोरेंगल उत्पन्न करता है।
ओले तांगे

23

यदि आपके पास shufउपलब्ध है (हाल ही में GNU कोरुटिल करता है) तो आप यह कर सकते हैं:

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

मेरे वीएम पर, यह अब एक 3: 4 कारक के बारे में स्टीफन के जवाब की तुलना में थोड़ा धीमा है।


shufपर मेरी कंपनी पीसी नहीं है -r, fmtनहीं है -gबहुत
phuclv

2
@ LưuV LnhPhúc येप - YMMV। मैंने पाया है कि कोर-बर्तन संस्करण 8.25 में ये हैं लेकिन 8.4 नहीं है। आप कौन सा संस्करण उपयोग कर रहे हैं?
डिजिटल ट्रॉमा


@ स्टीफनचेलजेलस चालाक paste/ printfचाल - धन्यवाद। आपका जवाब अब स्पष्ट रूप से तेज है।
डिजिटल ट्रॉमा

17

यदि आपको बहुत उच्च गुणवत्ता वाले यादृच्छिकता की आवश्यकता नहीं है, और निकट-से-समान वितरण काफी अच्छा है, तो आप वास्तव में तेजी से जा सकते हैं , विशेष रूप से एक आधुनिक सीपीयू पर SSE2 या AVX2 के साथ x86 जैसे कुशल SIMD पूर्णांक वैक्टर।

यह @ नॉमिनलिमल के उत्तर की तरह है क्योंकि हम दोनों का विचार समान था, लेकिन x86 के लिए मैन्युअल रूप से वेक्टर किया गया था। (और खराब गुणवत्ता वाले रैंडम नंबरों के साथ, लेकिन बहुत सारे उपयोग के मामलों के लिए अभी भी काफी अच्छा है।) यह 2.5GHz इंटेल हैसवेल पर ASCII आउटपुट के ~ 13GB / s पर @ नॉमिनल कोड से लगभग 15 या 30 गुना तेज चलता है। AVX2 के साथ सीपीयू। यह अभी भी सैद्धांतिक अधिकतम मुख्य मेमोरी बैंडविड्थ (दोहरी चैनल DDR3-1600 के बारे में 25.6GB / s) से कम है, लेकिन मैं / dev / null को लिख रहा था इसलिए यह वास्तव में कैश में गर्म रहने वाले बफर को फिर से लिखना है। स्काईलेक को हसवेल की तुलना में इस कोड को काफी तेज चलाना चाहिए (इस उत्तर के निचले भाग को देखें)।

डिस्क / या कहीं पर पाइपिंग करने के लिए आपको वास्तव में आई / ओ पर अड़चन मानते हुए, तेजी से कार्यान्वयन का मतलब है कि आपके सीपीयू को निष्क्रिय से अधिक घड़ी भी नहीं है। यह परिणाम उत्पन्न करने के लिए बहुत कम कुल ऊर्जा का उपयोग करता है। (बैटरी जीवन / गर्मी / ग्लोबल वार्मिंग।)

यह इतना तेज़ है कि आप इसे डिस्क पर लिखना नहीं चाहेंगे। बस जरूरत के अनुसार फिर से उत्पन्न करें (उसी बीज से यदि आप फिर से वही डेटा चाहते हैं)। यहां तक ​​कि अगर आप इसे एक बहु-थ्रेडेड प्रक्रिया में फीड करना चाहते हैं जो सभी सीपीयू का उपयोग कर सकते हैं, तो इसे चलाने के लिए डेटा को पाइप करने के लिए इसे L3 कैश में गर्म छोड़ देगा (और कोर पर L2 कैश जिसने इसे लिखा है), और बहुत उपयोग करें थोड़ा सीपीयू समय। (लेकिन ध्यान दें कि पाइपिंग बहुत सारे ओवरहेड बनाम राइटिंग को जोड़ता है /dev/null। स्काइलेक i7-6700k पर, पाइपिंग wc -cया किसी अन्य प्रोग्राम के लिए पाइपिंग जो सिर्फ अपने इनपुट को पढ़ता है + डिस्क्राइब करता है, यह लिखने की तुलना में लगभग 8x धीमा है/dev/null , और केवल 70% सीपीयू। लेकिन 3.9GHz CPU पर अभी भी 4.0GB / s है।

पुन: उत्पन्न करने से तेज पीसीआई से जुड़े एसएसडी से भी इसे फिर से पढ़ने से तेज है, लेकिन आईडीके अगर यह अधिक शक्ति कुशल है (वेक्टर-पूर्णांक गुणक को बहुत व्यस्त रखा गया है, और यह संभवतः बहुत शक्तिशाली है, अन्य AV2 के साथ-साथ भूख लगी है) 256b वेक्टर ALU)। OTOH, मुझे नहीं पता कि डिस्क से पढ़ने का सीपीयू समय कितना है जो इस इनपुट को संसाधित करने वाले सभी कोर को अधिकतम कर रहा है। मुझे लगता है कि 128k चंक्स में फिर से उत्पन्न करने के लिए एक संदर्भ-स्विच फाइलसिस्टम / पेजकेच कोड चलाने और डिस्क से डेटा पढ़ने के लिए पृष्ठों को आवंटित करने के लिए प्रतिस्पर्धी हो सकता है। बेशक, अगर यह पहले से ही पेजकेस में गर्म है, तो यह मूल रूप से यादगार है। OTOH, हम पहले से ही के रूप में तेजी से memcpy के रूप में लिखते हैं! (जिसे पढ़ने और लिखने के बीच मुख्य मेमोरी बैंडविड्थ को विभाजित करना है)। (यह भी याद रखें कि स्मृति को लिखना है कि 'rep movsb(माइक्रोकोड में अनुकूलित मेम्पी और मेमसेट, जो आरएफओ से बचा जाता है, क्योंकि पी 6 (पेंटियम प्रो) में एंडी ग्लीव के इसे लागू करने के बाद )।


अब तक यह केवल अवधारणा का प्रमाण है, और न्यूलाइन हैंडलिंग केवल लगभग सही है। यह एक शक्ति के -2 बफर के सिरों के आसपास गलत है। अधिक विकास समय के साथ। मुझे विश्वास है कि मुझे नई सुइयों को सम्मिलित करने के लिए एक अधिक कुशल तरीका मिल सकता है जो बिल्कुल सही है, ओवरहेड के साथ कम से कम कम से कम इस के रूप में (केवल रिक्त स्थान को आउटपुट करने की तुलना में)। मुझे लगता है कि यह 10 से 20% की तरह कुछ है। मुझे केवल यह जानने में दिलचस्पी है कि हम कितनी तेजी से यह रन बना सकते हैं, वास्तव में इसका पॉलिश संस्करण नहीं है, इसलिए मैं कुछ विचारों का वर्णन करने वाली टिप्पणियों के साथ पाठक के लिए एक अभ्यास के रूप में उस हिस्से को छोड़ दूंगा।


अपने 2.5GHz मैक्स टर्बो में एक हैडवेल i5 पर, DDR3-1600 मेगाहर्ट्ज रैम के साथ, 100GiB का उत्पादन किया, लेकिन कम हो गया। (Gcc5.4 के साथ Win10 पर cygwin64 पर लिया गया था -O3 -march=native, -funroll-loopsक्योंकि मैं काफी मुश्किल समय इस उधार के लैपटॉप पर अच्छा समय पा रहा था , क्योंकि एक USB पर लिनक्स में बूट होना चाहिए)।

जब तक अन्यथा निर्दिष्ट न हो / लेखन / देव / अशक्त।

  • जेम्स हैलिस: (परीक्षण नहीं)
  • नाममात्र का लेखन संस्करण: ~ २.२१
  • यह (SSE2): ~ 0.142 s ( बिना बिके समय = वास्तविक = 14.232s, उपयोगकर्ता = 13.999s, sys = 0.187s)।
  • यह (AVX-128): ~ 0.140s
  • यह (AVX2): ~ 0.073s ( अनसाल्टेड : वास्तविक = 0m7.291s, उपयोगकर्ता = 0m7.125s, sys = 0m0.155s)।
  • यह ( wc -cAVX2 ) साइबरविन पाइपिंग , 128kiB बफर साइज के साथ: 2.32GHz CPU के साथ 2.38GHz (अधिकतम डुअल-कोर टर्बो)। (अनसुलझा समय: वास्तविक = 32.466s उपयोगकर्ता = 11.468s sys = 41.092s, यह और दोनों सहित wc)। केवल आधा डेटा वास्तव में कॉपी किया गया था, हालांकि, क्योंकि मेरा मूर्खतापूर्ण कार्यक्रम मानता है कि लिखना पूर्ण बफर करता है, भले ही ऐसा नहीं है और साइबरविन लिखते हैं () केवल एक पाइप में 64k प्रति कॉल करता है।

तो SSE2 के साथ यह @Nominal Animal के स्केलर कोड की तुलना में लगभग 15 गुना तेज है। AVX2 के साथ, यह लगभग 30 गुना तेज है। मैंने नाममात्र के कोड के एक संस्करण की कोशिश नहीं की, जो सिर्फ write()इसके बजाय उपयोग करता है fwrite(), लेकिन संभवतः बड़े बफ़र्स के लिए stdio ज्यादातर रास्ते से बाहर रहता है। यदि यह डेटा की प्रतिलिपि बना रहा है, तो यह बहुत मंदी के लिए जिम्मेदार होगा।


एक Core2Duo E6600 (मेरोम 2.4GHz, 32kiB निजी L1, 4MiB साझा L2 कैश), DDR2-533MHz पर 64-बिट लिनक्स 4.2 (Ubuntu 15.10) पर 1GB डेटा का उत्पादन करने के लिए टाइम्स । अभी भी लिखने के लिए एक 128kiB बफर आकार का उपयोग कर (), उस आयाम का पता नहीं लगाया है।

जब तक अन्यथा निर्दिष्ट न हो / लेखन / देव / अशक्त।

  • (SSE2) ने न्यूलाइन हैंडलिंग के साथ और यादृच्छिक बाइट्स के प्रत्येक वेक्टर से अंकों के 4 वैक्टर: 0.183s (18.3 में 100GiB कर समय पर, लेकिन 1GiB रन के लिए इसी तरह के परिणाम)। प्रति चक्र 1.85 निर्देश।
  • (SSE2) इस, पाइपिंग टू wc -c: 0.593s ( अनसाल्टेड : रियल = 59.266s यूजर = 20.148 sys = 1m6.548s, wc का CPU टाइम सहित)। समान संख्या में राइट () सिस्टम साइबरविन के साथ कॉल करता है, लेकिन वास्तव में सभी डेटा को पाइप करता है क्योंकि लिनक्स एक पाइप के लिए एक लेखन () के सभी 128k को संभालता है।
  • नोमिनलिमल का fwrite()संस्करण (gcc5.2 -O3 -march=native), साथ चलता है ./decdig 100 $((1024*1024*1024/200)) > /dev/null: 3.19s +/- 0.1%, प्रति चक्र 1.40 निर्देश के साथ। -फिरोल-लूप्स ने शायद एक छोटे अंतर को बनाया। clang-3.8 -O3 -march=native: 3.42s +/- 0.1%
  • नाममात्र- fwriteपाइपिंग wc -c: असली = 3.980s उपयोगकर्ता = 3.176 sys = 2.080s
  • जेम्स हॉलिस का लाइन-ऑन-ए-टाइम संस्करण ( clang++-3.8 -O3 -march=native): 22.885s +/- 0.07%, प्रति चक्र 0.84 निर्देश के साथ। (g ++ 5.2 थोड़ा धीमा था: 22.98s)। एक बार में केवल एक पंक्ति लिखने से संभवतः काफी चोट लगी है।
  • स्टीफन चेज़लस tr < /dev/urandom | ...: रियल = 41.430s उपयोगकर्ता = 26.832 सेकेंड = 40.120 सेकेंड। trसभी समय के लिए सीपीयू कोर के सभी प्राप्त कर रहा था, कर्नेल चालक में लगभग अपना सारा समय यादृच्छिक बाइट्स बनाने और उन्हें एक पाइप पर कॉपी करने में खर्च कर रहा था। इस दोहरे कोर मशीन पर अन्य कोर बाकी पाइपलाइन चला रहा था।
  • time LC_ALL=C head -c512M </dev/urandom >/dev/null: यानी बिना किसी पाइपिंग के बस इतना यादृच्छिकता पढ़ना: वास्तविक = 35.018s उपयोगकर्ता = 0.036 sys = 34.940 s।
  • Lưu V (nh Phúc का पर्ल प्रोग्राम (Ubuntu15.10 से perl v5.20.2)
    LANG=en_CA.UTF-8:: वास्तविक = 4m32.634 उपयोगकर्ता = 4m3.288s sys = 0m29.364।
    LC_ALL=C LANG=C: real = 4m18.637s उपयोगकर्ता = 3m50.324s sys = 0m29.356s। अभी भी बहुत धीमी है।

  • (SSE2) यह बिना किसी नई हैंडलिंग के साथ , और यादृच्छिक बाइट्स के प्रत्येक वेक्टर से अंकों के 3 या 4 वैक्टर (लगभग एक ही गति: dig3 = v%10कदम इस HW पर भी ब्रेक के बारे में है): 0.166s (1.82 निर्देश प्रति चक्र) । यह मूल रूप से उस सीमा की निचली सीमा है जो हम पूरी तरह से कुशल न्यूलाइन हैंडलिंग के साथ करीब आ सकते हैं।

  • (SSE2) इसका कोई नया संस्करण नहीं है, लेकिन केवल एक अंक प्रति uint16_t तत्व का उपयोग करके v%10, 0.222 सेकंड +/- 0.4%, 2.12 निर्देश प्रति चक्र प्राप्त कर रहा है। (Gcc5.2 के साथ संकलित -march=native -O3 -funroll-loops। इस हार्डवेयर पर इस कोड के लिए मदद करने के लिए अनियंत्रित लूप होते हैं। इसका उपयोग आँख बंद करके न करें, विशेषकर बड़े कार्यक्रमों के लिए)।
  • (SSE2) इसका पुराना संस्करण, एक फाइल पर लिखना (3 फास्ट मैग्नेटिक हार्ड ड्राइव के RAID10f2 पर, लिखने के लिए बहुत अनुकूलित नहीं): ~ 4 सेकंड। लिखने से पहले बहुत अधिक गंदे डेटा को अनुमति देने के लिए कर्नेल I / O बफर सेटिंग्स को घुमाकर तेजी से जा सकते हैं। "सिस्टम" का समय अभी भी ~ 1.0 सेकंड है, "उपयोगकर्ता" समय से बहुत अधिक है। धीमे DDR2-533 रैम वाले इस पुराने सिस्टम पर, कर्नेल के लिए पेजकेच में डेटा को कम करने के लिए कर्नेल के लिए ~ 4x अधिक समय लगता है और मेरे लूप के लिए एक्सएफएस कार्यों को चलाने की तुलना में यह एक बफर में इसे फिर से लिखने के लिए रखता है - यह गर्म रहता है कैश।

यह कैसे किया है

एक तेजी से PRNG जाहिर है आवश्यक है। xorshift128 + को वेक्टर किया जा सकता है, इसलिए आपके पास सिमड वेक्टर के तत्वों में समानांतर में दो या चार 64-बिट जनरेटर होते हैं। प्रत्येक चरण यादृच्छिक बाइट्स की एक पूर्ण वेक्टर का उत्पादन करता है। ( इंटेल इंटिंसिक्स के साथ यहां 256b एवीएक्स 2 कार्यान्वयन )। मैंने इसे नॉमिनल की xorshift * की पसंद पर चुना, क्योंकि 64-बिट वेक्टर पूर्णांक गुणन केवल SSE2 / AVX2 में विस्तारित-सटीक तकनीकों के साथ संभव है


यादृच्छिक बाइट्स के एक वेक्टर को देखते हुए, हम प्रत्येक 16-बिट तत्व को कई दशमलव अंकों में काट सकते हैं। हम 16-बिट तत्वों के कई वैक्टर का उत्पादन करते हैं जो प्रत्येक एक ASCII अंक + ASCII स्थान हैं । हम सीधे अपने आउटपुट बफर में स्टोर करते हैं।

मेरा मूल संस्करण सिर्फ x / 6554एक वेक्टर के हर uint16_t तत्व से एक यादृच्छिक अंक प्राप्त करने के लिए उपयोग किया जाता है। यह हमेशा 0 और 9 के बीच है, समावेशी है। यह पक्षपाती है 9, क्योंकि (2^16 -1 ) / 6554यह केवल 9.99923 है। (6554 = छत ((2 ^ 16-1) / 10), जो यह सुनिश्चित करता है कि भागफल हमेशा <10. है)

x/6554एक "जादू" स्थिरांक ( निश्चित-बिंदु पारस्परिक ) द्वारा एक से गुणा किया जा सकता है और उच्च-आधे परिणाम का एक सही बदलाव। यह एक स्थिर द्वारा विभाजन के लिए सबसे अच्छा मामला है; कुछ भाजक अधिक ऑपरेशन करते हैं, और हस्ताक्षरित विभाजन अतिरिक्त काम लेता है। x % 10एक समान पूर्वाग्रह है और गणना करने के लिए उतना सस्ता नहीं है। (gcc का asm आउटपुट बराबर है x - 10*(x/10), अर्थात एक मॉड्यूलर गुणन प्रतिलोम का उपयोग करके विभाजन के शीर्ष पर एक अतिरिक्त गुणा और घटाना है।) इसके अलावा, xorshift128 का सबसे कम बिट उच्च गुणवत्ता वाला नहीं है , इसलिए उच्च बिट्स से एंट्रॉपी लेने के लिए विभाजित करना बेहतर है ( गुणवत्ता के लिए और साथ ही गति) modulo से कम बिट्स से एंट्रोपी लेने के लिए।

हालाँकि, हम प्रत्येक दशमलव के अधिक अंक का उपयोग कर सकते हैं, कम दशमलव अंकों को देखकर, जैसे @ नाममात्र का digit()कार्य। अधिकतम प्रदर्शन के लिए, मैंने कम 3 दशमलव अंक लेने का फैसला किया और x/6554, एक PMULLW और PSUBW (और शायद कुछ MOVDQA) बनाम 4 निम्न दशमलव अंकों को लेने के उच्च गुणवत्ता विकल्प को बचाने के लिए। x / 6554 निम्न 3 दशमलव अंकों से थोड़ा प्रभावित होता है, इसलिए एक ही तत्व के अंकों (ASCII आउटपुट में 8 या 16 अंकों के अलगाव, वेक्टर चौड़ाई के आधार पर) के बीच कुछ सहसंबंध होता है।

मुझे लगता है कि जीसीसी 100 और 1000 से विभाजित कर रहा है, बजाय एक लंबी श्रृंखला के जो क्रमिक रूप से 10 से विभाजित होता है, इसलिए यह संभवतः गैर-लूप-निर्भर निर्भरता श्रृंखला की लंबाई को छोटा नहीं करता है जो प्रत्येक PRNG आउटपुट से 4 परिणाम उत्पन्न करता है। port0 (वेक्टर गुणा और शिफ्ट) मॉड्यूलर गुणक व्युत्क्रम और xorshift + में बदलाव के कारण अड़चन है, इसलिए वेक्टर-गुणा को बचाने के लिए यह निश्चित रूप से उपयोगी है।

xorshift + इतना तेज़ है कि हर 16 (यानी 20% दक्षता) से केवल 3.3 बिट्स यादृच्छिकता का उपयोग करते हुए भी इसे कई दशमलव अंकों में काट देने से बहुत धीमा नहीं है। हम केवल समान वितरण को अनुमानित करते हैं, क्योंकि यह उत्तर गति पर केंद्रित है जब तक कि गुणवत्ता बहुत खराब न हो।

किसी भी प्रकार का सशर्त व्यवहार जो तत्वों की एक चर संख्या रखता है, बहुत अधिक काम करेगा। (लेकिन सिमड लेफ्ट-पैकिंग तकनीक का उपयोग करके शायद अभी भी कुछ हद तक कुशलता से किया जा सकता है । हालांकि, छोटे तत्व के लिए कम कुशल हो जाता है; विशाल शफल-मास्क लुकअप टेबल व्यवहार्य नहीं हैं, और 32 से छोटे के साथ कोई AVX2 लेन-क्रॉसिंग फेरबदल नहीं है। बिट तत्व। 128b PSHUFB संस्करण अभी भी BMI2 PEXT / PDEP के साथ फ्लाई पर मास्क उत्पन्न करने में सक्षम हो सकता है , जैसे आप बड़े तत्वों के साथ AVX2 के लिए कर सकते हैं , लेकिन यह मुश्किल है क्योंकि 64-बिट पूर्णांक केवल 8 बाइट्स के साथ है। गॉडबोल्ट लिंक उस उत्तर पर कुछ कोड है जो उच्च तत्व गणना के लिए काम कर सकता है।)


यदि RNG की विलंबता एक अड़चन है, तो हम समानांतर में दो जनरेटर के वैक्टर चलाकर और भी तेजी से जा सकते हैं, जो हम उपयोग करते हैं। कंपाइलर अभी भी आसानी से एक अनियंत्रित लूप में रजिस्टरों में सब कुछ रख सकता है, और इससे दो निर्भरता श्रृंखला समानांतर में चलती हैं।

वर्तमान संस्करण में, PRNG के आउटपुट को काटते हुए, हम वास्तव में पोर्ट 0 थ्रूपुट पर अड़चन हैं, PRNG विलंबता नहीं, इसलिए इसके लिए कोई आवश्यकता नहीं है।


कोड: AVX2 संस्करण

Godbolt संकलक एक्सप्लोरर पर अधिक टिप्पणियों के साथ पूर्ण संस्करण ।

बहुत चुस्त नहीं, माफ करना मुझे सोना है और इस पोस्ट को प्राप्त करना है।

SSE2 संस्करण प्राप्त करने के लिए, s/_mm256/_mm, s/256/128/, s/v16u/v8u/, और बदलने के vector_size(32)अलावा 4 * 16 के लिए 4 * 8 से न्यू लाइन वेतन वृद्धि बदल 16. करने के लिए। (जैसा कि मैंने कहा, कोड गड़बड़ है, और दो संस्करणों को संकलित करने के लिए अच्छी तरह से सेट नहीं किया गया है। मूल रूप से एक AVX2 संस्करण बनाने की योजना नहीं है, लेकिन तब मैं वास्तव में एक हसवेल सीपीयू पर परीक्षण करना चाहता था जिसकी मुझे पहुंच थी।)

#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, या ICC के साथ संकलन (या उम्मीद है कि कोई अन्य संकलक जो GN99 C की C99 की समझ और इंटेल की आंतरिकता को समझता है)। GNU C वेक्टर एक्सटेंशन कंपाइलर / मोडुलो के लिए जादुई संख्याओं का उपयोग करते हुए कंपाइलर प्राप्त करने के लिए अत्यधिक सुविधाजनक है __attribute__

यह आंशिक रूप से लिखा जा सकता है, लेकिन यह अधिक कोड ले जाएगा।


प्रदर्शन नोट:

ओवरलैपिंग-स्टोर में न्यूलाइन्स डालने के लिए यह तय करने के लिए महत्वपूर्ण ओवरहेड है कि इसे कहां रखा जाए (ब्रांच मिसप्रिडिक्शन, और कोर 2 पर अड़चनें), लेकिन स्टोर पर ही प्रदर्शन पर कोई प्रभाव नहीं पड़ता है। कंपाइलर की एशम में बस उस स्टोर इंस्ट्रक्शन को कमेंट करते हुए (सभी ब्रांचिंग को समान करते हुए) कोर 2 पर प्रदर्शन को पूरी तरह से अपरिवर्तित छोड़ दिया, साथ ही बार-बार रन देने का समय +/- 1% से कम था। इसलिए मैं यह निष्कर्ष निकालता हूं कि स्टोर बफर / कैश इसे ठीक से संभालता है।

फिर भी, ascii_digitspaceएक तत्व के साथ एक नई तरह की घूर्णन खिड़की का उपयोग करना एक नई रेखा से भी तेज हो सकता है, अगर हम पर्याप्त रूप से अनियंत्रित हो जाते हैं कि कोई भी काउंटर / ब्रांचिंग चले जाते हैं।


/ Dev / null को लिखना मूल रूप से एक no-op है, इसलिए बफर संभवतः L2 कैश में गर्म रहता है (Haswell पर 256kiB प्रति कोर)। 128b वैक्टर से 256b वैक्टर तक का सही स्पीडअप अपेक्षित है: कोई अतिरिक्त निर्देश नहीं हैं, और सब कुछ (दुकानों सहित) दो बार चौड़ाई के साथ होता है। नईलाइन-प्रविष्टि शाखा को दो बार लिया जाता है, हालांकि। मैंने दुर्भाग्यवश उस भाग के #ifdefएड के साथ अपने हैसवेल साइबरविन सेटअप पर समय नहीं दिया।

2.5GHz * 32B / 13.7GB / s = 5.84 साइकिल प्रति AVX2-Haswell पर स्टोर। यह बहुत अच्छा है, लेकिन तेज हो सकता है। शायद मैंने सोचा था कि साइबरविन सिस्टम कॉल में कुछ ओवरहेड है। मैंने कंपाइलर के एसएसएम आउटपुट में उन पर टिप्पणी करने की कोशिश नहीं की (जो यह सुनिश्चित करेगा कि कुछ भी अनुकूलित न हो।)

L1 कैश प्रति घड़ी एक 32B स्टोर को बनाए रख सकता है, और L2 बहुत कम बैंडविड्थ (उच्च विलंबता, हालांकि) नहीं है।

जब मैंने IACA को कुछ संस्करणों में देखा था (नई शाखाओं के लिए शाखा के बिना, लेकिन केवल एक ASCII वेक्टर प्रति RNG वेक्टर मिल रहा था), यह 4 या 5 घड़ियों के प्रति 32B वेक्टर स्टोर की तरह कुछ भविष्यवाणी कर रहा था।

मैं प्रत्येक RNG परिणाम से अधिक डेटा निकालने से स्पीडअप के अधिक होने की उम्मीद कर रहा था, जो कि खुद को देखने के आधार पर, एग्नर फॉग के गाइड और अन्य अनुकूलन संसाधनों पर विचार कर रहा है, जो मैंने एसओ x86 टैग विकी में लिंक जोड़े हैं ।

संभवतः यह स्काइलेक पर काफी तेज होगा , जहां वेक्टर पूर्णांक गुणा और बदलाव दो बार कई बंदरगाहों (p0 / p1) पर चल सकता है, हसवेल (p0 केवल) की तुलना में। xorshift और डिजिट एक्सट्रैक्शन दोनों ही बहुत सारी पारियों और गुणाओं का उपयोग करते हैं। ( अपडेट: स्काईलेक ने इसे 3.02 आईपीसी पर चलाया, हमें 3.77 साइकिल प्रति 32-बाइट एवीएक्स 2 स्टोर पर दी गई, जोकि 1 जीबी प्रति सेकंड के मग में समय पर मिली, /dev/nullलिनक्स 4.15 पर i7-6700k पर 3.9GHz पर लिखी गई।


अच्छी तरह से काम करने के लिए 64-बिट मोड की आवश्यकता नहीं है । SSE2 संस्करण केवल उतनी ही तेजी से है जब इसके साथ संकलित किया जाता है -m32, क्योंकि इसमें बहुत अधिक वेक्टर रजिस्टरों की आवश्यकता नहीं होती है, और सभी 64-बिट गणित वैक्टर में किया जाता है, न कि सामान्य-उद्देश्य रजिस्टरों में।

यह वास्तव में Core2 पर 32-बिट मोड में थोड़ा तेज है, क्योंकि मैक्रो-फ्यूजन की तुलना केवल 32-बिट मोड में काम करती है, इसलिए आउट-ऑफ-ऑर्डर कोर (18.3s (1.85 निर्देश प्रति घड़ी) बनाम के लिए कम यूओपी हैं। । 16.9s (2.0 IPC))। कोई REX उपसर्ग होने से छोटे कोड-आकार भी Core2 के डिकोडर्स में मदद करता है।

इसके अलावा, कुछ रेग-रेग वेक्टर चालों को भार के साथ बदल दिया जाता है, क्योंकि सभी स्थिरांक वेक्टर वेक्टर में अब ठीक नहीं होते हैं। चूंकि L1 कैश से लोड थ्रूपुट एक अड़चन नहीं है, यह वास्तव में मदद करता है। (जैसे set1(10): एक स्थिर वेक्टर के गुणा से movdqa xmm0, xmm10/ में pmullw xmm0, xmm1बदल जाता है movdqa xmm0, [constant]/ pmullw xmm0, xmm1।) चूंकि reg-reg MOVDQA को ALU पोर्ट की आवश्यकता होती है, यह वास्तविक कार्य के साथ प्रतिस्पर्धा करता है, लेकिन एक MOVDQA लोड केवल फ्रंट-एंड डायोड बैंडविड्थ के लिए प्रतिस्पर्धा करता है। (REX उपसर्गों को बचाने से कई निर्देशों को रद्द करते हुए, कई निर्देशों के अंदर एक 4-बाइट पते के साथ।

मुझे आश्चर्य नहीं होगा अगर ALU MOVDQA को बचाने के लिए वास्तविक लाभ वहां से आ रहा है, क्योंकि दृश्यपटल को 2.0 IPC के औसत के साथ अच्छी तरह से रखना चाहिए।

ये सभी अंतर हैसवेल पर गायब हो जाते हैं, जहां पूरी तरह से डिकोड-यूओपी कैश से चलना चाहिए, अगर लूपबैक बफर नहीं। नेहलम के बाद से ALU + ब्रांच मैक्रो-फ्यूजन दोनों मोड में काम करता है।


6
मैं सिर्फ प्यार करता हूँ कि आप इस विषय में "जानवर मोड" कैसे गए ! :) इससे भी महत्वपूर्ण बात यह है कि यह एक उत्कृष्ट उदाहरण है कि किस प्रकार के लाभ उपलब्ध हैं, यदि आपको वास्तव में जरूरत है या अधिकतम प्रदर्शन को निचोड़ना चाहते हैं, तो हाथ में हार्डवेयर के बहुत निम्न-स्तरीय ज्ञान का उपयोग करें। साथ ही, हम यहां केवल एक धागे का उपयोग कर रहे हैं; अधिकांश वर्तमान डेस्कटॉप और सर्वर इंटेल / एएमडी प्रोसेसर (और हल्के टैबलेट और एसबीसी में एआरएम वाले) के पास कई कोर हैं, इसलिए अभी भी अधिक वास्तविक-विश्व-टाइम स्पीडअप उपलब्ध हैं। और अंत में, कितना अव्यवहारिक " सवाल करने के लिए सबसे तेज़ तरीका" , सरासर प्रयास में शामिल हैं।
सांकेतिक पशु

1
@NominalAnimal: हाँ, यहां तक ​​कि एक धीमी एआरएम क्वाड या ऑक्टो कोर आसानी से नियॉन के साथ एक ही काम कर रही मुख्य मेमोरी बैंडविड्थ को संतृप्त कर सकती है (भले ही वे फास्ट ड्यूल चैनल DDR3 के लिए झुके थे), अगर इसमें 64-बिट पूर्णांक SIMD कहते हैं और शिफ्ट्स । मेरा मानना ​​है कि ऑडियो काम के लिए NEON में 16-बिट एलिमेंट-साइज़ मल्टीप्लीज़ हैं। निर्देश-शेड्यूलिंग एक इन-ऑर्डर एआरएम के लिए बहुत अधिक काम होगा, क्योंकि लूप-आधारित निर्भरता श्रृंखला (xorshift128 +) के प्रत्येक पुनरावृत्ति कुछ स्वतंत्र निर्भरता श्रृंखलाओं को काटती है जो इसे काटती हैं और इसे मेमोरी में संग्रहीत करती हैं ...
पीटर कॉर्ड्स

... आउट-ऑफ-ऑर्डर निष्पादन नाश्ते के लिए खाती है, क्योंकि पूरी चीज काफी कम है कि कई पुनरावृत्तियां आरओबी (एचएसडब्ल्यू IIRC पर 192 uops) में फिट होती हैं। (यानी निर्देशों का "विंडो" जो आउट-ऑफ-ऑर्डर निष्पादन देखता है, इसमें कई पुनरावृत्तियों शामिल हैं)। इसलिए सीपीयू 2 या 3 पुनरावृत्तियों के लिए अंतिम स्टोर को समाप्त कर सकता है जबकि वर्तमान पुनरावृत्ति की शुरुआत में भी शुरू हो रहा है। यह स्वतंत्र श्रृंखला की विलंबता को छुपाता है, इसलिए केवल थ्रूपुट मायने रखता है। इन-ऑर्डर कोर पर, इसे सॉफ्टवेयर पाइपलाइनिंग की आवश्यकता होगी ...
पीटर कॉर्ड्स

... एक अच्छा एआरएम कंपाइलर आपको इसके लिए कुछ करना चाहिए, अगर आप इसे इंट्रिनिक्स (या जीएनयू सी देशी वेक्टर सिंटैक्स के साथ पूरी बात के लिए लिखते हैं, जैसे कि मुझे पहली जगह में करना चाहिए था)। मुझे ऐसा करने का कोई अनुभव नहीं है, असली के लिए, इसलिए आपको अपने लूप की मालिश करने की आवश्यकता हो सकती है और हो सकता है कि अच्छा एएसएम प्राप्त करने के लिए स्रोत में कुछ मैनुअल अनरोलिंग / सॉफ़्टवेयर-पाइपलाइनिंग करें। (कुछ आउट-ऑफ-ऑर्डर एआरएम कोर हैं, जो उच्च-अंत फोन में पाए जाते हैं, लेकिन उनके पास हसवेल के रूप में बड़ी आउट-ऑफ-ऑर्डर विंडो नहीं है। ओटीओएच, उनके पास कम शिखर थ्रूपुट भी है, इसलिए वहां कम है। अधिक आईएलपी पाने से)।
पीटर कॉर्डेस

1
@NominalAnimal: भी, प्रश्न की शिथिलता पर सहमत हुए। यादृच्छिकता की गुणवत्ता पर कोई प्रतिबंध नहीं के साथ "सबसे तेज़" मूर्खतापूर्ण है ... BTRFS के साथ, डिस्क पर एक ही डेटा कई बार फ़ाइल का हिस्सा हो सकता है ( 4.2 में EXTENT_SAME देखें )। तो आप एक यादृच्छिक 4kiB या 1MB उत्पन्न कर सकते हैं और इसे दोहरा सकते हैं। यह एक छोटी अवधि के साथ यादृच्छिकता है, लेकिन यह अभी भी यादृच्छिक है, और केवल मेटाडेटा I / O का खर्च आता है। (वास्तव में, आपको एक नई लाइन के साथ ब्लॉक को समाप्त करने की आवश्यकता है। lcm (4096, 4096 * 200) = 4096 * 200 = 819200 = 800kiB, इसलिए आपका रिपीट ब्लॉक किसी भी प्रकार का अधिक है।)
पीटर कॉर्डेस

14

यहाँ एक समाधान है मुझे आशा है कि समझने में आसान है:

od -An -x /dev/urandom | tr -dc 0-9 | fold -w100 | awk NF=NF FS= | head -c1G
  • odसे हेक्साडेसिमल अंकों की एक समान धारा बनाता है /dev/random
  • trअक्षरों से छुटकारा मिलता है, केवल 0-9अंक रखते हुए
  • fold यह सुनिश्चित करता है कि प्रति पंक्ति 100 अंक हों
  • awk लाइनों के अंदर रिक्त स्थान सम्मिलित करता है
  • head 1 गीगाबाइट में इनपुट काटता है

2
यह एक अच्छा वैकल्पिक तरीका है कि बाइट / देव / यादृच्छिक द्वारा एक से अधिक अंकों का उत्पादन किया जाता है, जबकि अभी भी एक समान वितरण होता है, जो कि प्रत्येक 256 बाइट्स के लिए 320 डिजिट का उत्पादन करता है / औसत / जब आप बाइट्स परिवर्तित करते हैं (कम से कम जब आप बाइट्स परिवर्तित करते हैं <200 modulo 100 से दशमलव जो आपको प्रत्येक 256 बाइट्स के लिए 400 अंक देता है)।
स्टीफन चेज़लस

6

आप इसके लिए jotकमांड का उपयोग कर सकते हैं :

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

1
@DigitalTrauma के मेरे संस्करण में fmtलक्ष्य चौड़ाई विकल्प नहीं है। वैसे भी, यह सटीक होगा क्योंकि सभी अंक बिल्कुल एक कॉलम लेते हैं!
गार्डेनहेड नोव

रिकॉर्ड के लिए मेरा fmtसंस्करण fmt (GNU coreutils) 8.25(उबंटू 16.04)
डिजिटल ट्रॉमा

2
आधे gb के लिए सही संख्या है: 1024 * 1024 * 536870912
1024/2

1
@OlivierDulac निर्भर करता है कि आप किस "गीगाबाइट" पर बात कर रहे हैं। कुछ लोग तकनीकी रूप से गलत होने के बावजूद 2 ^ 30 के बजाय 1 Gb का मतलब 10 ^ 9 करते हैं। इसके अलावा मुझे अच्छे गोल नंबर पसंद हैं :)
गार्डहेड

6
@gardenhead, अधिक से अधिक लोग अब गीगाबाइट == 1e9 और गिबिबाइट == 2 ^ 30 पर जाने के लिए IEC मानक की परिभाषा देते हैं। विकिपीडिया देखें । ध्यान दें कि Gb ही Giga- सा होगा
स्टीफन चेज़लस

6

यह स्टीफन चेज़ेलस पद्धति के समान है, हालांकि मैंने प्रदर्शन में सुधार के लिए एक बार में 64 बिट्स पढ़े। वितरण अभी भी समान है लेकिन अब आपको पहले की तरह सर्वश्रेष्ठ मामले में केवल 8 के बजाय प्रत्येक 8 बाइट्स के लिए 19 अंक मिलेंगे

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-बिट प्लेटफॉर्म पर 19 के बजाय हर बार 9 अंक पढ़े जाएंगे।


यदि आपका सिस्टम 64-बिट पूर्णांक का समर्थन नहीं करता है या perlक्वाड समर्थन के साथ संकलित नहीं है, तो यह अपवाद को बढ़ा सकता है ।
cuonglm

@ cuonglm हाँ जैसा कि मैंने कहा कि अगर पर्ल उस सिस्टम पर 64 बिट्स नहीं है तो प्रोग्राम को next if $n >= 1000000000; $s = sprintf("%09u", $n);केवल 9 अंक प्राप्त करने के लिए बदल दिया जाना चाहिए
phuclv

$n = unpack("Q")यदि क्वाड समर्थित नहीं है, तो आप प्रोग्राम को क्रैश नहीं करेंगे ।
कोउंगलम

1
@cuonglm में BEGIN{$/=\4; $,=" "} $n = unpack("L");भी परिवर्तन हुआ
phuclv

1
क्षमा करें, लेकिन यह 8 बाइट इनपुट से 19 अंक प्राप्त करता है, केवल 54.2% समय और बाकी कोई नहीं, औसत प्रति इनपुट बाइट 1.29 अंक। यदि आप स्टीफन को अधिक पसंद करते हैं <16e18और 16 से विभाजित करते हैं, तो आपको 1.95 dpB के लिए 18 अंक 86.7% मिलते हैं। 32bit के साथ, <4e9 /42.10 dpB के लिए 9 अंक 93.1% मिलता है। लेकिन 5 बाइट्स (हेक्स के रूप में (एच 10)) <1e122.18 डीपीबी के लिए 12 अंक 90.9% देता है, या हेक्स को आधे में विभाजित करता है और प्रत्येक आधा करने पर <1e6 2.29 डीपीबी के लिए 6 अंक 95.4% देता है; यह log_10 (256) = 2.41 की सीमा तक पहुंचता है।
dave_thompson_085

3

यदि आप गति की आवश्यकता है, तो संकलित प्रोग्रामिंग भाषा का उपयोग करने में मैं नाममात्र के जानवर से सहमत हूं। हालाँकि, आपको C R ++ में अपना RNG कोड लिखना नहीं है। यह मानक लाइब्रेरी के भाग के रूप में उत्कृष्ट Mersenne Twister प्रदान करता है।

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

उपरोक्त कोड यथोचित सरल है और जब मैं किसी फाइल में आउटपुट देता हूं तो लगभग एक मिनट का समय लगता है। हम १०० अंकों के लिए एक स्ट्रिंग बड़ा बनाकर और उसमें अंकों को हैक करके बहुत तेजी से आगे बढ़ सकते हैं। यह हमें हर अंक के बजाय प्रत्येक पंक्ति को कॉल करने की अनुमति देता है।

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

यह कोड मेरी मशीन को छह सेकंड के आसपास ले जाता है। याद रखें कि यह मानक आउटपुट है, इसलिए इसे किसी फ़ाइल में पाइप करें।

मेरे पास कुछ अस्वीकरण हैं। सबसे पहले, मैं इसे विंडोज पीसी पर लिख रहा हूं। मुझे लगता है कि पुस्तकालय लिनक्स पर मौजूद हैं, लेकिन अगर मैं गलत हूं, तो इसे इंगित करना सुनिश्चित करें।

इसके अलावा, यह वास्तव में आधा बिलियन अंतरिक्ष से अलग किए गए अंकों को आउटपुट करता है, जो तकनीकी रूप से एक गीगाबाइट है लेकिन शायद बिल्कुल वैसा नहीं है जैसा आप चाहते थे। यह 5 मिलियन लाइनों, प्रति पंक्ति 100 अंकों को आउटपुट करता है। यदि अंतर महत्वपूर्ण है, तो आप लाइनों की संख्या बढ़ा सकते हैं। मेरे विंडोज बॉक्स पर फ़ाइल 10 ^ 9 बाइट्स से थोड़ी बड़ी लगती है, जो मुझे लगता है कि अतिरिक्त न्यूलाइन वर्णों के साथ कुछ करना है।


2
अरे, आलोचना वास्तव में उचित नहीं है! :) मेरे कार्यक्रम का अधिकांश कमांड-लाइन पैरामीटर पार्सिंग है। यदि मैं टिप्पणियों, त्रुटि जांचों, और स्तंभों और लाइनों के आउटपुट की संख्या को हार्डकोड करता हूं, तो मैं इसे आपके कोड के आकार से दोगुना से कम कर सकता हूं - शायद ही राक्षसी । :) एक तरफ से हटकर: हाँ, अधिकांश लिनक्स वितरण में पुस्तकालय उपलब्ध हैं। मेरे लैपटॉप पर, आपके लाइन-ऑन-ए-टाइम में लगभग 14 सेकंड लगते हैं, जबकि मेरे लाइन-ए-ए-टाइम संस्करण में केवल 1.3 सेकंड लगते हैं। अंतर केवल PRNG के कारण है: Mersenne Twister Xorshift64 * की तुलना में बहुत धीमा है।
सांकेतिक पशु

1
एक व्यावहारिक बात है जो मैं आपको बताना चाहता हूं, लेकिन मुझे आशा है कि आप इसे नकारात्मकता के रूप में नहीं लेंगे, बस कुछ सोचने के लिए: जैसा कि मैंने अपने जवाब में उल्लेख किया है, एक-शॉट कार्यक्रम शायद ही कभी लायक हैं। लिखने के लिए समय लिया। यही कारण है कि कमांड-लाइन पार्सिंग और हेल्प यूसेज टेक्स्ट को जोड़ना लगभग हमेशा सार्थक होता है। मेरे पास ऐसे उपयोगिता कार्यक्रमों का एक बड़ा सेट है, और उनके स्रोतों का शिकार करने के बजाय यह पता लगाने के लिए कि उनमें से प्रत्येक क्या करता है, मैं बस उन्हें चलाता हूं, इसलिए वे मुझे बताएंगे; और मैं एक से अधिक आवश्यकताओं के अनुरूप उनके व्यवहार को संशोधित कर सकता हूं। विकास लागत बढ़ाना।
सांकेतिक पशु

@NominalAnimal एक और महत्वपूर्ण बात यह है कि आपने आउटपुट को पाइप किया है /dev/nullजो वास्तविक फ़ाइल में लिखने की तुलना में कहीं अधिक तेज़ होगा
phuclv

@ LưuV LnhPhúc: ठीक है, वास्तव में नहीं। इस lappy में सैमसंग 128GB SSD है, जिसमें ~ 500 MB / s अनुक्रमिक रीडिंग और लिखते हैं। चार लिनक्स सॉफ्टवेयर-RAID0 विन्यास में रखो, और आप एक बड़े गीगाबाइट पर पढ़ेंगे और दूसरे बड़े डेटासेट उत्पन्न करते हुए लिखेंगे (मेरा अनुमान है ~ 1.75 टीबी / एस)। लिनक्स स्व-RAID0 के साथ 12 SATA ड्राइव (कताई पट्टियाँ, SSDs भी नहीं) के साथ 1GB / s साल पहले पहुंच गया था। (नोट: बाइट्स / एस, बिट्स / एस नहीं।) निश्चित रूप से, यह एक "सामान्य" मशीन के लिए मूर्खतापूर्ण लगता है, लेकिन जो बड़े डेटासेट के साथ खेलते हैं, वे इस लायक पाते हैं - आप जो कुछ भी करते हैं उससे बड़े समय के लिए शेव करते हैं (बड़े डेटासेट के साथ) उस तरफ।
नाममात्र पशु

1
@NominalAnimal और Lu'u: इससे भी महत्वपूर्ण बात, अगर आपके पास पर्याप्त रैम है, तो प्रोग्राम डिस्क पर डेटा सभी से पहले अच्छी तरह से बाहर निकल सकता है। एक बड़े write()सिस्टम कॉल में अधिकांश कार्य पेजचेस में एक यादगार है, जो केवल तभी ब्लॉक करता है जब कर्नेल अधिक बफर स्थान आवंटित करने के बजाय ऐसा करने का निर्णय लेता है। यह प्रोग्राम केवल डिस्क I / O पर टोंटी को तब चलाना चाहिए जब मेमोरी तंग हो, या यदि आपने पेजचे को बायपास करने के लिए O_DIRECT का उपयोग किया था। यदि आप write()कैश आकार से छोटे चंक्स में हैं, तो उम्मीद है कि आपका डेटा केवल एक बार मुख्य मेमोरी में जाता है, और जिस बफर को फिर से लिखा जाता है वह L2 या L3 कैश में गर्म रहता है।
पीटर कॉर्ड्स

1

यह "यादृच्छिक" की आपकी परिभाषा पर निर्भर करता है। यदि आप क्रिप्टोग्राफिक रूप से यादृच्छिक का मतलब रखते हैं, तो आपको बस एक अच्छी लाइब्रेरी प्राप्त करनी होगी और बुलेट को काटने के लिए, इसके चलने का इंतजार करना होगा।

यदि आपको बस कुछ ऐसा चाहिए, जो बहुत यादृच्छिक लगे, तो यहां एक आसान तरीका है:

  1. ऐसी फ़ाइल प्राप्त करें जो कई Gb लंबी हो। आपकी पसंदीदा फिल्म अच्छी होगी।
  2. इसे दोहराएं, दोहराया पैटर्न को निचोड़ने का एक आसान तरीका
  3. एक समय में एक नीब्बल (आधा बाइट) फ़ाइल के माध्यम से जाओ। प्रत्येक मान 0 और 15. के बीच होगा। किसी भी 1 से कम या 1 से अधिक दूर फेंक दें। प्रत्येक 1 बिलियन बचे में से 1 को घटाएं और इसे एक अंक के रूप में लिखें।

धीमी मशीन पर चलने में एक घंटे का समय लग सकता है; अधिकांश उद्देश्यों के लिए पर्याप्त तेज़ और यादृच्छिक।


9
/dev/urandomgzipगति और यादृच्छिकता दोनों से बेहतर होने की संभावना है ।
स्टिग हेमर

Get a file that is several Gb long1GB फ़ाइल पाने के लिए आपको एक फ़ाइल ** कम से कम 8Gb` की आवश्यकता होगी
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
साइट पर आपका स्वागत है! मेरे प्रोफाइल पेज पर लिंक देखें। यहां कई बेहतरीन मुद्दे हैं जो मुझे शेल स्क्रिप्ट में लगभग सार्वभौमिक रूप से दिखाई देते हैं, लेकिन यह उन्हें सही नहीं बनाता है।
वाइल्डकार्ड

2
@Wildcard: कभी नहीं cat file | trजब आप बस कर सकते हैं tr <file। IIRC, आप भी कर सकते हैं <file tr। मुझे लगा कि आप इस शेल लिपि के बारे में बात कर रहे थे, जो du | awkहर तरह से आकार की जाँच करने के लिए क्लंकी और धीमी दिख रही थी, और लूप के बाहर रीडायरेक्ट करने के बजाय हर लाइन को जोड़ने के लिए फाइल को फिर से खोलना।
पीटर कॉर्डेस

2
@PeterCordes, हाँ। पाठ को संसाधित करने के लिए शेल लूप का उपयोग करना बुरा व्यवहार क्यों माना जाता है? विशेष रूप से प्रासंगिक है - यह स्क्रिप्ट इस विचार पर आधारित है कि बैश C जैसी प्रोग्रामिंग भाषा है, जो कि नहीं है। लेकिन, \ _NNNT, मुझे आशा है कि आप इस साइट पर चारों ओर से चिपके रहेंगे क्योंकि यह स्पष्ट है कि आपके पास बहुत तार्किक दिमाग है। :)
वाइल्डकार्ड

4
@PeterCordes cat /dev/urandom | busy-cmdउन दुर्लभ मामलों में से एक है जहां यह समझ में आता है क्योंकि यह यादृच्छिक पीढ़ी और प्रोसेसर के बीच व्यस्त cmd को विभाजित कर सकता है। ट्र के लिए इतना नहीं है, लेकिन सैम के odउदाहरण के लिए एक फर्क पड़ता है ।
स्टीफन चेजलस 7

1
@ स्टीफनचेज़ेलस: ओह सही !! हाँ, रीड () सिस्टम कॉल वह जगह है जहाँ RNG CPU समय व्यतीत होता है।
पीटर कॉर्डेस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.