(顺便说一句.这是指32位操作系统)
一些更新:
这绝对是一个对齐问题
有时对齐(无论出于什么原因?)是如此糟糕,以至于对双倍的访问速度比其最快访问速度慢50倍.
在64位机器上运行代码减少了问题,但我认为它仍然在两个时间之间交替(我可以通过将双倍更改为32位机器上的浮点数来获得类似结果)
在mono下运行代码没有问题 - 微软,你有没有机会从那些Novell家伙那里复制一些东西?
有没有办法在c#中记忆对齐类的分配?
以下演示(我认为!)没有双打正确对齐的不良.它对存储在类中的double执行一些简单的数学计算,对每次运行进行计时,在变量上运行5次定时运行,然后再分配新的并重新执行.
基本上结果看起来像你有一个快速,中等或慢速的记忆位置(在我的古代处理器上,这些最终每次运行大约40,80或120ms)
我曾尝试使用StructLayoutAttribute,但没有任何乐趣 - 也许还有其他事情正在发生?
class Sample
{
class Variable { public double Value; }
static void Main()
{
const int COUNT = 10000000;
while (true)
{
var x = new Variable();
for (int inner = 0; inner < 5; ++inner)
{
// move allocation here to allocate more often so more probably to get 50x slowdown problem
var stopwatch = Stopwatch.StartNew();
var total = 0.0;
for (int i = 1; i <= COUNT; ++i)
{
x.Value = i;
total += x.Value;
}
if (Math.Abs(total - 50000005000000.0) > 1)
throw new ApplicationException(total.ToString());
Console.Write("{0}, ", stopwatch.ElapsedMilliseconds);
}
Console.WriteLine();
}
}
}
Run Code Online (Sandbox Code Playgroud)
所以我看到很多关于interop结构对齐的网页,那么类的对齐呢?
(或者我的假设是错误的,上面还有另一个问题?)
谢谢,保罗.
运行机器的齿轮看起来很有趣。当双精度数只能以两种方式对齐时,我在解释为什么有多个不同的值(我有 4 个)时遇到了一些问题。我认为与 CPU 缓存行的对齐也发挥了作用,尽管这只增加了 3 个可能的时序。
好吧,你对此无能为力,CLR 只承诺 4 字节值的对齐,以便保证 32 位机器上的原子更新。这不仅仅是托管代码的问题,C/C++ 也有这个问题。看来芯片制造商需要解决这个问题。
如果很重要,那么您可以使用 Marshal.AllocCoTaskMem() 分配非托管内存,并使用可以正确对齐的不安全指针。如果您为使用 SIMD 指令的代码分配内存,您也必须做同样的事情,它们需要 16 字节对齐。不过,请认为这是绝望之举。
小智 6
为了在.NET中证明堆上对象未对齐的概念,您可以运行以下代码,现在它将始终快速运行.请不要拍我,它只是一个PoC,但如果你真的关心性能,你可以考虑使用它;)
public static class AlignedNew
{
public static T New<T>() where T : new()
{
LinkedList<T> candidates = new LinkedList<T>();
IntPtr pointer = IntPtr.Zero;
bool continue_ = true;
int size = Marshal.SizeOf(typeof(T)) % 8;
while( continue_ )
{
if (size == 0)
{
object gap = new object();
}
candidates.AddLast(new T());
GCHandle handle = GCHandle.Alloc(candidates.Last.Value, GCHandleType.Pinned);
pointer = handle.AddrOfPinnedObject();
continue_ = (pointer.ToInt64() % 8) != 0 || (pointer.ToInt64() % 64) == 24;
handle.Free();
if (!continue_)
return candidates.Last.Value;
}
return default(T);
}
}
class Program
{
[StructLayoutAttribute(LayoutKind.Sequential)]
public class Variable
{
public double Value;
}
static void Main()
{
const int COUNT = 10000000;
while (true)
{
var x = AlignedNew.New<Variable>();
for (int inner = 0; inner < 5; ++inner)
{
var stopwatch = Stopwatch.StartNew();
var total = 0.0;
for (int i = 1; i <= COUNT; ++i)
{
x.Value = i;
total += x.Value;
}
if (Math.Abs(total - 50000005000000.0) > 1)
throw new ApplicationException(total.ToString());
Console.Write("{0}, ", stopwatch.ElapsedMilliseconds);
}
Console.WriteLine();
}
}
}
Run Code Online (Sandbox Code Playgroud)