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