這個(gè)不是瞎說(shuō),微軟自己解釋的: System.DateTime.Now 運(yùn)行緩慢
DateTime.Now 成本高昂砖顷,因?yàn)樗褂?TimeZoneInfo.GetIsDaylightSavingsFromUtc() 確定夏令時(shí) (DST)。 請(qǐng)考慮改用 DateTime.UtcNow翁锡,而不使用 DateTime.Now众旗。
如果使用 DateTime 來(lái)計(jì)算時(shí)間增量(例如測(cè)量某些活動(dòng)的持續(xù)時(shí)間),則頻繁調(diào)用此方法的成本可能會(huì)很高粪小。 在此方案中大磺,檢查是否可以改用 DateTime.UtcNow。
但是 DateTime.UtcNow
同樣也有性能問(wèn)題:
1, 首先解決肉眼可見的 DateTime.Now 的性能問(wèn)題
DateTime.Now 看起來(lái)人畜無(wú)害, 但是在高并發(fā), 大量需要時(shí)間計(jì)算的場(chǎng)景下, 這個(gè)東西會(huì)浪費(fèi)掉大量的 CPU. 無(wú)奈要使用當(dāng)前時(shí)間只有這一個(gè)途徑, 微軟只是給了一個(gè)解釋, 并沒有給出詳細(xì)的解決方案 (DateTime.UtcNow 也不行, 上圖可以解釋).
既然無(wú)法改變 DateTime.Now, 那只得改變自己.
DateTime.Now 運(yùn)行緩慢, 那不防我們減少調(diào)用它的頻率. 大部分的業(yè)務(wù)場(chǎng)景只是用來(lái)比較一下時(shí)間, 不必太精確.
由此, 可以設(shè)計(jì)一個(gè)定時(shí)刷新的類, 這個(gè)類提供一個(gè)不大精確的當(dāng)前時(shí)間, 這樣就可以減少 DateTime.Now 的調(diào)用, 從而減少 CPU 的使用頻率:
/// <summary>
/// DateTime.Now, UtcNow 是消耗CPU的大戶.
/// 對(duì)于不需要太精確的時(shí)間, 可以定期刷新值, 以節(jié)省CPU
/// </summary>
public class SlowDateTime
{
private static TimeSpan interval;
private static DateTime _now = DateTime.Now;
public static DateTime Now => _now;
private static DateTime _utcNow = DateTime.UtcNow;
public static DateTime UtcNow => _utcNow;
public static void Init(TimeSpan interval)
{
SlowDateTime.interval = interval;
Refresh();
}
private static void Refresh()
{
Task.Factory.StartNew(async () =>
{
while (true)
{
await Task.Delay(interval);
_now = DateTime.Now;
_utcNow = DateTime.UtcNow;
}
}, TaskCreationOptions.LongRunning);
}
}
在啟動(dòng)的時(shí)候, 調(diào)用 SlowDateTime.Init(...)
, 確定刷新頻率, 在不需要太精確的當(dāng)前時(shí)間的地方使用 SlowDateTime.Now
就行了.
2, 由于 DateTime.UtcNow, 用來(lái)提升性能的 MemoryCache 反而降低了性能
一般, 為了減少 數(shù)據(jù)庫(kù) / Redis 等服務(wù) 的壓力, 也為了減少網(wǎng)絡(luò)交換帶來(lái)的性能損耗, 我會(huì)在程序中在設(shè)置一道 MemoryCache, 定期過(guò)期其中的數(shù)據(jù). 也就是用內(nèi)存換時(shí)間.
但是在不知不覺中就陷入了一個(gè)高級(jí)的BUG:
MemoryCache 在取數(shù)據(jù)的時(shí)候, 會(huì)確定數(shù)據(jù)是否過(guò)期, 怎么判斷呢? 當(dāng)然是獲取當(dāng)前時(shí)間, 在和數(shù)據(jù)的過(guò)期時(shí)間對(duì)比.在高并發(fā)的情況下, 這個(gè)自然就降低了程序的性能.
好在:
- 可以用 MemoryCache 的數(shù)據(jù), 一般精確度要求度不高.
-
MemoyCacheOptons
有一個(gè)Clock
屬性.
public class SlowClock : ISystemClock
{
public DateTimeOffset UtcNow => SlowDateTime.UtcNow;
public static SlowClock Default { get; }
static SlowClock()
{
Default = new SlowClock();
}
}
在聲明 MemoryCache 的時(shí)候, 帶上這個(gè) SlowClock 就行了.
private static readonly MemoryCache cache = new(new MemoryCacheOptions() { Clock = SlowClock.Default });
3, 其它
在StackExchange.Redis
, log4net
中, 也大量的使用了 DateTime.UtcNow , 但是不像 MemoryCache , 可以修改 Clock.