【问题标题】:Where is this TrySZBinarySearch implemented? [closed]这个 TrySZBinarySearch 在哪里实现? [关闭]
【发布时间】:2013-08-16 17:06:56
【问题描述】:

在研究一些微性能技术时,我在array.cs file 中遇到了.net 框架中对二进制搜索函数的外部引用。

private static extern bool TrySZBinarySearch(Array sourceArray, int sourceIndex, int count, Object value, out int retVal); 

在哪里可以找到此功能的文档?或者更好的是,它是如何实施的? 为什么.net里有这么多SZ?

private static extern bool TrySZIndexOf(Array sourceArray, int sourceIndex, int count, Object value, out int retVal); 

private static extern bool TrySZLastIndexOf(Array sourceArray, int sourceIndex, int count, Object value, out int retVal);

sealed class SZArrayHelper { ... }

【问题讨论】:

标签: c# .net extern reference-implementation


【解决方案1】:
    [System.Security.SecurityCritical]  // auto-generated
    [ResourceExposure(ResourceScope.None)]
    [MethodImplAttribute(MethodImplOptions.InternalCall)]
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
    private static extern bool TrySZBinarySearch(Array sourceArray, 
        int sourceIndex, int count, Object value, out int retVal);

这是从参考源中检索到的声明。其中拥有 .NET 框架的绝大部分源代码。你可以下载it here

使用 [MethodImpl(MethodImplOptions.InternalCall)] 属性的方法是用 C++ 编写的,并包含在 CLR 中。 CLR 的源代码也可从 SSCLI20 获得,这是 CLR 的共享源版本,旨在帮助将 .NET 移植到其他操作系统。由于它是在 .NET 2.0 时间范围内发布的,所以它有点过时了,但许多主要的辅助函数仍然是准确的。您可以下载it here

您会在 clr/src/vm/ecall.cpp 中找到 TrySZBinarySearch,这是查找 InternalCall 方法的第一个位置。您会看到它映射到 ArrayHelper::TrySZBinarySearch() C++ 方法,您可以在 clr/src/vm/comarrayhelper.cpp 中找到该方法

没有什么特别有趣的,只是一个简单的二进制搜索算法,专门用于各种简单的值类型。您会在 this answer 中找到它是用 C++ 而不是 C# 编写的原因。

SZ 是从零开始的单维数组的缩写,您将从 C# array[] 声明中获得这种数组。在 C# 中更好地称为“向量”。由于它非常常用,因此进行了大量的微优化。

更新:今天使用 github 提供的 CoreCLR 源代码更容易查看,函数 is here

【讨论】:

【解决方案2】:
[MethodImplAttribute(MethodImplOptions.InternalCall)]

... 在方法声明上表明这是作为本机方法(即通常是 C++/程序集)实现的,而不是在 .NET 中(例如 C#)。您将在 clr\src\vm\comarrayhelpers.cpp 的SSCLI 中找到实现(将进一步的调用留给读者作为练习——Hans Passant 已经解释了你会发现的比我更好的内容):

FCIMPL5(FC_BOOL_RET, ArrayHelper::TrySZBinarySearch, ArrayBase * array, UINT32 index, UINT32 count, Object * value, INT32 * retVal)
    WRAPPER_CONTRACT;
    STATIC_CONTRACT_SO_TOLERANT;

    VALIDATEOBJECTREF(array);
    _ASSERTE(array != NULL);

    if (array->GetRank() != 1 || array->GetLowerBoundsPtr()[0] != 0)
        FC_RETURN_BOOL(FALSE);

    _ASSERTE(retVal != NULL);
    _ASSERTE(index <= array->GetNumComponents());
    _ASSERTE(count <= array->GetNumComponents());
    _ASSERTE(array->GetNumComponents() >= index + count);
    *retVal = 0xdeadbeef;  // Initialize the return value.
    // value can be NULL, but of course, will not be in primitive arrays.
    TypeHandle arrayTH = array->GetArrayElementTypeHandle();
    const CorElementType arrayElType = arrayTH.GetVerifierCorElementType();
    if (!CorTypeInfo::IsPrimitiveType(arrayElType))
        FC_RETURN_BOOL(FALSE);
    // Handle special case of looking for a NULL object in a primitive array.
    if (value == NULL) {
        *retVal = -1;
        FC_RETURN_BOOL(TRUE);
    }

    TypeHandle valueTH = value->GetTypeHandle();
    if (arrayTH != valueTH)
        FC_RETURN_BOOL(FALSE);

    switch(arrayElType) {
    case ELEMENT_TYPE_I1:
        *retVal = ArrayHelpers<I1>::BinarySearchBitwiseEquals((I1*) array->GetDataPtr(), index, count, *(I1*)value->UnBox());
        break;

    case ELEMENT_TYPE_U1:
    case ELEMENT_TYPE_BOOLEAN:
        *retVal = ArrayHelpers<U1>::BinarySearchBitwiseEquals((U1*) array->GetDataPtr(), index, count, *(U1*)value->UnBox());
        break;

    case ELEMENT_TYPE_I2:
        *retVal = ArrayHelpers<I2>::BinarySearchBitwiseEquals((I2*) array->GetDataPtr(), index, count, *(I2*)value->UnBox());
        break;

    case ELEMENT_TYPE_U2:
    case ELEMENT_TYPE_CHAR:
        *retVal = ArrayHelpers<U2>::BinarySearchBitwiseEquals((U2*) array->GetDataPtr(), index, count, *(U2*)value->UnBox());
        break;

    case ELEMENT_TYPE_I4:
        *retVal = ArrayHelpers<I4>::BinarySearchBitwiseEquals((I4*) array->GetDataPtr(), index, count, *(I4*)value->UnBox());
        break;

    case ELEMENT_TYPE_U4:
        *retVal = ArrayHelpers<U4>::BinarySearchBitwiseEquals((U4*) array->GetDataPtr(), index, count, *(U4*)value->UnBox());
        break;

    case ELEMENT_TYPE_R4:
        *retVal = ArrayHelpers<R4>::BinarySearchBitwiseEquals((R4*) array->GetDataPtr(), index, count, *(R4*)value->UnBox());
        break;

    case ELEMENT_TYPE_I8:
        *retVal = ArrayHelpers<I8>::BinarySearchBitwiseEquals((I8*) array->GetDataPtr(), index, count, *(I8*)value->UnBox());
        break;

    case ELEMENT_TYPE_U8:
        *retVal = ArrayHelpers<U8>::BinarySearchBitwiseEquals((U8*) array->GetDataPtr(), index, count, *(U8*)value->UnBox());
        break;

    case ELEMENT_TYPE_R8:
        *retVal = ArrayHelpers<R8>::BinarySearchBitwiseEquals((R8*) array->GetDataPtr(), index, count, *(R8*)value->UnBox());
        break;

    case ELEMENT_TYPE_I:
    case ELEMENT_TYPE_U:
        // In V1.0, IntPtr & UIntPtr are not fully supported types.  They do 
        // not implement IComparable, so searching & sorting for them should
        // fail.  In V1.1 or V2.0, this should change.  --                                   
        FC_RETURN_BOOL(FALSE);

    default:
        _ASSERTE(!"Unrecognized primitive type in ArrayHelper::TrySZBinarySearch");
        FC_RETURN_BOOL(FALSE);
    }
    FC_RETURN_BOOL(TRUE);
FCIMPLEND

各种方法名称中的SZ 指的是“S单维,Z基于ero”的数组。这就是你会得到的,例如:

int[] myArray;

MyObject[] myArray;

... 与 .NET 中另一种更通用的数组不同,它可以是多个维度:

int[,] myArray;

...或者可能具有不同于 0 的下限:

// Creates a single-dimensional array of size 10 with a lower bound of 5
// - as far as I recall C# doesn't have any dedicated declaration for this.
// It's mainly there to support other languages:
Array myArray = Array.CreateInstance(
   typeof(int), 
   new int[] { 10 }, 
   new int[] { 5 }
);

SZ 阵列的性能更高、优化程度更高,因此通常是首选。这就是为什么您会在 CLR 代码中看到如此多对它们的引用(因此也是在上述代码的早期检查排名和下限的原因)。

【讨论】:

    猜你喜欢
    • 2013-03-08
    • 2013-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-01
    • 2014-11-17
    相关资源
    最近更新 更多