Changes On Branch a011e0dddc8085d1
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch sebres-8-6-clock-speedup-cr2 Through [a011e0dddc] Excluding Merge-Ins

This is equivalent to a diff from 0c840ffac5 to a011e0dddc

2018-05-29
17:03
Added max permitted threshold (buffer size > result size) in percent, to directly return the buffer ... check-in: 2f98e44d4b user: sebres tags: sebres-8-6-clock-speedup-cr2
17:02
further optimization: better cache for GMT-timezone + minimize (re)allocation of buffers check-in: a011e0dddc user: sebres tags: sebres-8-6-clock-speedup-cr2
17:01
code review: micro optimizations check-in: f5e76e52be user: sebres tags: sebres-8-6-clock-speedup-cr2
16:40
tests-perf\test-performance.tcl: ported from sebres-8-6-event-perf-branch (common test performance f... check-in: a08a2e912f user: sebres tags: sebres-8-6-clock-speedup-cr2
2017-10-20
12:36
Merge core-8-6-branch (execpt file win/makefile.vc) Closed-Leaf check-in: 0c840ffac5 user: dgp tags: sebres-8-6-clock-speedup-cr1
2017-10-19
09:28
Oops; put the code in the wrong place. Mixins have priority when deciding method visibility. check-in: 4140046408 user: dkf tags: core-8-6-branch
2017-08-08
15:19
fixed overflow of year (resp. julianday), closes ticket [16e4fc3096]; test cases adjusted. check-in: 3efed18ef8 user: sebres tags: sebres-8-6-clock-speedup-cr1

Changes to generic/tclClock.c.
234
235
236
237
238
239
240

241
242
243
244
245
246
247
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248







+







    data->yearOfCenturySwitch = ClockDefaultCenturySwitch;

    data->systemTimeZone = NULL;
    data->systemSetupTZData = NULL;
    data->gmtSetupTimeZoneUnnorm = NULL;
    data->gmtSetupTimeZone = NULL;
    data->gmtSetupTZData = NULL;
    data->gmtTZName = NULL;
    data->lastSetupTimeZoneUnnorm = NULL;
    data->lastSetupTimeZone = NULL;
    data->lastSetupTZData = NULL;
    data->prevSetupTimeZoneUnnorm = NULL;
    data->prevSetupTimeZone = NULL;
    data->prevSetupTZData = NULL;

306
307
308
309
310
311
312

313
314
315
316
317
318
319
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321







+








    data->lastTZEpoch = 0;
    Tcl_UnsetObjRef(data->systemTimeZone);
    Tcl_UnsetObjRef(data->systemSetupTZData);
    Tcl_UnsetObjRef(data->gmtSetupTimeZoneUnnorm);
    Tcl_UnsetObjRef(data->gmtSetupTimeZone);
    Tcl_UnsetObjRef(data->gmtSetupTZData);
    Tcl_UnsetObjRef(data->gmtTZName);
    Tcl_UnsetObjRef(data->lastSetupTimeZoneUnnorm);
    Tcl_UnsetObjRef(data->lastSetupTimeZone);
    Tcl_UnsetObjRef(data->lastSetupTZData);
    Tcl_UnsetObjRef(data->prevSetupTimeZoneUnnorm);
    Tcl_UnsetObjRef(data->prevSetupTimeZone);
    Tcl_UnsetObjRef(data->prevSetupTZData);

1227
1228
1229
1230
1231
1232
1233

1234
1235





1236
1237

1238
1239
1240
1241
1242
1243



1244
1245
1246
1247
1248
1249
1250
1251
1229
1230
1231
1232
1233
1234
1235
1236


1237
1238
1239
1240
1241
1242

1243
1244
1245
1246



1247
1248
1249

1250
1251
1252
1253
1254
1255
1256







+
-
-
+
+
+
+
+

-
+



-
-
-
+
+
+
-







    Tcl_Obj *timezoneObj)
{
    ClockClientData *dataPtr = clientData;
    int loaded;
    Tcl_Obj *callargs[2];

    /* if cached (if already setup this one) */
    if ( timezoneObj == dataPtr->literals[LIT_GMT]
    if ( dataPtr->lastSetupTimeZone != NULL
      && ( timezoneObj == dataPtr->lastSetupTimeZone
      && dataPtr->gmtSetupTZData != NULL
    ) {
	return timezoneObj;
    }
    if ( ( timezoneObj == dataPtr->lastSetupTimeZone
	|| timezoneObj == dataPtr->lastSetupTimeZoneUnnorm
      )
      ) && dataPtr->lastSetupTimeZone != NULL
    ) {
	return dataPtr->lastSetupTimeZone;
    }
    if ( dataPtr->prevSetupTimeZone != NULL
      && ( timezoneObj == dataPtr->prevSetupTimeZone
	|| timezoneObj == dataPtr->prevSetupTimeZoneUnnorm
    if ( ( timezoneObj == dataPtr->prevSetupTimeZone
	|| timezoneObj == dataPtr->prevSetupTimeZoneUnnorm
      ) && dataPtr->prevSetupTimeZone != NULL
      )
    ) {
	return dataPtr->prevSetupTimeZone;
    }

    /* differentiate normalized (last, GMT and system) zones, because used often and already set */
    callargs[1] = NormTimezoneObj(dataPtr, timezoneObj, &loaded);
    /* if loaded (setup already called for this TZ) */
1550
1551
1552
1553
1554
1555
1556









1557
1558
1559
1560
1561
1562
1563
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577







+
+
+
+
+
+
+
+
+







     * Convert to Julian or Gregorian calendar.
     */

    GetGregorianEraYearDay(fields, changeover);
    GetMonthDay(fields);
    GetYearWeekDay(fields, changeover);

    
    /*
     * Seconds of the day.
     */
    fields->secondOfDay = (int)(fields->localSeconds % SECONDS_PER_DAY);
    if (fields->secondOfDay < 0) {
	fields->secondOfDay += SECONDS_PER_DAY;
    }

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * ClockGetjuliandayfromerayearmonthdayObjCmd --
2105
2106
2107
2108
2109
2110
2111
2112

2113
2114
2115
2116



2117
2118
2119
2120
2121







2122
2123
2124
2125
2126
2127
2128
2119
2120
2121
2122
2123
2124
2125

2126


2127
2128
2129
2130
2131





2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145







-
+
-
-


+
+
+
-
-
-
-
-
+
+
+
+
+
+
+







{
    ClockClientData *dataPtr = clientData;
    Tcl_Obj *tzdata;		/* Time zone data */
    int rowc;			/* Number of rows in tzdata */
    Tcl_Obj **rowv;		/* Pointers to the rows */

    /* fast phase-out for shared GMT-object (don't need to convert UTC 2 UTC) */
    if (timezoneObj == dataPtr->literals[LIT_GMT]
    if (timezoneObj == dataPtr->literals[LIT_GMT]) {
	&& dataPtr->gmtSetupTZData != NULL
    ) {
	fields->localSeconds = fields->seconds;
	fields->tzOffset = 0;
	if (dataPtr->gmtTZName == NULL) {
	    Tcl_Obj *tzName;
	    tzdata = ClockGetTZData(clientData, interp, timezoneObj);
	if ( TclListObjGetElements(interp, dataPtr->gmtSetupTZData, &rowc, &rowv) != TCL_OK
	  || Tcl_ListObjIndex(interp, rowv[0], 3, &fields->tzName) != TCL_OK) {
	    return TCL_ERROR;
	}
	Tcl_IncrRefCount(fields->tzName);
	    if ( TclListObjGetElements(interp, tzdata, &rowc, &rowv) != TCL_OK
	      || Tcl_ListObjIndex(interp, rowv[0], 3, &tzName) != TCL_OK) {
		return TCL_ERROR;
	    }
	    Tcl_SetObjRef(dataPtr->gmtTZName, tzName);
	}
	Tcl_SetObjRef(fields->tzName, dataPtr->gmtTZName);
	return TCL_OK;
    }

    /*
     * Check cacheable conversion could be used
     * (last-period UTC2Local cache within the same TZ)
     */
2606
2607
2608
2609
2610
2611
2612
2613

2614












2615
2616
2617







2618
2619
2620
2621
2622
2623
2624
2623
2624
2625
2626
2627
2628
2629

2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643



2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657







-
+

+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+








static void
GetMonthDay(
    TclDateFields *fields)	/* Date to convert */
{
    int day = fields->dayOfYear;
    int month;
    const int *h = hath[IsGregorianLeapYear(fields)];
    const int *dipm = daysInPriorMonths[IsGregorianLeapYear(fields)];

    /* 
     * Estimate month by calculating `dayOfYear / (365/12)`
     */
    month = (day*12) / dipm[12];
    /* then do forwards backwards correction */
    while (1) {
	if (day > dipm[month]) {
	    if (month >= 11 || day <= dipm[month+1]) {
		break;
	    }
	    month++;
	} else {
    for (month = 0; month < 12 && day > h[month]; ++month) {
	day -= h[month];
    }
	    if (month == 0) {
		break;
	    }
	    month--;
	}
    }
    day -= dipm[month];
    fields->month = month+1;
    fields->dayOfMonth = day;
}

/*
 *----------------------------------------------------------------------
 *
3256
3257
3258
3259
3260
3261
3262
3263

3264
3265
3266
3267
3268
3269
3270
3271
3272
3289
3290
3291
3292
3293
3294
3295

3296
3297

3298
3299
3300
3301
3302
3303
3304







-
+

-







	Tcl_SetObjResult(interp, Tcl_NewStringObj("cannot use -gmt and -timezone in same call", -1));
	Tcl_SetErrorCode(interp, "CLOCK", "gmtWithTimezone", NULL);
	return TCL_ERROR;
    }
    if (gmtFlag) {
	opts->timezoneObj = dataPtr->literals[LIT_GMT];
    }

    else
    /* If time zone not specified use system time zone */

    if ( opts->timezoneObj == NULL
      || TclGetString(opts->timezoneObj) == NULL
      || opts->timezoneObj->length == 0
    ) {
	opts->timezoneObj = ClockGetSystemTimeZone(opts->clientData, interp);
	if (opts->timezoneObj == NULL) {
	    return TCL_ERROR;
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249

4250
4251
4252
4253
4254
4255
4256
4262
4263
4264
4265
4266
4267
4268

4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288







-












+







{
    static char* tzWas = INT2PTR(-1);	 /* Previous value of TZ, protected by
					  * clockMutex. */
    static long	 tzLastRefresh = 0;	 /* Used for latency before next refresh */
    static size_t tzWasEpoch = 0;        /* Epoch, signals that TZ changed */
    static size_t tzEnvEpoch = 0;        /* Last env epoch, for faster signaling,
					    that TZ changed via TCL */

    const char *tzIsNow;		 /* Current value of TZ */

    /*
     * Prevent performance regression on some platforms by resolving of system time zone:
     * small latency for check whether environment was changed (once per second)
     * no latency if environment was chaned with tcl-env (compare both epoch values)
     */
    Tcl_Time now;
    Tcl_GetTime(&now);
    if (now.sec == tzLastRefresh && tzEnvEpoch == TclEnvEpoch) {
	return tzWasEpoch;
    }

    tzEnvEpoch = TclEnvEpoch;
    tzLastRefresh = now.sec;

    /* check in lock */
    Tcl_MutexLock(&clockMutex);
    tzIsNow = getenv("TCL_TZ");
    if (tzIsNow == NULL) {
Changes to generic/tclClockFmt.c.
2412
2413
2414
2415
2416
2417
2418



2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431














2432
2433
2434
2435
2436
2437
2438
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430




2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451







+
+
+









-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	-1));
    Tcl_SetErrorCode(opts->interp, "CLOCK", "badInputString", NULL);

done:

    return ret;
}

#define FrmResultIsAllocated(dateFmt) \
    (dateFmt->resEnd - dateFmt->resMem > MIN_FMT_RESULT_BLOCK_ALLOC)

static inline int
FrmResultAllocate(
    register DateFormat *dateFmt,
    int len)
{
    int needed = dateFmt->output + len - dateFmt->resEnd;
    if (needed >= 0) { /* >= 0 - regards NTS zero */
	int newsize = dateFmt->resEnd - dateFmt->resMem
		    + needed + MIN_FMT_RESULT_BLOCK_ALLOC;
	char *newRes = ckrealloc(dateFmt->resMem, newsize);
	if (newRes == NULL) {
	    return TCL_ERROR;
		    + needed + MIN_FMT_RESULT_BLOCK_ALLOC*2;
	char *newRes;
	/* differentiate between stack and memory */
	if (!FrmResultIsAllocated(dateFmt)) {
	    newRes = ckalloc(newsize);
	    if (newRes == NULL) {
		return TCL_ERROR;
	    }
	    memcpy(newRes, dateFmt->resMem, dateFmt->output - dateFmt->resMem);
	} else {
	    newRes = ckrealloc(dateFmt->resMem, newsize);
	    if (newRes == NULL) {
		return TCL_ERROR;
	    }
	}
	dateFmt->output = newRes + (dateFmt->output - dateFmt->resMem);
	dateFmt->resMem = newRes;
	dateFmt->resEnd = newRes + newsize;
    }
    return TCL_OK;
}
2957
2958
2959
2960
2961
2962
2963

2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982



2983
2984
2985
2986






2987
2988
2989
2990
2991
2992
2993
2994
2995
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989






2990
2991
2992
2993




2994
2995
2996
2997
2998
2999
3000

3001
3002
3003
3004
3005
3006
3007







+












-
-
-
-
-
-

+
+
+
-
-
-
-
+
+
+
+
+
+

-







ClockFormat(
    register DateFormat *dateFmt, /* Date fields used for parsing & converting */
    ClockFmtScnCmdArgs *opts)	  /* Command options */
{
    ClockFmtScnStorage	*fss;
    ClockFormatToken	*tok;
    ClockFormatTokenMap *map;
    char resMem[MIN_FMT_RESULT_BLOCK_ALLOC];

    /* get localized format */
    if (ClockLocalizeFormat(opts) == NULL) {
	return TCL_ERROR;
    }

    if ( !(fss = ClockGetOrParseFmtFormat(opts->interp, opts->formatObj))
      || !(tok = fss->fmtTok)
    ) {
	return TCL_ERROR;
    }

    /* prepare formatting */
    dateFmt->date.secondOfDay = (int)(dateFmt->date.localSeconds % SECONDS_PER_DAY);
    if (dateFmt->date.secondOfDay < 0) {
	dateFmt->date.secondOfDay += SECONDS_PER_DAY;
    }

    /* result container object */
    dateFmt->resMem = resMem;
    dateFmt->resEnd = dateFmt->resMem + sizeof(resMem);
    if (fss->fmtMinAlloc > sizeof(resMem)) {
    dateFmt->resMem = ckalloc(MIN_FMT_RESULT_BLOCK_ALLOC);
    if (dateFmt->resMem == NULL) {
	return TCL_ERROR;
    }
	dateFmt->resMem = ckalloc(fss->fmtMinAlloc);
	dateFmt->resEnd = dateFmt->resMem + fss->fmtMinAlloc;
	if (dateFmt->resMem == NULL) {
	    return TCL_ERROR;
	}
    }
    dateFmt->output = dateFmt->resMem;
    dateFmt->resEnd = dateFmt->resMem + MIN_FMT_RESULT_BLOCK_ALLOC;
    *dateFmt->output = '\0';

    /* do format each token */
    for (; tok->map != NULL; tok++) {
	map = tok->map;
	switch (map->type)
	{
3078
3079
3080
3081
3082
3083
3084

3085


3086
3087
3088
3089
3090

3091
3092


3093
3094
3095
3096
















3097
3098
3099
3100
3101
3102
3103
3090
3091
3092
3093
3094
3095
3096
3097

3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109




3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132







+
-
+
+





+


+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	}
    }

    goto done;

error:

    if (dateFmt->resMem != resMem) {
    ckfree(dateFmt->resMem);
	ckfree(dateFmt->resMem);
    }
    dateFmt->resMem = NULL;

done:

    if (dateFmt->resMem) {
    	size_t size;
	Tcl_Obj * result = Tcl_NewObj();
	result->length = dateFmt->output - dateFmt->resMem;
	size = result->length+1;
	if (dateFmt->resMem == resMem) {
	result->bytes = NULL;
	result->bytes = ckrealloc(dateFmt->resMem, result->length+1);
	if (result->bytes == NULL) {
	    result->bytes = dateFmt->resMem;
	    result->bytes = ckalloc(size);
	    if (result->bytes == NULL) {
		return TCL_ERROR;
	    }
	    memcpy(result->bytes, dateFmt->resMem, size);
	} else {
	    result->bytes = ckrealloc(dateFmt->resMem, size);
	    if (result->bytes == NULL) {
		result->bytes = dateFmt->resMem;
	    }
	}
	/* save last used buffer length */
	if ( dateFmt->resMem != resMem
	  && fss->fmtMinAlloc < size + MIN_FMT_RESULT_BLOCK_DELTA
	) {
	    fss->fmtMinAlloc = size + MIN_FMT_RESULT_BLOCK_DELTA;
	}
	result->bytes[result->length] = '\0';
	Tcl_SetObjResult(opts->interp, result);
	return TCL_OK;
    }

    return TCL_ERROR;
Changes to generic/tclDate.h.
289
290
291
292
293
294
295

296
297
298
299
300
301
302
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303







+







    int currentYearCentury;
    int yearOfCenturySwitch;
    Tcl_Obj *systemTimeZone;
    Tcl_Obj *systemSetupTZData;
    Tcl_Obj *gmtSetupTimeZoneUnnorm;
    Tcl_Obj *gmtSetupTimeZone;
    Tcl_Obj *gmtSetupTZData;
    Tcl_Obj *gmtTZName;
    Tcl_Obj *lastSetupTimeZoneUnnorm;
    Tcl_Obj *lastSetupTimeZone;
    Tcl_Obj *lastSetupTZData;
    Tcl_Obj *prevSetupTimeZoneUnnorm;
    Tcl_Obj *prevSetupTimeZone;
    Tcl_Obj *prevSetupTZData;

379
380
381
382
383
384
385
386

387
388
389
390
391
392
393
394
395
396

397
398
399


400
401
402
403
404
405
406
380
381
382
383
384
385
386

387
388
389
390
391
392
393
394
395
396

397
398
399

400
401
402
403
404
405
406
407
408







-
+









-
+


-
+
+







    unsigned short int	minSize;
    unsigned short int	maxSize;
    unsigned short int	offs;
    ClockScanTokenProc *parser;
    const void	       *data;
} ClockScanTokenMap;

typedef struct ClockScanToken {
struct ClockScanToken {
    ClockScanTokenMap  *map;
    struct {
	const char *start;
	const char *end;
    } tokWord;
    unsigned short int	endDistance;
    unsigned short int	lookAhMin;
    unsigned short int	lookAhMax;
    unsigned short int	lookAhTok;
} ClockScanToken;
};


#define MIN_FMT_RESULT_BLOCK_ALLOC 200
#define MIN_FMT_RESULT_BLOCK_ALLOC 80
#define MIN_FMT_RESULT_BLOCK_DELTA 30

typedef struct DateFormat {
    char *resMem;
    char *resEnd;
    char *output;

    TclDateFields date;
428
429
430
431
432
433
434

435

436
437
438
439
440
441

442
443
444
445
446

447
448
449
450
451
452
453
454
455
456

457
458
459
460
461

462
463
464
465
466
467
468
430
431
432
433
434
435
436
437

438
439
440
441
442
443

444
445
446
447
448

449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464

465
466
467
468
469
470
471
472







+
-
+





-
+




-
+










+




-
+







    unsigned short int	flags;
    unsigned short int	divider;
    unsigned short int	divmod;
    unsigned short int	offs;
    ClockFormatTokenProc *fmtproc;
    void	       *data;
} ClockFormatTokenMap;

typedef struct ClockFormatToken {
struct ClockFormatToken {
    ClockFormatTokenMap *map;
    struct {
	const char *start;
	const char *end;
    } tokWord;
} ClockFormatToken;
};


typedef struct ClockFmtScnStorage ClockFmtScnStorage;

typedef struct ClockFmtScnStorage {
struct ClockFmtScnStorage {
    int			 objRefCount;	/* Reference count shared across threads */
    ClockScanToken	*scnTok;
    unsigned int	 scnTokC;
    unsigned int	 scnSpaceCount; /* Count of mandatory spaces used in format */
    ClockFormatToken	*fmtTok;
    unsigned int	 fmtTokC;
#if CLOCK_FMT_SCN_STORAGE_GC_SIZE > 0
    ClockFmtScnStorage	*nextPtr;
    ClockFmtScnStorage	*prevPtr;
#endif
    size_t		 fmtMinAlloc;
#if 0
   +Tcl_HashEntry    hashEntry		/* ClockFmtScnStorage is a derivate of Tcl_HashEntry,
					 * stored by offset +sizeof(self) */
#endif
} ClockFmtScnStorage;
};

/*
 * Prototypes of module functions.
 */

MODULE_SCOPE int    ToSeconds(int Hours, int Minutes,
			    int Seconds, MERIDIAN Meridian);
Changes to generic/tclStrIdxTree.c.
82
83
84
85
86
87
88
89

90
91
92
93
94
95
96
82
83
84
85
86
87
88

89
90
91
92
93
94
95
96







-
+







    TclStrIdx	  **foundItem,	 /* Return value of found item */
    TclStrIdxTree  *tree,	 /* Index tree will be browsed */
    const char	*start,		 /* UTF string to find in tree */
    const char	*end)		 /* End of string */
{
    TclStrIdxTree *parent = tree, *prevParent = tree;
    TclStrIdx  *item = tree->firstPtr, *prevItem = NULL;
    const char *s = start, *f, *cin, *cinf, *prevf;
    const char *s = start, *f, *cin, *cinf, *prevf = NULL;
    int offs = 0;

    if (item == NULL) {
	goto done;
    }

    /* search in tree */
271
272
273
274
275
276
277
278


279
280
281
282
283
284
285
271
272
273
274
275
276
277

278
279
280
281
282
283
284
285
286







-
+
+







		/* ignore element if fulfilled or ambigous */
		if (f == e) {
		    continue;
		}
		/* if shortest key was found with the same value,
		 * just replace its current key with longest key */
		if ( foundItem->value == val
		  && foundItem->length < lwrv[i]->length
		  && foundItem->length <= lwrv[i]->length
		  && foundItem->length <= (f - s) /* only if found item is covered in full */
		  && foundItem->childTree.firstPtr == NULL
		) {
		    Tcl_SetObjRef(foundItem->key, lwrv[i]);
		    foundItem->length = lwrv[i]->length;
		    continue;
		}
		/* split tree (e. g. j->(jan,jun) + jul == j->(jan,ju->(jun,jul)) )
Changes to tests-perf/clock.perf.tcl.
12
13
14
15
16
17
18
19
20
21




22
23
24
25
26

27
28
29


30

31

32
33
34
35
36
37


38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
12
13
14
15
16
17
18



19
20
21
22


23


24



25
26
27
28

29






30
31
32
33
34
35
36
37
38
39
40
41




















































































42
43
44
45
46
47
48







-
-
-
+
+
+
+
-
-

-
-
+
-
-
-
+
+

+
-
+
-
-
-
-
-
-
+
+










-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







# Copyright (c) 2014 Serg G. Brester (aka sebres)
# 
# See the file "license.terms" for information on usage and redistribution
# of this file.
# 


## set testing defaults:
set ::env(TCL_TZ) :CET

## common test performance framework:
if {![namespace exists ::tclTestPerf]} {
  source [file join [file dirname [info script]] test-performance.tcl]
}
# warm-up interpeter compiler env, clock platform-related features,
# calibrate timerate measurement functionality:

# if no timerate here - import from unsupported:
if {[namespace which -command timerate] eq {}} {
namespace eval ::tclTestPerf-TclClock {
  namespace inscope ::tcl::unsupported {namespace export timerate}
  namespace import ::tcl::unsupported::timerate
}

namespace path {::tclTestPerf}

## set testing defaults:
# if not yet calibrated:
set ::env(TCL_TZ) :CET
if {[lindex [timerate {} 10] 6] >= (10-1)} {
  puts -nonewline "Calibration ... "; flush stdout
  puts "done: [lrange \
    [timerate -calibrate {}] \
  0 1]"
}

# warm-up interpeter compiler env, clock platform-related features:

## warm-up test-related features (load clock.tcl, system zones, locales, etc.):
clock scan "" -gmt 1
clock scan ""
clock scan "" -timezone :CET
clock scan "" -format "" -locale en
clock scan "" -format "" -locale de

## ------------------------------------------

proc {**STOP**} {args} {
  return -code error -level 4 "**STOP** in [info level [expr {[info level]-2}]] [join $args { }]" 
}

proc _test_get_commands {lst} {
  regsub -all {(?:^|\n)[ \t]*(\#[^\n]*|\msetup\M[^\n]*|\mcleanup\M[^\n]*)(?=\n\s*(?:[\{\#]|setup|cleanup))} $lst "\n{\\1}"
}

proc _test_out_total {} {
  upvar _ _

  set tcnt [llength $_(itm)]
  if {!$tcnt} {
    puts ""
    return
  }

  set mintm 0x7fffffff
  set maxtm 0
  set nett 0
  set wtm 0
  set wcnt 0
  set i 0
  foreach tm $_(itm) {
    if {[llength $tm] > 6} {
      set nett [expr {$nett + [lindex $tm 6]}]
    }
    set wtm [expr {$wtm + [lindex $tm 0]}]
    set wcnt [expr {$wcnt + [lindex $tm 2]}]
    set tm [lindex $tm 0]
    if {$tm > $maxtm} {set maxtm $tm; set maxi $i}
    if {$tm < $mintm} {set mintm $tm; set mini $i}
    incr i
  }

  puts [string repeat ** 40]
  set s [format "%d cases in %.2f sec." $tcnt [expr {$tcnt * $_(reptime) / 1000.0}]]
  if {$nett > 0} {
    append s [format " (%.2f nett-sec.)" [expr {$nett / 1000.0}]]
  }
  puts "Total $s:"
  lset _(m) 0 [format %.6f $wtm]
  lset _(m) 2 $wcnt
  lset _(m) 4 [format %.3f [expr {$wcnt / (($nett ? $nett : ($tcnt * $_(reptime))) / 1000.0)}]]
  if {[llength $_(m)] > 6} {
    lset _(m) 6 [format %.3f $nett]
  }
  puts $_(m)
  puts "Average:"
  lset _(m) 0 [format %.6f [expr {[lindex $_(m) 0] / $tcnt}]]
  lset _(m) 2 [expr {[lindex $_(m) 2] / $tcnt}]
  if {[llength $_(m)] > 6} {
    lset _(m) 6 [format %.3f [expr {[lindex $_(m) 6] / $tcnt}]]
    lset _(m) 4 [format %.0f [expr {[lindex $_(m) 2] / [lindex $_(m) 6] * 1000}]]
  }
  puts $_(m)
  puts "Min:"
  puts [lindex $_(itm) $mini]
  puts "Max:"
  puts [lindex $_(itm) $maxi]
  puts [string repeat ** 40]
  puts ""
}

proc _test_run {reptime lst {outcmd {puts $_(r)}}} {
  upvar _ _
  array set _ [list itm {} reptime $reptime]

  foreach _(c) [_test_get_commands $lst] {
    puts "% [regsub -all {\n[ \t]*} $_(c) {; }]"
    if {[regexp {^\s*\#} $_(c)]} continue
    if {[regexp {^\s*(?:setup|cleanup)\s+} $_(c)]} {
      puts [if 1 [lindex $_(c) 1]]
      continue
    }
    set _(r) [if 1 $_(c)]
    if {$outcmd ne {}} $outcmd
    puts [set _(m) [timerate $_(c) $reptime]]
    lappend _(itm) $_(m)
    puts ""
  }
  _test_out_total
}

proc test-format {{reptime 1000}} {
  _test_run $reptime {
    # Format : short, week only (in gmt)
    {clock format 1482525936 -format "%u" -gmt 1}
    # Format : short, week only (system zone)
    {clock format 1482525936 -format "%u"}
    # Format : short, week only (CEST)
478
479
480
481
482
483
484

485









388
389
390
391
392
393
394
395

396
397
398
399
400
401
402
403
404







+
-
+
+
+
+
+
+
+
+
+
  test-add $reptime
  test-convert [expr {$reptime / 2}]; #fast enough
  test-other $reptime

  puts \n**OK**
}

}; # end of ::tclTestPerf-TclClock
test 500; # ms

# ------------------------------------------------------------------------

# if calling direct:
if {[info exists ::argv0] && [file tail $::argv0] eq [file tail [info script]]} {
  array set in {-time 500}
  array set in $argv
  ::tclTestPerf-TclClock::test $in(-time)
}
Added tests-perf/test-performance.tcl.

























































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# ------------------------------------------------------------------------
#
# test-performance.tcl --
# 
#  This file provides common performance tests for comparison of tcl-speed
#  degradation or regression by switching between branches.
#
#  To execute test case evaluate direct corresponding file "tests-perf\*.perf.tcl".
#
# ------------------------------------------------------------------------
# 
# Copyright (c) 2014 Serg G. Brester (aka sebres)
# 
# See the file "license.terms" for information on usage and redistribution
# of this file.
# 

namespace eval ::tclTestPerf {
# warm-up interpeter compiler env, calibrate timerate measurement functionality:

# if no timerate here - import from unsupported:
if {[namespace which -command timerate] eq {}} {
  namespace inscope ::tcl::unsupported {namespace export timerate}
  namespace import ::tcl::unsupported::timerate
}

# if not yet calibrated:
if {[lindex [timerate {} 10] 6] >= (10-1)} {
  puts -nonewline "Calibration ... "; flush stdout
  puts "done: [lrange \
    [timerate -calibrate {}] \
  0 1]"
}

proc {**STOP**} {args} {
  return -code error -level 4 "**STOP** in [info level [expr {[info level]-2}]] [join $args { }]" 
}

proc _test_get_commands {lst} {
  regsub -all {(?:^|\n)[ \t]*(\#[^\n]*|\msetup\M[^\n]*|\mcleanup\M[^\n]*)(?=\n\s*(?:[\{\#]|setup|cleanup|$))} $lst "\n{\\1}"
}

proc _test_out_total {} {
  upvar _ _

  set tcnt [llength $_(itm)]
  if {!$tcnt} {
    puts ""
    return
  }

  set mintm 0x7fffffff
  set maxtm 0
  set nett 0
  set wtm 0
  set wcnt 0
  set i 0
  foreach tm $_(itm) {
    if {[llength $tm] > 6} {
      set nett [expr {$nett + [lindex $tm 6]}]
    }
    set wtm [expr {$wtm + [lindex $tm 0]}]
    set wcnt [expr {$wcnt + [lindex $tm 2]}]
    set tm [lindex $tm 0]
    if {$tm > $maxtm} {set maxtm $tm; set maxi $i}
    if {$tm < $mintm} {set mintm $tm; set mini $i}
    incr i
  }

  puts [string repeat ** 40]
  set s [format "%d cases in %.2f sec." $tcnt [expr {([clock milliseconds] - $_(starttime)) / 1000.0}]]
  if {$nett > 0} {
    append s [format " (%.2f nett-sec.)" [expr {$nett / 1000.0}]]
  }
  puts "Total $s:"
  lset _(m) 0 [format %.6f $wtm]
  lset _(m) 2 $wcnt
  lset _(m) 4 [format %.3f [expr {$wcnt / (($nett ? $nett : ($tcnt * $_(reptime))) / 1000.0)}]]
  if {[llength $_(m)] > 6} {
    lset _(m) 6 [format %.3f $nett]
  }
  puts $_(m)
  puts "Average:"
  lset _(m) 0 [format %.6f [expr {[lindex $_(m) 0] / $tcnt}]]
  lset _(m) 2 [expr {[lindex $_(m) 2] / $tcnt}]
  if {[llength $_(m)] > 6} {
    lset _(m) 6 [format %.3f [expr {[lindex $_(m) 6] / $tcnt}]]
    lset _(m) 4 [format %.0f [expr {[lindex $_(m) 2] / [lindex $_(m) 6] * 1000}]]
  }
  puts $_(m)
  puts "Min:"
  puts [lindex $_(itm) $mini]
  puts "Max:"
  puts [lindex $_(itm) $maxi]
  puts [string repeat ** 40]
  puts ""
}

proc _test_run {reptime lst {outcmd {puts $_(r)}}} {
  upvar _ _
  array set _ [list itm {} reptime $reptime starttime [clock milliseconds]]

  foreach _(c) [_test_get_commands $lst] {
    puts "% [regsub -all {\n[ \t]*} $_(c) {; }]"
    if {[regexp {^\s*\#} $_(c)]} continue
    if {[regexp {^\s*(?:setup|cleanup)\s+} $_(c)]} {
      puts [if 1 [lindex $_(c) 1]]
      continue
    }
    if {$reptime > 1} {; #if not once:
      set _(r) [if 1 $_(c)]
      if {$outcmd ne {}} $outcmd
    }
    puts [set _(m) [timerate $_(c) $reptime]]
    lappend _(itm) $_(m)
    puts ""
  }
  _test_out_total
}

}; # end of namespace ::tclTestPerf
Changes to tests/clock.test.
27
28
29
30
31
32
33


34
35
36
37
38
39
40
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42







+
+








package require msgcat 1.4

testConstraint detroit \
    [expr {![catch {clock format 0 -timezone :America/Detroit -format %z}]}]
testConstraint y2038 \
    [expr {[clock format 2158894800 -format %z -timezone :America/Detroit] eq {-0400}}]
testConstraint no_tclclockmod \
    [expr {[namespace which -command ::tcl::clock::configure] eq {}}]

# TEST PLAN

# clock-0:
#	several base test-cases
#
# clock-1:
251
252
253
254
255
256
257
258

259
260
261
262
263
264
265
253
254
255
256
257
258
259

260
261
262
263
264
265
266
267







-
+







	return -code error "test case attempts to read unknown registry entry $path $key"
    }
    return [dict get $reg $path $key]
}

# Base test cases:

test clock-0.1 "initial: auto-loading of ensemble and stubs on demand" {
test clock-0.1 "initial: auto-loading of ensemble and stubs on demand" no_tclclockmod {
    set i [interp create]; # because clock can be used somewhere, test it in new interp:

    set ret [$i eval {

	lappend ret ens:[namespace ensemble exists ::clock]
	clock seconds; # init ensemble (but not yet stubs, loading of clock.tcl retarded)
	lappend ret ens:[namespace ensemble exists ::clock]
35183
35184
35185
35186
35187
35188
35189























35190
35191
35192
35193
35194
35195
35196
35185
35186
35187
35188
35189
35190
35191
35192
35193
35194
35195
35196
35197
35198
35199
35200
35201
35202
35203
35204
35205
35206
35207
35208
35209
35210
35211
35212
35213
35214
35215
35216
35217
35218
35219
35220
35221







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







        -format {%J %Ol:%M:%S %P}
} 86399
test clock-29.1800 {time parsing} {
    clock scan {2440588 xi:lix:lix pm} \
        -gmt true -locale en_US_roman \
        -format {%J %Ol:%OM:%OS %P}
} 86399

test clock-29.1811 {parsing of several localized formats} {
    set res {}
    foreach loc {en de fr} {
	foreach fmt {"%x %X" "%X %x"} {
	    lappend res [clock scan \
	      [clock format 0 -format $fmt -locale $loc -gmt 1] \
	      -format $fmt -locale $loc -gmt 1]
	}
    }
    set res
} [lrepeat 6 0]
test clock-29.1812 {parsing of several localized formats} {
    set res {}
    foreach loc {en de fr} {
	foreach fmt {"%a %d-%m-%Y" "%a %b %x-%X" "%a, %x %X" "%b, %x %X"} {
	    lappend res [clock scan \
	      [clock format 0 -format $fmt -locale $loc -gmt 1] \
	      -format $fmt -locale $loc -gmt 1]
	}
    }
    set res
} [lrepeat 12 0]
# END testcases29


# BEGIN testcases30

# Test [clock add]
test clock-30.1 {clock add years} {