2012-03-01 47 views
5

Tôi đang làm việc trên một số quy trình chuyển đổi văn bản phân tích giá trị thời gian ở các định dạng khác nhau trong Ruby. Thường trình này ngày càng phức tạp và hiện tôi đang thử nghiệm một cách tiếp cận tốt hơn cho vấn đề này.tại sao ruby ​​scanf quá chậm?

Tôi hiện đang thử nghiệm cách sử dụng scanf. Tại sao? Tôi luôn luôn nghĩ rằng nhanh hơn regex, nhưng điều gì đã xảy ra với Ruby? Nó chậm hơn nhiều!

Tôi đang làm gì sai?

Lưu ý: Tôi đang sử dụng ruby-1.9.2-p290 [x86_64] (MRI)

đầu tiên của Ruby kiểm tra:

require "scanf" 
require 'benchmark' 

def duration_in_seconds_regex(duration) 
    if duration =~ /^\d{2,}\:\d{2}:\d{2}$/ 
    h, m, s = duration.split(":").map{ |n| n.to_i } 
    h * 3600 + m * 60 + s 
    end 
end 

def duration_in_seconds_scanf(duration) 
    a = duration.scanf("%d:%d:%d") 
    a[0] * 3600 + a[1] * 60 + a[2] 
end 

n = 500000 
Benchmark.bm do |x| 
    x.report { for i in 1..n; duration_in_seconds_scanf("00:10:30"); end } 
end 

Benchmark.bm do |x| 
    x.report { for i in 1..n; duration_in_seconds_regex("00:10:30"); end } 
end 

Đây là những gì tôi đã sử dụng scanf đầu tiên và một regex thứ hai :

 user  system  total  real 
    95.020000 0.280000 95.300000 (96.364077) 
     user  system  total  real 
    2.820000 0.000000 2.820000 ( 2.835170) 

thử nghiệm thứ hai sử dụng C:

#include <stdio.h> 
#include <stdlib.h> 
#include <time.h> 
#include <sys/types.h> 
#include <string.h> 
#include <regex.h> 

char *regexp(char *string, char *patrn, int *begin, int *end) { 
    int i, w = 0, len; 
    char *word = NULL; 
    regex_t rgT; 
    regmatch_t match; 
    regcomp(&rgT, patrn, REG_EXTENDED); 
    if ((regexec(&rgT, string, 1, &match, 0)) == 0) { 
     *begin = (int) match.rm_so; 
     *end = (int) match.rm_eo; 
     len = *end - *begin; 
     word = malloc(len + 1); 
     for (i = *begin; i<*end; i++) { 
      word[w] = string[i]; 
      w++; 
     } 
     word[w] = 0; 
    } 
    regfree(&rgT); 
    return word; 
} 

int main(int argc, char** argv) { 
    char * str = "00:01:30"; 
    int h, m, s; 
    int i, b, e; 
    float start_time, end_time, time_elapsed; 
    regex_t regex; 
    regmatch_t * pmatch; 
    char msgbuf[100]; 
    char *pch; 
    char *str2; 
    char delims[] = ":"; 
    char *result = NULL; 

    start_time = (float) clock()/CLOCKS_PER_SEC; 
    for (i = 0; i < 500000; i++) { 
     if (sscanf(str, "%d:%d:%d", &h, &m, &s) == 3) { 
      s = h * 3600L + m * 60L + s; 
     } 
    } 
    end_time = (float) clock()/CLOCKS_PER_SEC; 
    time_elapsed = end_time - start_time; 
    printf("sscanf_time (500k iterations): %.4f", time_elapsed); 

    start_time = (float) clock()/CLOCKS_PER_SEC; 
    for (i = 0; i < 500000; i++) { 
     char * match = regexp(str, "[0-9]{2,}:[0-9]{2}:[0-9]{2}", &b, &e); 
     if (strcmp(match, str) == 0) { 
      str2 = (char*) malloc(sizeof (str)); 
      strcpy(str2, str); 
      h = strtok(str2, delims); 
      m = strtok(NULL, delims); 
      s = strtok(NULL, delims); 
      s = h * 3600L + m * 60L + s; 
     } 
    } 
    end_time = (float) clock()/CLOCKS_PER_SEC; 
    time_elapsed = end_time - start_time; 
    printf("\n\nregex_time (500k iterations): %.4f", time_elapsed); 

    return (EXIT_SUCCESS); 
} 

Kết quả mã C rõ ràng là nhanh hơn, và kết quả regex là chậm hơn so với scanf kết quả như mong đợi:

sscanf_time (500k iterations): 0.1774 

regex_time (500k iterations): 3.9692 

Rõ ràng là thời gian C chạy nhanh hơn, vì vậy xin đừng nhận xét rằng Ruby giải thích và các công cụ như thế xin vui lòng.

Đây là số liên quan gist.

+0

Bạn không biên dịch lại biểu thức mọi lần lặp trong C? Tôi không nghĩ Ruby làm thế. Tôi muốn được quan tâm để xem kết quả C nếu bạn biên dịch biểu thức chỉ một lần. Ngoài ra, tại sao bạn thậm chí sử dụng một chia? Bạn đang khớp chuỗi để bạn có thể nắm bắt trực tiếp các giá trị, mà không cần thao tác thêm trên chuỗi. – Qtax

+0

Vâng tôi đang biên dịch lại, nó thậm chí còn nhanh hơn thế nữa nhưng đôi khi tôi cần thay đổi điểm kinh nghiệm. – AndreDurao

+0

Sau đó, bạn chỉ cần biên dịch lại khi nó được thay đổi. Nhưng tôi chỉ muốn xem các con số. ;-) – Qtax

Trả lời

4

Vấn đề không phải là nó được giải thích, nhưng mọi thứ trong Ruby là một đối tượng. Bạn có thể khám phá "scanf.rb" trong bản phân phối Ruby của bạn và so sánh nó với việc thực hiện scanf trong C.

Ruby thực hiện quét dựa trên kết hợp RegExp. Mỗi nguyên tử như "% d" là một đối tượng trong ruby, trong khi nó chỉ là một trường hợp trong C. Vì vậy, với tâm trí của tôi, lý do của thời gian thực hiện như vậy là rất nhiều phân bổ đối tượng/deallocation.

+0

Tôi nghĩ rằng scanf sử dụng thực hiện bản địa giống như openssl và những người khác yêu cầu tệp .so – AndreDurao

2

Giả sử MRI: scanf được viết bằng Ruby (scanf.rb) dường như cách đây 10 năm và không bao giờ chạm vào (và nó trông phức tạp!). split, map và regex được triển khai trong tối ưu hóa rất nhiều C.

Các vấn đề liên quan