C# 与 C++ 静态数组中静态常量列表初始化的效率
Posted
技术标签:
【中文标题】C# 与 C++ 静态数组中静态常量列表初始化的效率【英文标题】:Efficiency of static constant list initialization in C# vs C++ static arrays 【发布时间】:2019-03-25 13:33:04 【问题描述】:我提前道歉。我的域主要是 C(和 C++)。我正在尝试在 C# 中编写类似的东西。让我用代码解释一下。
在 C++ 中,我可以使用在编译时处理并存储在 PE 文件的只读部分中的大型静态数组。例如:
typedef struct _MY_ASSOC
const char* name;
unsigned int value;
MY_ASSOC, *LPMY_ASSOC;
bool GetValueForName(const char* pName, unsigned int* pnOutValue = nullptr)
bool bResult = false;
unsigned int nValue = 0;
static const MY_ASSOC all_assoc[] =
"name1", 123,
"name2", 213,
"name3", 1433,
//... more to follow
"nameN", 12837,
;
for(size_t i = 0; i < _countof(all_assoc); i++)
if(strcmp(all_assoc[i].name, pName) == 0)
nValue = all_assoc[i].value;
bResult = true;
break;
if(pnOutValue)
*pnOutValue = nValue;
return bResult;
在上面的例子中,static const MY_ASSOC all_assoc
的初始化不会在运行时被调用。它在编译时完全处理。
现在如果我用 C# 写类似的东西:
public struct NameValue
public string name;
public uint value;
private static readonly NameValue[] g_arrNV_Assoc = new NameValue[]
new NameValue() name = "name1", value = 123 ,
new NameValue() name = "name2", value = 213 ,
new NameValue() name = "name3", value = 1433 ,
// ... more to follow
new NameValue() name = "nameN", value = 12837 ,
;
public static bool GetValueForName(string name, out uint nOutValue)
foreach (NameValue nv in g_arrNV_Assoc)
if (name == nv.name)
nOutValue = nv.value;
return true;
nOutValue = 0;
return false;
private static readonly NameValue[] g_arrNV_Assoc
行必须在宿主类初始化期间调用一次,并且针对该数组中的每个元素执行此操作!
所以我的问题是——我能否以某种方式对其进行优化,以便将存储在 g_arrNV_Assoc
数组中的数据存储在 PE 部分中而不是在运行时初始化?
附言。我希望我的术语能让 .NET 人员清楚。
【问题讨论】:
我对 c 和 c++ 一无所知,但在 c# 中,readonly
和 const
意味着不同的东西。 const
在编译时被替换为它在整个代码中的值。 readonly
仅仅意味着任何变量只能在创建类型的实例时分配 - static readonly
意味着变量只能在类型初始化过程中分配,这是一个您无法控制的 clr 后台进程在你的代码中。
g_arrNV_Assoc
看起来是一个相当低效的数据结构,因为你是 foreach
'ing name == nv.name
这应该是一个字典,是的,它会在第一次使用时加载一次。并且对GetValueForName
的每个后续调用都只会查找一个哈希表。我的意思是你还能做什么,除了在加载时加载一个非托管内存,并通过它迭代指针以获得你想要的结果。你到底想在这里实现什么,更好的加载时间?
@TheGeneral:好吧,当然我并没有声称我的查找算法的效率。我只是在询问数组初始化部分。我选择它只是因为缺少打字。所以是的,使用字典(即使在 C++ 中)也需要时间来加载它。
为了在 C 中更有效地查找,我会按名称按字母顺序对静态数组进行排序(在源代码中,可能通过 PY 脚本),并在我的函数中使用二进制搜索算法.但正如你可以想象的那样,它需要的代码比我上面显示的要多得多,因此我选择了一个更简单的例子。但同样,那部分与我的问题无关。这与有效加载大型静态(即不变/不可变)数组有关。 (无论您在 .net 中如何称呼它:列表、字典、地图等)
【参考方案1】:
确实术语足够了,大型静态数组就可以了。
您实际上无法做任何事情来提高开箱即用的效率。
它最初会加载一次(在不同的时间取决于 .net 的版本以及是否有静态构造函数)。但是,它会在您调用它之前加载。
即使您只使用预定的大小将其创建为空,CLR 仍会将每个元素初始化为默认值,然后您必须以某种方式缓冲复制数据,而这些数据又必须从文件中加载。
问题是
与您在 C 中执行的操作相比,加载结构的默认静态数组实际上有多少开销 在应用程序的生命周期中加载时是否重要如果这太过分了(我已经假设你已经确定了),还有哪些其他选项可能在框外可用?
您可以预先分配一块非托管内存,然后从某处读取并复制字节,然后使用指针依次访问。
您也可以像其他非托管 DLL 一样在标准 Dll、Pinvoke 中创建它。但是,我不确定您是否会在这里获得很多免费午餐,因为编组此类调用以加载您的 dll 会产生开销。
如果您的问题只是学术问题,那么这些确实是您唯一的选择。但是,如果这实际上是您遇到的性能问题,则需要尝试对其进行基准测试以进行微优化,并尝试找出适合您的方法。
无论如何,我并不自称什么都知道,也许其他人有更好的想法或更多信息。祝你好运
【讨论】:
嗯,是的,这更像是一种学术好奇心。我没有在那里加载 GB 的数据。 IDK,如果我不需要的话,我只是不喜欢浪费 CPU 周期。另外,我想我(仍在)学习 C#。所以谢谢你的解释。以上是关于C# 与 C++ 静态数组中静态常量列表初始化的效率的主要内容,如果未能解决你的问题,请参考以下文章