Refactor Time#xmlschema
And refine uncommon date cases. # Iteration per second (i/s) | |compare-ruby|built-ruby| |:---------------------------|-----------:|---------:| |time.xmlschema | 5.020M| 14.192M| | | -| 2.83x| |utc_time.xmlschema | 6.454M| 15.331M| | | -| 2.38x| |time.xmlschema(6) | 4.216M| 10.043M| | | -| 2.38x| |utc_time.xmlschema(6) | 5.486M| 10.592M| | | -| 1.93x| |time.xmlschema(9) | 4.294M| 10.340M| | | -| 2.41x| |utc_time.xmlschema(9) | 4.784M| 10.909M| | | -| 2.28x| |fraction_sec.xmlschema(10) | 366.982k| 3.406M| | | -| 9.28x| |future_time.xmlschema | 994.595k| 15.853M| | | -| 15.94x|
This commit is contained in:
parent
b811a9a097
commit
7be1fafe58
Notes:
git
2024-09-23 06:17:39 +00:00
@ -14,6 +14,8 @@ prelude: |
|
||||
end
|
||||
time = Time.now
|
||||
utc_time = Time.now.utc
|
||||
fraction_sec = Time.at(123456789.quo(9999999999)).getlocal("+09:00")
|
||||
future_time = Time.utc(10000)
|
||||
benchmark:
|
||||
- time.xmlschema
|
||||
- utc_time.xmlschema
|
||||
@ -21,3 +23,5 @@ benchmark:
|
||||
- utc_time.xmlschema(6)
|
||||
- time.xmlschema(9)
|
||||
- utc_time.xmlschema(9)
|
||||
- fraction_sec.xmlschema(10)
|
||||
- future_time.xmlschema
|
||||
|
@ -18927,6 +18927,7 @@ time.$(OBJEXT): {$(VPATH)}thread_native.h
|
||||
time.$(OBJEXT): {$(VPATH)}time.c
|
||||
time.$(OBJEXT): {$(VPATH)}timev.h
|
||||
time.$(OBJEXT): {$(VPATH)}timev.rbinc
|
||||
time.$(OBJEXT): {$(VPATH)}util.h
|
||||
time.$(OBJEXT): {$(VPATH)}vm_core.h
|
||||
time.$(OBJEXT): {$(VPATH)}vm_opts.h
|
||||
transcode.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
|
125
time.c
125
time.c
@ -42,6 +42,7 @@
|
||||
#include "internal/time.h"
|
||||
#include "internal/variable.h"
|
||||
#include "ruby/encoding.h"
|
||||
#include "ruby/util.h"
|
||||
#include "timev.h"
|
||||
|
||||
#include "builtin.h"
|
||||
@ -570,6 +571,9 @@ num_exact(VALUE v)
|
||||
|
||||
/* time_t */
|
||||
|
||||
/* TIME_SCALE should be 10000... */
|
||||
static const int TIME_SCALE_NUMDIGITS = rb_strlen_lit(STRINGIZE(TIME_SCALE)) - 1;
|
||||
|
||||
static wideval_t
|
||||
rb_time_magnify(wideval_t w)
|
||||
{
|
||||
@ -2643,10 +2647,6 @@ time_init_parse(rb_execution_context_t *ec, VALUE time, VALUE str, VALUE zone, V
|
||||
}
|
||||
if (!NIL_P(subsec)) {
|
||||
/* subseconds is the last using ndigits */
|
||||
static const size_t TIME_SCALE_NUMDIGITS =
|
||||
/* TIME_SCALE should be 10000... */
|
||||
rb_strlen_lit(STRINGIZE(TIME_SCALE)) - 1;
|
||||
|
||||
if (ndigits < TIME_SCALE_NUMDIGITS) {
|
||||
VALUE mul = rb_int_positive_pow(10, TIME_SCALE_NUMDIGITS - ndigits);
|
||||
subsec = rb_int_mul(subsec, mul);
|
||||
@ -5232,51 +5232,79 @@ time_xmlschema(int argc, VALUE *argv, VALUE time)
|
||||
GetTimeval(time, tobj);
|
||||
MAKE_TM(time, tobj);
|
||||
|
||||
long year = -1;
|
||||
const long size_after_year = sizeof("-MM-DDTHH:MM:SS+ZH:ZM") + fraction_digits
|
||||
+ (fraction_digits > 0);
|
||||
VALUE str;
|
||||
char *ptr;
|
||||
|
||||
# define fill_digits_long(len, prec, n) \
|
||||
for (int fill_it = 1, written = snprintf(ptr, len, "%0*ld", prec, n); \
|
||||
fill_it; ptr += written, fill_it = 0)
|
||||
|
||||
if (FIXNUM_P(tobj->vtm.year)) {
|
||||
year = FIX2LONG(tobj->vtm.year);
|
||||
}
|
||||
if (RB_UNLIKELY(year > 9999 || year < 0 || fraction_digits > 9)) {
|
||||
// Slow path for uncommon dates.
|
||||
VALUE format = rb_utf8_str_new_cstr("%FT%T");
|
||||
if (fraction_digits > 0) {
|
||||
rb_str_catf(format, ".%%#%ldN", fraction_digits);
|
||||
long year = FIX2LONG(tobj->vtm.year);
|
||||
int year_width = (year < 0) + rb_strlen_lit("YYYY");
|
||||
int w = (year >= -9999 && year <= 9999 ? year_width : (year < 0) + DECIMAL_SIZE_OF(year));
|
||||
str = rb_usascii_str_new(0, w + size_after_year);
|
||||
ptr = RSTRING_PTR(str);
|
||||
fill_digits_long(w + 1, year_width, year) {
|
||||
if (year >= -9999 && year <= 9999) {
|
||||
RUBY_ASSERT(written == year_width);
|
||||
}
|
||||
else {
|
||||
RUBY_ASSERT(written >= year_width);
|
||||
RUBY_ASSERT(written <= w);
|
||||
}
|
||||
}
|
||||
rb_str_cat_cstr(format, TZMODE_UTC_P(tobj) ? "Z" : "%:z");
|
||||
return rb_funcallv(time, rb_intern("strftime"), 1, &format);
|
||||
}
|
||||
else {
|
||||
str = rb_int2str(tobj->vtm.year, 10);
|
||||
rb_str_modify_expand(str, size_after_year);
|
||||
ptr = RSTRING_END(str);
|
||||
}
|
||||
|
||||
long buf_size = sizeof("YYYY-MM-DDTHH:MM:SS+ZH:ZM") + fraction_digits + (fraction_digits > 0 ? 1 : 0);
|
||||
|
||||
VALUE str = rb_str_buf_new(buf_size);
|
||||
rb_enc_associate_index(str, rb_utf8_encindex());
|
||||
|
||||
char *ptr = RSTRING_PTR(str);
|
||||
char *start = ptr;
|
||||
int written = snprintf(
|
||||
ptr,
|
||||
sizeof("YYYY-MM-DDTHH:MM:SS"),
|
||||
"%04ld-%02d-%02dT%02d:%02d:%02d",
|
||||
year,
|
||||
tobj->vtm.mon,
|
||||
tobj->vtm.mday,
|
||||
tobj->vtm.hour,
|
||||
tobj->vtm.min,
|
||||
tobj->vtm.sec
|
||||
);
|
||||
RUBY_ASSERT(written == sizeof("YYYY-MM-DDTHH:MM:SS") - 1);
|
||||
ptr += written;
|
||||
# define fill_2(c, n) (*ptr++ = c, *ptr++ = '0' + (n) / 10, *ptr++ = '0' + (n) % 10)
|
||||
fill_2('-', tobj->vtm.mon);
|
||||
fill_2('-', tobj->vtm.mday);
|
||||
fill_2('T', tobj->vtm.hour);
|
||||
fill_2(':', tobj->vtm.min);
|
||||
fill_2(':', tobj->vtm.sec);
|
||||
|
||||
if (fraction_digits > 0) {
|
||||
long nsec = NUM2LONG(mulquov(tobj->vtm.subsecx, INT2FIX(1000000000), INT2FIX(TIME_SCALE)));
|
||||
long subsec = nsec / (long)pow(10, 9 - fraction_digits);
|
||||
|
||||
*ptr = '.';
|
||||
ptr++;
|
||||
|
||||
written = snprintf(ptr, fraction_digits + 1, "%0*ld", (int)fraction_digits, subsec); // Always allow to write \0
|
||||
RUBY_ASSERT(written > 0);
|
||||
ptr += written;
|
||||
VALUE subsecx = tobj->vtm.subsecx;
|
||||
long subsec;
|
||||
int digits = -1;
|
||||
*ptr++ = '.';
|
||||
if (fraction_digits <= TIME_SCALE_NUMDIGITS) {
|
||||
digits = TIME_SCALE_NUMDIGITS - (int)fraction_digits;
|
||||
}
|
||||
else {
|
||||
long w = fraction_digits - TIME_SCALE_NUMDIGITS; /* > 0 */
|
||||
subsecx = mulv(subsecx, rb_int_positive_pow(10, (unsigned long)w));
|
||||
if (!RB_INTEGER_TYPE_P(subsecx)) { /* maybe Rational */
|
||||
subsecx = rb_Integer(subsecx);
|
||||
}
|
||||
if (FIXNUM_P(subsecx)) digits = 0;
|
||||
}
|
||||
if (digits >= 0 && fraction_digits < INT_MAX) {
|
||||
subsec = NUM2LONG(subsecx);
|
||||
if (digits > 0) subsec /= (long)pow(10, digits);
|
||||
fill_digits_long(fraction_digits + 1, (int)fraction_digits, subsec) {
|
||||
RUBY_ASSERT(written == (int)fraction_digits);
|
||||
}
|
||||
}
|
||||
else {
|
||||
subsecx = rb_int2str(subsecx, 10);
|
||||
long len = RSTRING_LEN(subsecx);
|
||||
if (fraction_digits > len) {
|
||||
memset(ptr, '0', fraction_digits - len);
|
||||
}
|
||||
else {
|
||||
len = fraction_digits;
|
||||
}
|
||||
ptr += fraction_digits;
|
||||
memcpy(ptr - len, RSTRING_PTR(subsecx), len);
|
||||
}
|
||||
}
|
||||
|
||||
if (TZMODE_UTC_P(tobj)) {
|
||||
@ -5285,14 +5313,13 @@ time_xmlschema(int argc, VALUE *argv, VALUE time)
|
||||
}
|
||||
else {
|
||||
long offset = NUM2LONG(rb_time_utc_offset(time));
|
||||
*ptr++ = offset < 0 ? '-' : '+';
|
||||
char sign = offset < 0 ? '-' : '+';
|
||||
if (offset < 0) offset = -offset;
|
||||
int offset_hours = (int)(offset / 3600);
|
||||
int offset_minutes = (int)(offset % 3600 / 60);
|
||||
written = snprintf(ptr, sizeof("ZH:ZM"), "%02d:%02d", offset_hours, offset_minutes);
|
||||
RUBY_ASSERT(written == sizeof("ZH:ZM") - 1, "%d[%.*s]", written, written, ptr);
|
||||
ptr += written;
|
||||
offset /= 60;
|
||||
fill_2(sign, offset / 60);
|
||||
fill_2(':', offset % 60);
|
||||
}
|
||||
const char *const start = RSTRING_PTR(str);
|
||||
rb_str_set_len(str, ptr - start); // We could skip coderange scanning as we know it's full ASCII.
|
||||
return str;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user