在领域层结构中使用 Int64 而不是 DateTime / TimeSpan 是否合理?

huangapple go评论90阅读模式
英文:

Is it reasonable to use Int64 instead of DateTime / TimeSpan in the Domain-level structures?

问题

在我的解决方案的领域项目(.NET Framework)中,我有以下简单的结构:

public struct Candlestick
{
    public DateTime OpenTime;
    public DateTime CloseTime;
    public double Open;
    public double High;
    public double Low;
    public double Close;
    public double Volume;
}

在我的业务逻辑中,我有许多方法用于在Candlestick结构的大型列表中执行基于时间的搜索。例如:

public static int IndexOfFirst(this IReadOnlyList<Candlestick> data, DateTime from)
{
    if (data == null)
        throw new ArgumentNullException(nameof(data));

    int count = data.Count;
    if (count == 0) return -1;

    int fromIdx = 0;
    while (data[fromIdx].OpenTime < from)
    {
        if (++fromIdx == count)
        {
            return -1;
        }
    }

    return fromIdx;
}

是否一般来说,使用longInt64)类型作为Candlestick结构中的时间变量是一个好主意,以便逻辑层能够更快地操作这个结构?另外,我打算在应用程序层级保留相应的类(视图模型层中的CandlestickVM),其中时间变量的类型是DateTime

为了验证我使用Int64的想法,我在dotnetfiddle.net上运行了一个简单的性能测试:

DateTime a = new DateTime(2023, 01, 01);
DateTime b = new DateTime(2023, 02, 01);

TimeSpan incr = new TimeSpan(1);
DateTime now = a;
Stopwatch sw = Stopwatch.StartNew();
while (now < b)
{
    now += incr;
}
sw.Stop();
Console.WriteLine($"运行日期和时间测试耗时:{sw.Elapsed}");

long incr2 = incr.Ticks;
long now2 = a.Ticks;
long bticks = b.Ticks;
sw.Restart();
while (now2 < bticks)
{
    now2 += incr2;
}
sw.Stop();
Console.WriteLine($"运行长整数测试耗时:{sw.Elapsed}");

结果如下:

运行日期和时间测试耗时:00:00:04.8027716
运行长整数测试耗时:00:00:00.9261338

如果您要求只翻译代码部分,以上即为代码部分的翻译。

英文:

I have the following simple structure in my solution's domain project (.NET Framework):

public struct Candlestick
{
    public DateTime OpenTime;
    public DateTime CloseTime;
    public double Open;
    public double High;
    public double Low;
    public double Close;
    public double Volume;
}

In my business logic I have many methods that perform time-based search in big lists of Candlestick structures. For example:

public static int IndexOfFirst(this IReadOnlyList&lt;Candlestick&gt; data, DateTime from)
{
    if (data == null)
        throw new ArgumentNullException(nameof(data));

    int count = data.Count;
    if (count == 0) return -1;

    int fromIdx = 0;
    while (data[fromIdx].OpenTime &lt; from)
    {
        if (++fromIdx == count)
        {
            return -1;
        }
    }

    return fromIdx;
}

Would it be generally a good idea to use the long (Int64) type for time variables in the Candlestick struct, so that the logic layer can operate faster with this structure? Also, I mean to keep the corresponding class on the application level (CandlestickVM in the view-model layer) with the time variables of the type DateTime.

To check my idea of using Int64, I ran a simple performance test on dotnetfiddle.net:

DateTime a = new DateTime(2023, 01, 01);
DateTime b = new DateTime(2023, 02, 01);

TimeSpan incr = new TimeSpan(1);
DateTime now = a;
Stopwatch sw = Stopwatch.StartNew();
while (now &lt; b)
{
    now += incr;
}
sw.Stop();
Console.WriteLine($&quot;It took {sw.Elapsed} to run the Date &amp; Time test.&quot;);

long incr2 = incr.Ticks;
long now2 = a.Ticks;
long bticks = b.Ticks;
sw.Restart();
while (now2 &lt; bticks)
{
    now2 += incr2;
}
sw.Stop();
Console.WriteLine($&quot;It took {sw.Elapsed} to run the long test.&quot;);

The results is as the following:

It took 00:00:04.8027716 to run the Date &amp; Time test.
It took 00:00:00.9261338 to run the long test.

答案1

得分: 1

您的代码中,与 DateTime 相关的操作仅涉及比较,而您的基准测试执行了加法操作。我使用 BenchmarkDotNet 创建了一个仅涉及比较的简单基准测试,以下是代码和结果:

[SimpleJob(RuntimeMoniker.Net70)]    
[RPlotExporter]
public class LongXDateTime
{
    [Params(1_000_000)]
    private int N;
    private DateTime[] Da;
    private DateTime[] Db;
    private long[] La;
    private long[] Lb;

    [GlobalSetup]
    public void Setup()
    {
        Da = new DateTime[N];
        Db = a DateTime[N];
        La = new long[N];
        Lb = new long[N];
        for (int i = 0; i &lt; N; i++)
        {
            La[i] = Random.Shared.NextInt64(DateTime.MinValue.Ticks, DateTime.MaxValue.Ticks);
            Lb[i] = Random.Shared.NextInt64(DateTime.MinValue.Ticks, DateTime.MaxValue.Ticks);
            Da[i] = new DateTime(La[i]);
            Db[i] = new DateTime(Lb[i]);
        }
    }

    [Benchmark]
    public bool[] LongMethod()
    {
        bool[] result = new bool[La.Length];
        for(int i = 0; i &lt; La.Length; i++)
        {
            result[i] = La[i] &lt; Lb[i];
        }
        return result;
    }
    [Benchmark]
    public bool[] DateTimeMethod()
    {
        bool[] result = new bool[Da.Length];
        for (int i = 0; i &lt; Da.Length; i++)
        {
            result[i] = Da[i] &lt; Db[i];
        }
        return result;
    }
}

结果如下:

|         Method |          N |     Mean |     Error |    StdDev |
|--------------- |----------- |---------:|----------:|----------:|
|     LongMethod |    1000000 | 1.460 ms | 0.0253 ms | 0.0370 ms |
| DateTimeMethod |    1000000 | 1.467 ms | 0.0292 ms | 0.0380 ms |

因此,切换到使用 long 可能不会在应用性能方面具有重要意义,而且它:

  1. 可能不会对应用性能产生影响
  2. 可能不够可读
  3. 不够安全,因为您会失去类型安全性

此外,建议阅读 Eric Lippert 关于性能的博客

英文:

Your code only operations with DateTime are comparisons, while your benchmark does adition. I made a simple benchmark using BenchmarkDtonet with comparisons only and here are the code and results:

[SimpleJob(RuntimeMoniker.Net70)]    
[RPlotExporter]
public class LongXDateTime
{
    [Params(1_000_000)]
    private int N;
    private DateTime[] Da;
    private DateTime[] Db;
    private long[] La;
    private long[] Lb;

    [GlobalSetup]
    public void Setup()
    {
        Da = new DateTime[N];
        Db = new DateTime[N];
        La = new long[N];
        Lb = new long[N];
        for (int i = 0; i &lt; N; i++)
        {
            La[i] = Random.Shared.NextInt64(DateTime.MinValue.Ticks, DateTime.MaxValue.Ticks);
            Lb[i] = Random.Shared.NextInt64(DateTime.MinValue.Ticks, DateTime.MaxValue.Ticks);
            Da[i] = new DateTime(La[i]);
            Db[i] = new DateTime(Lb[i]);
        }
    }

    [Benchmark]
    public bool[] LongMethod()
    {
        bool[] result = new bool[La.Length];
        for(int i = 0; i &lt; La.Length; i++)
        {
            result[i] = La[i] &lt; Lb[i];
        }
        return result;
    }
    [Benchmark]
    public bool[] DateTimeMethod()
    {
        bool[] result = new bool[Da.Length];
        for (int i = 0; i &lt; Da.Length; i++)
        {
            result[i] = Da[i] &lt; Db[i];
        }
        return result;
    }
}

Result:

|         Method |          N |     Mean |     Error |    StdDev |
|--------------- |----------- |---------:|----------:|----------:|
|     LongMethod |    1000000 | 1.460 ms | 0.0253 ms | 0.0370 ms |
| DateTimeMethod |    1000000 | 1.467 ms | 0.0292 ms | 0.0380 ms |

So switching to long:

  1. might not be relevant in terms of application performance
  2. Is less readable
  3. Is less safe, since you lose type safety

Also, recommend reading Eric Lippert Blog about performance

huangapple
  • 本文由 发表于 2023年2月23日 23:09:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/75546667.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定