Pi Бенчмаркинг в C

Я написал следующую программу для вычисления цифр n для Pi (где n может быть чем угодно, например 10M), чтобы сравнить CPU и работать отлично (без OpenMP):

/*
*
* Simple PI Benchmarking tool
* Author: Suyash Srijan
* Email: [email protected]
*
* This program calculates how much time your CPU takes to compute n digits of PI using Chudnovsky Algorithm
* (http://en.wikipedia.org/wiki/Chudnovsky_algorithm) and uses the GNU Multiple Precision Arithmetic Library
* for computation.
*
* For verification of digits, you can download the digits from here: http://piworld.calico.jp/estart.html
*
* It's a single threaded program but you can compile it with OpenMP support to enable parallelization.
* WARN: OpenMP support is experimental
*
* Compile using gcc : gcc -O2 -Wall -o pibench pibench.c -lgmp -lssl -lcrypto
* Compile using gcc (with OpenMP): gcc -O2 -Wall -o pibench pibench.c -lgmp -lssl -lcrypto -fopenmp
*
*/

#include <gmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/resource.h>
#include <sys/utsname.h>
#include <openssl/md5.h>

/* Import OpenMP header if compiling with -fopenmp */
#if defined(_OPENMP)
#include <omp.h>
#endif

/* You can't compile this on Windows */
#ifdef _WIN32
#error >>> Fatal: It is not possible to compile this program on Windows <<<
#endif

/* Build timestamp */
#define build_time __TIME__
#define build_date __DATE__

/* Calculate log to the base 2 using GCC's bit scan reverse intrinsic */
__inline__ unsigned int clc_log2(const unsigned int num) {
    return ((num <= 1) ? 0 : 32 - (__builtin_clz(num - 1)));
}

/* Calculate MD5 checksum for verification */
__inline__ char *clc_md5(const char *string) {
    MD5_CTX context;
    unsigned char digest[16];
    char *checksum = (char*)malloc(33);
    int i;

    MD5_Init(&context);
    MD5_Update(&context, string, strlen(string));
    MD5_Final(digest, &context);

    for (i = 0; i < 16; ++i) {
        snprintf(&(checksum[i*2]), 3, "%02x", (unsigned int)digest[i]);
    }
    return checksum;
}

/* Calculate pi digits main function */
__inline__ char *clc_pi(unsigned long dgts)
{
    /* Variable declaration */
    struct timespec start, end;
    unsigned long int i, ti, constant1, constant2, constant3;
    unsigned long iters = (dgts / 15) + 1;
    unsigned long precision;
    double bits;
    char *oput;
    mpz_t v1, v2, v3, v4, v5;
    mpf_t V1, V2, V3, total, tmp, res;
    mp_exp_t exponent;

    /* Initialize */
    constant1 = 545140134;
    constant2 = 13591409;
    constant3 = 640320;
    bits = clc_log2(10);
    precision = (dgts * bits) + 1;
    mpf_set_default_prec(precision);
    mpz_inits(v1, v2, v3, v4, v5, NULL);
    mpf_inits(res, tmp, V1, V2, V3, total, NULL);
    mpf_set_ui(total, 0);
    mpf_sqrt_ui(tmp, 10005);
    mpf_mul_ui(tmp, tmp, 426880);

    /* Get high-res time */
    clock_gettime(CLOCK_REALTIME, &start);

    /* Print total iterations and start computation of digits */
    printf("Total iterations: %lu\n\n", iters - 1);

#if defined(_OPENMP)
#pragma omp parallel for private(v1, v2, v3, v4, v5, V1, V2, V3, ti) reduction(+:total)
#endif

    /* Iterate and compute value using Chudnovsky Algorithm */
    for (i = 0x0; i < iters; i++) {
        ti = i * 3;
        mpz_fac_ui(v1, 6 * i);
        mpz_set_ui(v2, constant1);
        mpz_mul_ui(v2, v2, i);
        mpz_add_ui(v2, v2, constant2);
        mpz_fac_ui(v3, ti);
        mpz_fac_ui(v4, i);
        mpz_pow_ui(v4, v4, 3);
        mpz_ui_pow_ui(v5, constant3, ti);
        if ((1 & ti) == 1) { mpz_neg(v5, v5); }
        mpz_mul(v1, v1, v2);
        mpf_set_z(V1, v1);
        mpz_mul(v3, v3, v4);
        mpz_mul(v3, v3, v5);
        mpf_set_z(V2, v3);
        mpf_div(V3, V1, V2);
        mpf_add(total, total, V3);

        /* Print interations executed if debugging (I don't like spamming stdout unnecesarily) */
        #ifdef DEBUG
        printf("Iteration %lu of %lu successfully executed\n", i, iters - 1);
        #endif
    }

    /* Some final computations */
    mpf_ui_div(total, 1, total);
    mpf_mul(total, total, tmp);

    /* Get high-res time */
    clock_gettime(CLOCK_REALTIME, &end);

    /* Calculate and print time taken */
    double time_taken = (double)(end.tv_sec - start.tv_sec) + (double)(end.tv_nsec - start.tv_nsec) / 1E9;
    printf("Done!\n\nTime taken (seconds): %lf\n", time_taken);

    /* Store output */
    oput = mpf_get_str(NULL, &exponent, 10, dgts, total);

    /* Free up space consumed by variables */
    mpz_clears(v1, v2, v3, v4, v5, NULL);
    mpf_clears(res, tmp, V1, V2, V3, total, NULL);

    /* Return value */
    return oput;
}

/* Entry point of program */
int main(int argc, char *argv[]) {

    /* Set number of threads if compiling with -fopenmp */
#if defined(_OPENMP)
    omp_set_num_threads(8);
#endif

    /* Variable declaration and initialization */
    unsigned long how_many_digits = 10000;
    unsigned int base = 10;
    char *tmp_ptr;
    int pd = 0;
    int dd = 0;

    /* Try setting process priority to highest */
    int returnvalue = setpriority(PRIO_PROCESS, (id_t)0, -20);
    if (returnvalue == -1) { printf("WARN: Unable to max out priority. Did you not run this app as root?\n"); }

    /* Parse command line */
    if (argc == 3 && ((strcmp(argv[2], "--printdigits") == 0) || (strcmp(argv[2], "--nodigits") == 0) || (strcmp(argv[2], "--dumpdigits") == 0))) {
        how_many_digits = strtol(argv[1], &tmp_ptr, base);
        pd = (strcmp(argv[2], "--printdigits") == 0) ? 1 : 0;
        dd = (strcmp(argv[2], "--dumpdigits") == 0) ? 1 : 0; }

    /* Invalid command line parameters */
    else { fprintf(stderr, "Error: Invalid command-line arguments!\nUsage: pibench [digits] [parameter]\nParameter:\n--printdigits : Prints all digits on console\n--nodigits : Suppresses printing of digits on console\n--dumpdigits : Saves all the digits to a text file\n\nUsage example: pibench 50000 --printdigits\n"); exit(1); }

    /* Print introductory text */
    struct utsname uname_ptr;
    uname(&uname_ptr);
    printf("\n---------------------------------------------------------------");
    printf("\nPi Bench v1.0 beta (%s)\nBuild date: %s %s\n", uname_ptr.machine, build_date, build_time);
    printf("---------------------------------------------------------------\n\n");

    /* Check if digits isnt zero or below */
    if (how_many_digits < 1) { fprintf(stderr, "Error: Digit cannot be lower than 1\n"); exit(1); }

    /* Calculate digits of pi */
    printf("Computing %lu digits of PI...\n", how_many_digits);
    char *digits_of_pi = clc_pi(how_many_digits);

    /* Print the digits if user specified the --printdigits flag */
    if (pd == 1) {
        printf("Here are the digits:\n\n%.1s.%s\n", digits_of_pi, digits_of_pi + 1); }

    /* Save digits to text file if user specified the --dumpdigits flag */
    if (dd == 1) {
        FILE *file;
        if ((file = fopen("pidigits.txt", "w")) == NULL) {
            fprintf(stderr, "Error while opening file\n"); exit(-1); } else {
            fprintf(file, "%.1s.%s\n", digits_of_pi, digits_of_pi + 1);
            fclose(file); }
    }


    /* Print MD5 checksum */
    char *md5 = clc_md5(digits_of_pi);
    printf("MD5 checksum (for verification): %s\n", md5);

    /* Free the memory */
    free(digits_of_pi);

    /* Time to go! */
    printf("Goodbye!\n");
    return 0;
}

Исходный код доступен здесь .

Приветствуются любые предложения или советы!

11 голосов | спросил noobprohacker 28 Jpm1000000pmTue, 28 Jan 2014 15:37:21 +040014 2014, 15:37:21

3 ответа


6

Несколько заметок о некоторых вещах, о которых я не упоминал.

Компиляция:

  • Я изначально не мог скомпилировать программу с помощью команды в комментариях.

      

    /tmp/cc2H2h0a.o: В функции 'clc_pi':
      test.c :(. текст + 0x148): неопределенная ссылка на «clock_gettime»
      test.c :(. text + 0x2f0): undefined referenceto 'clock_gettime'
      collect2: ld возвращен 1 статус выхода

    Добавить -lrt в список библиотек, на которые вы ссылаетесь.

    // Compile using gcc : gcc -O2 -Wall -o pibench pibench.c -lgmp -lssl -lcrypto -lrt
    

Синтаксис:

  • Материал DEBUG отвлекает. Возможно, это временно, но если вы хотел оставить его, я предлагаю извлечь его:

    #include <stdarg.h>
    
    static inline void debug(const char *format, ...)
    {
    #ifdef DEBUG
        va_list ap;
        va_start(ap, format);
        vfprintf(stdout, format, ap);
        va_end(ap);
    #endif
    }
    

    и называя его:

    debug("Iteration %lu of %lu successfully executed\n", i, iters - 1);
    

    Если DEBUG не определено, встроенный debug будет пустым и будет во время компиляции исключается - он исчезает.

  • Поместите else в свою собственную строку.

    if (dd == 1) {
        FILE *file;
        if ((file = fopen("pidigits.txt", "w")) == NULL) {
            fprintf(stderr, "Error while opening file\n"); exit(-1); } else {
            fprintf(file, "%.1s.%s\n", digits_of_pi, digits_of_pi + 1);
            fclose(file); }
    }
    

    Когда вы используете его таким образом, его очень легко упускать из виду. Я почти переглянулся, изучая ваш код. На самом деле нет причин поместить его в свою линию, кроме как сохранить LOC, что вы могли бы сделать лучше в других местах.

    if (dd == 1) 
    {
        FILE *file;
        if ((file = fopen("pidigits.txt", "w")) == NULL) 
        {
            fprintf(stderr, "Error while opening file\n"); exit(-1); 
        } else {
            fprintf(file, "%.1s.%s\n", digits_of_pi, digits_of_pi + 1);
            fclose(file); 
        }
    }
    
  • Поместите все выражения в отдельные строки. Из Код завершен, второе издание , стр. 759:

      

    С заявлениями в своих строках, код читается сверху вниз, а не сверху вниз и слева направо. Когда вы ищете определенную строку кода, ваш глаз должен иметь возможность следить за левым полем кода. Он не должен опускаться в каждую строку только потому, что одна строка может содержать два утверждения.

  • Я бы использовал больше комментариев, особенно вокруг ваших OpenMP #pragma s и вызовов функций.

  • Определите i в вашем for петель. (С99)

    for (int i = 0x0; i < iters; i++)
    

Разное:

  • fopen(), широко используемые функции ввода-вывода файлов, которые вы используете, получили подтяжку лица на C11. Теперь он поддерживает новый эксклюзивный режим создания и открытия (“...x“). Новый режим ведет себя как O_CREAT|O_EXCL в POSIX и обычно используется для файлов блокировок. Семейство режимов “...x” включает следующие опции:

    • wx создать текстовый файл для записи с эксклюзивным доступом.

    • wbx создать двоичный файл для записи с эксклюзивным доступом.

    • w+x создать текстовый файл для обновления с эксклюзивным доступом.

    • w+bx или wb+x создать двоичный файл для обновления с эксклюзивным доступом.

    Открытие файла с любым из исключительных режимов выше завершается с ошибкой, если файл уже существует или не может быть создан. В противном случае файл создается с эксклюзивным (не общим) доступом. Кроме того, более безопасная версия fopen() называется fopen_s() также доступен. Это то, что я использовал бы в вашем коде, если бы был вами, но я оставлю это для вас, чтобы решить и изменить.

  • CLOCK_REALTIME представляет собой наилучшее предположение машины о текущих настенных часах, времени суток. Это означает, что CLOCK_REALTIME может перепрыгивать вперед и назад при изменении системного времени суток, включая NTP.

    CLOCK_MONOTONIC представляет собой абсолютное истекшее время настенных часов с некоторой произвольной неподвижной точки в прошлом. На него не влияют изменения в системном времени суток.

    Если вы хотите вычислить прошедшее времямежду двумя событиями, наблюдаемыми на одной машине без промежуточной перезагрузки, CLOCK_MONOTONIC - лучший вариант.

ответил syb0rg 19 FebruaryEurope/MoscowbWed, 19 Feb 2014 02:01:19 +0400000000amWed, 19 Feb 2014 02:01:19 +040014 2014, 02:01:19
5

Для начала не используйте __inline__. Он не переносится. Вместо этого используйте стандартный код C inline.

Затем используйте больше пробелов. Сложные вызовы и почти все условные обозначения должны входить в несколько строк, а операции должны быть разнесены. Также объясните, что вы делаете:

// original
snprintf(&(checksum[i*2]), 3, "%02x", (unsigned int)digest[i]);

// what it ought to look like
snprintf(                           // Describe
        &(checksum[i * 2]),         // what
        3,                          // you
        "%02x",                     // are
        (unsigned int)digest[i]     // doing
        );

и аналогично, условные обозначения:

// original
    dd = (strcmp(argv[2], "--dumpdigits") == 0) ? 1 : 0; }

/* Invalid command line parameters */
else { fprintf(stderr, "Error: Invalid command-line arguments!\nUsage: pibench [digits] [parameter]\nParameter:\n--printdigits : Prints all digits on console\n--nodigits : Suppresses printing of digits on console\n--dumpdigits : Saves all the digits to a text file\n\nUsage example: pibench 50000 --printdigits\n"); exit(1); }



// what it ought to look like
    dd = (strcmp(argv[2], "--dumpdigits") == 0) ? 1 : 0;
} else {
    fputs (
            "Error: Invalid command-line arguments!\n"
            "Usage: pibench [digits] [parameter]\n"
            "Parameters:\n"
            "\t--printdigits : Prints all digits on console\n"
            "\t--nodigits : Suppresses printing of digits on console\n"
            "\t--dumpdigits : Saves all the digits to a text file\n\n"
            "Usage example: pibench 50000 --printdigits\n",
            stderr
            );

    return 1;
}

Теперь я сделал еще несколько вещей:

  • Несколько строк на нескольких строках будут автоматически соединены - используйте их.
  • При объяснении параметров используйте жесткие вкладки.
  • Не используйте printf(), если вам это не нужно. Вместо этого используйте fputs().
  • Не используйте exit (), если вы не используете функцию return void. Вместо этого используйте return 1;.

Наконец, используйте char **argv. Это ничего не меняет, и любой, кто стоит их соли, знает об обоих, но по крайней мере для меня это кажется чище:

// original
int main(int argc, char *argv[]) {

// what it should look like
int main (int argc, char **argv) {

Кроме этого, ваш код выглядит довольно чистым. Молодцы!

ответил haneefmubarak 18 FebruaryEurope/MoscowbTue, 18 Feb 2014 05:59:17 +0400000000amTue, 18 Feb 2014 05:59:17 +040014 2014, 05:59:17
5

В верхней части комментария haneefmubarak я хотел бы указать несколько вещей, которые мне не очень нравятся:

Переменные имена

Я думаю:

mpz_t v1, v2, v3, v4, v5;
mpf_t V1, V2, V3, total, tmp, res;

говорит все. Не только переменные - это бессмысленные имена, но, кроме того, дело имеет значение. Трудно каждому прочитать это, не запутавшись.

Кроме того, unsigned long how_many_digits = 10000; следует, вероятно, назвать более понятным, например nb_of_digits или digits_number.

Объявление переменной

Вы объявляете всю переменную в самом начале функции. Не только это делает функцию еще дольше, чем она должна быть, но, кроме того, она усложняет чтение без какой-либо добавленной стоимости.

Не нужно идти туда и обратно с десятками строк в коде, чтобы увидеть, как эта переменная окончательно определяется /используется /объявляется, если все может быть сделано всего в нескольких строках.

Кроме того, поскольку вы пытаетесь определить переменные меньше, где это полезно, вы заметите, что делаете то, что не очень полезно: например, в unsigned long how_many_digits = 10000;, мы никогда не будем использовать значение 10000, но все же вам нужно его написать, компилятор должен прочитайте его и даже стоите, и я должен его прочитать.

Раннее возвращение

Вместо того, чтобы делать

if (A) { foo(); } else { bar(); return; }  foobar()

вы можете просто сделать:

if (!A) { bar(); return; } foo(); foobar()

, чтобы сделать вещи намного более линейными.

Автоматическая оценка true /false

Вы можете использовать тот факт, что в C нулевой результат равен false и не равен нулю.

Вот как это выглядит после учета этих простых комментариев:

/*
 *
 * Simple PI Benchmarking tool
 * Author: Suyash Srijan
 * Email: [email protected]
 *
 * This program calculates how much time your CPU takes to compute n digits of PI using Chudnovsky Algorithm
 * (http://en.wikipedia.org/wiki/Chudnovsky_algorithm) and uses the GNU Multiple Precision Arithmetic Library
 * for computation.
 *
 * For verification of digits, you can download the digits from here: http://piworld.calico.jp/estart.html
 *
 * It's a single threaded program but you can compile it with OpenMP support to enable parallelization.
 * WARN: OpenMP support is experimental
 *
 * Compile using gcc : gcc -O2 -Wall -o pibench pibench.c -lgmp -lssl -lcrypto
 * Compile using gcc (with OpenMP): gcc -O2 -Wall -o pibench pibench.c -lgmp -lssl -lcrypto -fopenmp
 *
 */

#include <gmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/resource.h>
#include <sys/utsname.h>
#include <openssl/md5.h>

/* Import OpenMP header if compiling with -fopenmp */
#if defined(_OPENMP)
#include <omp.h>
#endif

/* You can't compile this on Windows */
#ifdef _WIN32
#error >>> Fatal: It is not possible to compile this program on Windows <<<
#endif

/* Build timestamp */
#define build_time __TIME__
#define build_date __DATE__

/* Calculate log to the base 2 using GCC's bit scan reverse intrinsic */
__inline__ unsigned int clc_log2(const unsigned int num) {
    return ((num <= 1) ? 0 : 32 - (__builtin_clz(num - 1)));
}

/* Calculate MD5 checksum for verification */
__inline__ char *clc_md5(const char *string) {
    MD5_CTX context;
    MD5_Init(&context);
    MD5_Update(&context, string, strlen(string));

    unsigned char digest[16];
    MD5_Final(digest, &context);

    char *checksum = (char*)malloc(33);
    for (int i = 0; i < 16; ++i) {
        snprintf(&(checksum[i*2]), 3, "%02x", (unsigned int)digest[i]);
    }
    return checksum;
}

/* Calculate pi digits main function */
__inline__ char *clc_pi(unsigned long dgts)
{
    double bits = clc_log2(10);
    unsigned long precision = (dgts * bits) + 1;
    mpf_set_default_prec(precision);
    mpz_t v1, v2, v3, v4, v5;
    mpf_t V1, V2, V3, total, tmp, res;
    mpz_inits(v1, v2, v3, v4, v5, NULL);
    mpf_inits(res, tmp, V1, V2, V3, total, NULL);
    mpf_set_ui(total, 0);
    mpf_sqrt_ui(tmp, 10005);
    mpf_mul_ui(tmp, tmp, 426880);

    /* Get high-res time */
    struct timespec start;
    clock_gettime(CLOCK_REALTIME, &start);

    /* Print total iterations and start computation of digits */
    unsigned long iters = (dgts / 15) + 1;
    printf("Total iterations: %lu\n\n", iters - 1);

#if defined(_OPENMP)
#pragma omp parallel for private(v1, v2, v3, v4, v5, V1, V2, V3, ti) reduction(+:total)
#endif

    /* Iterate and compute value using Chudnovsky Algorithm */
    for (unsigned long int i = 0x0; i < iters; i++) {
        unsigned long int ti = i * 3;
        unsigned long int constant1 = 545140134;
        unsigned long int constant2 = 13591409;
        unsigned long int constant3 = 640320;

        mpz_fac_ui(v1, 6 * i);
        mpz_set_ui(v2, constant1);
        mpz_mul_ui(v2, v2, i);
        mpz_add_ui(v2, v2, constant2);
        mpz_fac_ui(v3, ti);
        mpz_fac_ui(v4, i);
        mpz_pow_ui(v4, v4, 3);
        mpz_ui_pow_ui(v5, constant3, ti);
        if ((1 & ti) == 1) { mpz_neg(v5, v5); }
        mpz_mul(v1, v1, v2);
        mpf_set_z(V1, v1);
        mpz_mul(v3, v3, v4);
        mpz_mul(v3, v3, v5);
        mpf_set_z(V2, v3);
        mpf_div(V3, V1, V2);
        mpf_add(total, total, V3);

        /* Print interations executed if debugging (I don't like spamming stdout unnecesarily) */
#ifdef DEBUG
        printf("Iteration %lu of %lu successfully executed\n", i, iters - 1);
#endif
    }

    /* Some final computations */
    mpf_ui_div(total, 1, total);
    mpf_mul(total, total, tmp);

    /* Get high-res time */
    struct timespec end;
    clock_gettime(CLOCK_REALTIME, &end);

    /* Calculate and print time taken */
    double time_taken = (double)(end.tv_sec - start.tv_sec) + (double)(end.tv_nsec - start.tv_nsec) / 1E9;
    printf("Done!\n\nTime taken (seconds): %lf\n", time_taken);

    /* Store output */
    mp_exp_t exponent;
    char * oput = mpf_get_str(NULL, &exponent, 10, dgts, total);

    /* Free up space consumed by variables */
    mpz_clears(v1, v2, v3, v4, v5, NULL);
    mpf_clears(res, tmp, V1, V2, V3, total, NULL);

    /* Return value */
    return oput;
}

/* Entry point of program */
int main(int argc, char *argv[]) {
    /* Set number of threads if compiling with -fopenmp */
#if defined(_OPENMP)
    omp_set_num_threads(8);
#endif

    /* Try setting process priority to highest */
    if (setpriority(PRIO_PROCESS, (id_t)0, -20) == -1) {
        printf("WARN: Unable to max out priority. Did you not run this app as root?\n");
    }

    /* Parse command line */
    if (argc != 3 || (strcmp(argv[2], "--printdigits") && strcmp(argv[2], "--nodigits") && strcmp(argv[2], "--dumpdigits"))) {
        fprintf(stderr, "Error: Invalid command-line arguments!\nUsage: pibench [digits] [parameter]\nParameter:\n--printdigits : Prints all digits on console\n--nodigits : Suppresses printing of digits on console\n--dumpdigits : Saves all the digits to a text file\n\nUsage example: pibench 50000 --printdigits\n");
        return 1;
    }

    char *tmp_ptr;
    unsigned long nb_digits = strtol(argv[1], &tmp_ptr, base);
    int print_digits = strcmp(argv[2], "--printdigits");
    int dump_digits = strcmp(argv[2], "--dumpdigits");

    /* Invalid command line parameters */

    /* Print introductory text */
    struct utsname uname_ptr;
    uname(&uname_ptr);
    printf("\n---------------------------------------------------------------");
    printf("\nPi Bench v1.0 beta (%s)\nBuild date: %s %s\n", uname_ptr.machine, build_date, build_time);
    printf("---------------------------------------------------------------\n\n");

    /* Check if digits isnt zero or below */
    if (nb_digits < 1) { fprintf(stderr, "Error: Digit cannot be lower than 1\n"); exit(1); }

    /* Calculate digits of pi */
    printf("Computing %lu digits of PI...\n", nb_digits);
    char *digits_of_pi = clc_pi(nb_digits);

    /* Print the digits if user specified the --printdigits flag */
    if (print_digits) {
        printf("Here are the digits:\n\n%.1s.%s\n", digits_of_pi, digits_of_pi + 1);
    }

    /* Save digits to text file if user specified the --dumpdigits flag */
    if (dump_digits) {
        FILE *file;
        if ((file = fopen("pidigits.txt", "w"))) {
            fprintf(file, "%.1s.%s\n", digits_of_pi, digits_of_pi + 1);
            fclose(file);
        } else {
            fprintf(stderr, "Error while opening file\n");
            return -1;
        }
    }


    /* Print MD5 checksum */
    char *md5 = clc_md5(digits_of_pi);
    printf("MD5 checksum (for verification): %s\n", md5);

    /* Free the memory */
    free(digits_of_pi);

    /* Time to go! */
    printf("Goodbye!\n");
    return 0;
}
ответил Josay 18 FebruaryEurope/MoscowbTue, 18 Feb 2014 14:51:06 +0400000000pmTue, 18 Feb 2014 14:51:06 +040014 2014, 14:51:06

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132