- 已关闭**。此问题需要details or clarity。当前不接受答案。
- 想要改进此问题?**添加详细信息并通过editing this post阐明问题。
昨天关门了。
Improve this question
直觉
为了简化我的API,我想给用户提供一些枚举器来遍历数据集,但我没有深入到数据集,因为这会超出范围。
我在评测和基准测试时注意到,c#枚举数和自定义(结构)枚举数比原始数组访问要慢得多。
由于我的API和应用程序需要高性能和尽可能快的速度,我正在寻找影响这一点的方法。
问题
MoveNext()
和Current
从未内联,即使标记为inline。这会导致大量地址跳转,从而影响性能。
举个例子:
/// <summary>
/// The <see cref="Enumerator{T}"/> struct
/// represents an enumerator with which one can iterate over all items of an array or span.
/// </summary>
/// <typeparam name="T">The generic type.</typeparam>
public ref struct Enumerator<T>
{
private readonly Span<T> _span;
private int _index;
private readonly int _size;
/// <summary>
/// Initializes a new instance of the <see cref="Enumerator{T}"/> struct.
/// </summary>
/// <param name="span">The <see cref="Span{T}"/> with items to iterate over.</param>
public Enumerator(Span<T> span)
{
_span = span;
_index = -1;
_size = span.Length;
}
/// <summary>
/// Initializes a new instance of the <see cref="Enumerator{T}"/> struct.
/// </summary>
/// <param name="span">The <see cref="Span{T}"/> with items to iterate over.</param>
/// <param name="length">Its length or size.</param>
public Enumerator(Span<T> span, int length)
{
_span = span;
_index = -1;
_size = length;
}
/// <summary>
/// Moves to the next item.
/// </summary>
/// <returns>True if there still items, otherwhise false.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
return unchecked(++_index) < _size;
}
/// <summary>
/// Resets this instance.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset()
{
_index = -1;
}
/// <summary>
/// Returns a reference to the current item.
/// </summary>
public readonly ref T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _span[_index];
}
}
public void EnumerateSample(){
var items = new int[100000];
var enumerator = new Enumerator<int>(items);
// MoveNext and .Current will not inline, bad
// Adress jumps on every single item, incredible bad
while(enumerator.MoveNext()){
ref var i = ref enumerator.Current;
i++;
}
}
添加SkipLocalsInit
也没有帮助。foreach(ref var item in myEnumerator)
非常慢,因为这些调用没有内联,这在大量迭代中非常明显。
问题
有没有办法进一步影响枚举器的性能,或者真正强制movenext和current调用内联?
欢迎任何帮助或想法!
1条答案
按热度按时间7rfyedvj1#
下面是这个方法在版本配置中反汇编时的样子。唯一的
call
是用于new int[]
的。条件逻辑需要跳转,例如循环。没有调用/返回。我在这里没有看到任何问题。BenchmarkDotNet显示,如果我将
new int[]
移出此方法,只留下foreach
和大约630 us的内存分配,则此方法大约需要320 us的时间。