提高速度的不安全 C# 技巧
Posted
技术标签:
【中文标题】提高速度的不安全 C# 技巧【英文标题】:Unsafe C# trick to improve speed 【发布时间】:2011-07-15 04:49:48 【问题描述】:我不习惯使用指针(例如 C++)进行编码,也不习惯使用不安全岛:只有“安全”的 C#。 现在我想在 C# 中为 .Net Micro Framework 实现一个函数,其中紧凑性和性能非常重要。 基本上,我会收集 4 个短裤,从而填充一个缓冲区(例如字节数组)。 假设每个样本都是这样的:
struct MyStruct
public short An1;
public short An2;
public short An3;
public short An4;
每个样本都是通过计时器事件收集的,因此我无法循环(有几个原因)。 我尝试了很多方法来有效地做到这一点,但表现最好的似乎是这个:
unsafe struct MyStruct2
public fixed byte Buffer[Program.BufferSize];
unsafe class Program
public const int BufferSize = 0x1000;
public const int ArraySize = BufferSize / 8;
static MyStruct2 _struct2 = new MyStruct2();
static MyStruct* _structPtr;
unsafe static void Main(string[] args)
int iter = 5000; //just for simulate many cycles
for (int i = 0; i < iter; i++)
//let's make a trick!
fixed (byte* ptr = _struct2.Buffer)
_structPtr = (MyStruct*)ptr;
_structIndex = 0;
do
Test5();
while (++_structIndex < ArraySize);
Console.ReadKey();
unsafe static void Test5()
_structPtr->An1 = (short)An1();
_structPtr->An2 = (short)An2();
_structPtr->An3 = (short)An3();
_structPtr->An4 = (short)An4();
_structPtr++;
//simulations of ADC reading
static int An1()
return 0x1111;
static int An2()
return 0x2222;
static int An3()
return 0x3333;
static int An4()
return 0x4444;
比以下更安全的方式(例如,)的改进不是那么高(177 毫秒与 224 毫秒),但无论如何它是显着的。
static MyStruct Test3()
var data = new MyStruct();
data.An1 = (short)An1();
data.An2 = (short)An2();
data.An3 = (short)An3();
data.An4 = (short)An4();
return data;
注意:我已经剪掉了一些代码,但我认为它已经足够清晰了。
我的问题是:我通过将“固定”指针复制到另一个未固定的指针而做出的“技巧”是否可靠?...但是您可以假设所有数据都是静态分配的,因此应该固定。 先感谢您。 干杯
【问题讨论】:
澄清一下,Test3 还是 Test5 更快? 您是否在没有一次性 JITting 的情况下进行了测量?我的意思是您是否尝试运行一次托管函数然后循环测量它? JITting 只发生一次,因此如果将其包含在时间总和中,与不安全的方式相比将是一个糟糕的比较。 为什么 AnX 方法返回一个 int?你不能让他们退回短裤并跳过演员表吗?这会提高性能吗?此外,从fixed
语句中获取指向托管缓冲区的指针似乎是一个非常糟糕的主意。我不确定,但我认为如果 GC 挡道,那可能会受到伤害。
@jde - 没有抖动,IL 被解释。放弃所有关于效率的假设。
哦,谢谢汉斯,我不知道关于 .NET 微框架的事实......
【参考方案1】:
我认为代码不安全。在固定范围内的_structPtr = (MyStruct*)ptr
之后,您继续将数据放入 _structPtr 假设 _struct2 不会移动。虽然您说它不会被 GC 是正确的,但这并不意味着 GC 在内存压缩期间不会移动它。 .NET Compact Framework 仍会进行垃圾收集,我认为它会压缩内存而不是使其碎片化。
例如,如果在 _struct2 之前分配在堆上的瞬态(非静态)对象被 GC 删除,则该结构使用的内存可能会转移到该瞬态对象使用的空闲空间中。此时 _structPtr 指向未使用的内存。
是否会修改 Test3() 以获取 ref MyStruct data
帮助?
另外,检查[StructLayout(LayoutKind.Explicit)]
和[FieldOffset(...)]
,这将允许您拥有一个具有多种访问其中相同数据的方式的结构。在您的情况下,可以是 4 个字节或 1 个 int 或(可能)1 个 4 个字节的数组。
【讨论】:
“我不认为代码不安全”...双重否定是有意的吗? 谢谢克里斯。晚上写的太晚了。编辑即将到来。 好主意:我没有尝试使用“ref”。另一个想法是使用“long”。我不能使用“StructLayout”,因为它在 Micro Framework 库中不可用。还是谢谢。 StructLayout 是微框架的一部分。 msdn.microsoft.com/en-us/library/ee434399.aspx【参考方案2】:看看这个example 来自另一个帖子,关于我广泛使用的一些代码。你有正确的想法,只需要设置FieldOffset()
的值,然后在访问数据时使用fixed()
关键字。
这种方法相当可靠,但速度并不快。我使用它的原因是,当我有很多字段时,我想以一种快速的方式将它们作为数组访问。
【讨论】:
【参考方案3】:我不认为你的“技巧”有问题。没有人关心你如何索引内存,或者你使用多少偏移量来完成它。我确实认为您需要听取其他人的建议,并确保您使用 StructLayout.Sequential 或 .Explicit 控制结构的布局。还要注意尺寸和包装选项。
已经提到的另一个问题是您需要在固定块中完成所有工作。
fixed (byte* ptr = struct2.Buffer)
var structPtr = (MyStruct*)ptr;
var structIndex = 0;
do
Test5(structPtr);
while (++structIndex < ArraySize);
就个人而言,我认为您偶然发现了一些微优化剧院,使用安全的 C# 代码会更好。根据您给出的数字,(224x10^-3 - 177x10^-3) 产生 47ms,除以 5000 次迭代,每次迭代净您 9.4us (9.4x10^-6)(假设 windows 在时间)。
【讨论】:
微框架是标准框架的一个很小的子集,它是为小型设备(慢时钟,少量 ram/rom)而设计的。我展示的时间是在台式电脑上进行的(相当快),但这只是性能之间的一个指示性比率。我不能按照你的建议使用闭环,因为我的 cpu 太慢了。我想唯一合理的答案是创建一个 C++ 原生自定义驱动程序。 我不知道你所说的闭环是什么意思.祝你好运。以上是关于提高速度的不安全 C# 技巧的主要内容,如果未能解决你的问题,请参考以下文章