Я пытаюсь создать структуру, которая по сути является обычным двойником, но к которой с помощью дженериков прикреплена физическая единица.
Мои попытки работают ужасно, даже без дженериков. Может ли кто-нибудь подсказать мне, как я могу сделать это быстрее, если это вообще возможно, на C#?
Я использовал
public readonly struct MyDouble
{
public readonly double Value;
public MyDouble(double value)
{
Value = value;
}
public static MyDouble operator +(MyDouble self, MyDouble value)
{
return new MyDouble(self.Value + value.Value);
}
public static MyDouble operator *(MyDouble self, double value)
{
return new MyDouble(self.Value * value);
}
}
и хотя их простое создание, сохранение и извлечение происходит так же быстро, как и для двойных чисел, как только я использую операторы, производительность снижается по сравнению с необработанной двойной арифметикой.
Я думал, что это всего лишь вопрос встраивания, но добавление атрибутов [MethodImpl(MethodImplOptions.AggressiveInlining)] к операторам совсем не помогло.
Я проверил низкую производительность с помощью модульных тестов, а также с помощью Dottrace JetBrain при производственных запусках.
Вот воспроизведение с использованием Benchmark Dotnet:
using System;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using NUnit.Framework;
namespace MyBenchmark;
public readonly struct MyDouble
{
public readonly double Value;
public MyDouble(double value)
{
Value = value;
}
public static MyDouble operator +(MyDouble self, MyDouble value)
{
return new MyDouble(self.Value + value.Value);
}
public static MyDouble operator *(MyDouble self, double value)
{
return new MyDouble(self.Value * value);
}
}
public class DoubleBenchmark
{
private const int N = 1_000_000;
private readonly double[] _data;
private readonly double[] _workspace;
private readonly MyDouble[] _typedData;
private readonly MyDouble[] _typedWorkspace;
public DoubleBenchmark()
{
_data= new double[N];
_workspace = new double[N];
_typedWorkspace = new MyDouble[N];
var rand = new Random(0);
for (var i = 0; i < N; i++)
{
_data = rand.NextDouble();
}
_typedData = _data.Select(x => new MyDouble(x)).ToArray();
}
[Benchmark]
public MyDouble TypedDoubles()
{
var d = new MyDouble(0);
for (var i = 0; i < N; i++)
{
d += _typedData * 3;
_typedWorkspace = d * 2 + new MyDouble(1);
}
var e = new MyDouble(0);
for (var i = 0; i < N; i++)
{
e += _typedWorkspace;
}
return e;
}
[Benchmark]
public MyDouble ManuallyInlinedTypedDoubles()
{
var d = new MyDouble(0);
for (var i = 0; i < N; i++)
{
d = new MyDouble(d.Value + _typedData.Value * 3);
_typedWorkspace = new MyDouble(d.Value * 2 + 1);
}
var e = new MyDouble(0);
for (var i = 0; i < N; i++)
{
e = new MyDouble(e.Value + _typedWorkspace.Value);
}
return e;
}
[Benchmark]
public double Doubles()
{
var d = 0d;
for (var i = 0; i < N; i++)
{
d += _data * 3;
_workspace = d * 2 + 1;
}
var e = 0d;
for (var i = 0; i < N; i++)
{
e += _workspace;
}
return e;
}
[Test, Explicit]
public static void RunBenchmarks()
{
BenchmarkRunner.Run();
}
[Test, Explicit]
public static void EqualResults()
{
var a = new DoubleBenchmark();
Console.WriteLine((a.TypedDoubles().Value, a.Doubles(), a.ManuallyInlinedTypedDoubles().Value));
if (Math.Abs(a.TypedDoubles().Value - a.Doubles()) > 0 ||
Math.Abs(a.TypedDoubles().Value - a.ManuallyInlinedTypedDoubles().Value) > 1e-16)
{
throw new Exception("Different results");
}
}
}
| Method | Mean | Error | StdDev |
|---------------------------- |----------:|----------:|----------:|
| TypedDoubles | 12.205 ms | 0.0170 ms | 0.0142 ms |
| ManuallyInlinedTypedDoubles | 6.158 ms | 0.0063 ms | 0.0055 ms |
| Doubles | 3.149 ms | 0.0032 ms | 0.0028 ms |
Подробнее здесь: https://stackoverflow.com/questions/786 ... -in-csharp
Перенос двойников без накладных расходов в Csharp ⇐ C#
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение
-
-
В чем причина накладных расходов на функцию childItems() в Qt C++ QGraphicsItems?
Anonymous » » в форуме C++ - 0 Ответы
- 58 Просмотры
-
Последнее сообщение Anonymous
-