比尔盖子 博客

Python比C快1000倍?

之前看见C代码连蒙带猜,也能编译通过很多东西,今天(没错,今天)刚刚开始学习C,才发现:

1. C没有想象中的那么可怕;
2. C和想像中的可怕程度差不多。

于是,便现学现用,打算把之前用Python写的凯撒加密练习用C重写。结果发现了关键的问题,数组的大小是静态的,根本无法根据用户输入的字符实现动态分配内存;若要动态也可以,方法对初学者难以理解。

后来,想到了方便的GNU Readline库可以自动处理键盘快捷键,便看了看文档,发现只要将一个指针指向它的返回值,就可以获得一段有字符串的内存了,程序完成,结果,速度比Python要至少慢1000倍,难道是Readline效率低下?于是做了一个试验。


程序1: caesar.c

#include <string.h>
#define MAX 99902

int main(void) {
    char plain[MAX];
    char letter;
    int value;
    int index;

    printf("Please input your plain text: ");
    fgets(plain, MAX, stdin);
    printf("Please input your key (included negatives): ");
    scanf("%i", &value);

    for (index = 0; index < strlen(plain); index++) {
        letter = plain[index];

        if (letter >= 'A' && letter <= 'Z') {
            fprintf(stderr, "%c", (letter - 'A' + value) % 26 + 'A');
        }

        else if (letter >= 'a' && letter <= 'z') {
            fprintf(stderr, "%c", (letter - 'a' + value) % 26 + 'a');
        }
        else {
            fprintf(stderr, "%c", letter);
        }
    }
}

编译参数:

gcc caesar.c -o caesar -O2

程序2 caesar_readline.c:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <readline/readline.h>

int main(void) {
    char * plain;
    char letter;
    int value;
    int index;

    plain = readline("Please input your plain text: ");
    printf("Please input your key (included negatives): ");
    scanf("%i", &value);

    for (index = 0; index < strlen(plain); index++) {
        letter = plain[index];

        if (letter >= 'A' && letter <= 'Z') {
            fprintf(stderr, "%c", (letter - 'A' + value) % 26 + 'A');
        }

        else if (letter >= 'a' && letter <= 'z') {
            fprintf(stderr, "%c", (letter - 'a' + value) % 26 + 'a');
        }
        else {
            fprintf(stderr, "%c", letter);
        }
    }
    fprintf(stderr, "\n");
    free(plain);
}

编译参数:

gcc caesar_readline.c -o caesar_readline -O2 -lreadline

程序3 caesar.py:

import readline
import sys

plain = input("Please input your plain text: ")
value = int(input("Please input your key (included negatives): "))

for letter in plain:
    if ord(letter) >= ord('A') and ord(letter) <= ord('Z'):
        print("%s" % chr(((ord(letter) - ord('A') + value) % 26 + ord('A'))), end='', file=sys.stderr)
    elif ord(letter) >= ord('a') and ord(letter) <= ord('z'):
        print("%s" % chr(((ord(letter) - ord('a') + value) % 26 + ord('a'))), end='', file=sys.stderr)
    else:
        print("%s" % letter, end='', file=sys.stderr)

print("", file=sys.stderr)

版本: Python 3.2.3

资源1: string
大量随机字符,使用以下代码生成:

import random
def getstr():
    temp = chr(random.randint(65, 126))
    return temp

for i in range(1,101):
    print(getstr(), end='')

然后在string文件结尾加一行,写16

跑分开始!

biergaizi@localhost ~/learning_c/test $ time ./caesar < string 2> c_result > /dev/null

real    0m1.205s
user    0m1.126s
sys     0m0.076s

biergaizi@localhost ~/learning_c/test $ time ./caesar_readline < string 2> c_readline_result > /dev/null

real    2m31.212s
user    2m30.776s
sys     0m0.165s

biergaizi@localhost ~/learning_c/test $ time python caesar.py < string 2> python_result > /dev/null

real    0m0.314s
user    0m0.309s
sys     0m0.004s

输出相同吗?

biergaizi@localhost ~/learning_c/test $ md5sum *result
550963d7440ea4ef23e426147995f38e  c_readline_result
550963d7440ea4ef23e426147995f38e  c_result
550963d7440ea4ef23e426147995f38e  python_result

可以看到,使用readline库后,程序的速度立刻慢了下来,这是为什么?难道readline库效率如此底下?在去掉’> /dev/null’后,发现弹出一个提示符,不停的自动输入string里面的内容,晕,原来是终端输入的速度在影响它……

因此,问题还是出在readline上,显然,把终端输入的时间计算进程序时间也是不公平的,来看sys时间:带不带readline都差不多。来看real时间:第一个C程序用了1.205s完成,而Python仅仅用了0.314s,这不科学啊?!Python同样用了readline库,为何不受影响?如果抛开这个问题,那为什么Python的运行时间依然是最短的?

P.S:其实这些程序处理负数都存在bug,还没能找到一个通用公式解决。

Categories: IT生活, 代码如诗

巧用位运算 » « 再次更换浏览器 —— 我的浏览器史

9 Comments

  1. readline 是个全功能的编辑器了,在它的man page最后有条 😉
    BUGS
    It’s too big and too slow.

  2. letter = *plain++:
    while(letter)
    {
    ……
    letter = *plain++:
    }
    // free的时候需要释放另存的头指针

  3. strlen(plain);
    用变量保存plain的长度

  4. c计算时间的一般都用clock()函数。

发表评论

Your email address will not be published.

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

Copyright © 2023 比尔盖子 博客

Theme by Anders NorenUp ↑