切换语句有大量案例

如果switch case超过5000,会发生什么。 有什么缺点以及如何用更快的东西替换它?

注意:我不希望使用数组来存储案例,因为它是相同的。

没有特别的理由认为除了转换/案例陈述之外你还想要任何其他东西(实际上我会积极地期望它没有用)。 编译器应该创建有效的调度代码,这可能涉及静态[稀疏]表和直接索引,二进制分支等的某种组合。 它可以深入了解案例的静态值,并且应该做得很好(每次更改案例时都会重新调整它,而新的值不适合手工制作的方法 – 例如极不相同的值当你有一个非常紧凑的数组查找 – 可能需要重新编写代码或静默导致内存膨胀或性能下降)。

当C试图赢得核心汇编程序员时,人们真正关心这种事情……编译器对生成好的代码负责。 换句话说 – 如果它没有(可测量地)坏了,就不要修理它。

更一般地说,对这种事情感到好奇并获得人们关于替代方案及其性能影响的想法是很棒的 ,但是如果你真的关心并且性能差异可以对你的程序产生有用的影响(特别是如果分析表明它),那么总是基准你的程序做实际工作。

作为思考的东西……如果一个人可能会被一个旧的/错误/低效的编译器困住,或者只是喜欢黑客攻击。

switch语句的内部工作由两部分组成。 寻找跳跃的地址,并在那里跳得很好。 对于第一部分,您需要使用表来查找地址。 如果案例数增加,表变大 – 搜索地址跳转需要时间。 这是编译器试图优化的点,结合了几种技术,但一种简单的方法是直接使用表,这取决于案例值空间。

在卫生巾背面的例子;

 switch (n) { case 1: foo(); break; case 2: bar(); break; case 3: baz(); break; } 

使用这样的代码编译器可以创建一个jump_addresses数组,并通过array [n]直接获取地址。 现在搜索只拿O(1)。 但如果你有一个如下所示的开关:

 switch (n) { case 10: foo(); break; case 17: bar(); break; case 23: baz(); break; // and a lot other } 

编译器需要生成一个包含case_id,jump_address对和代码的表来搜索该结构,其中最差的实现可以采用O(n)。 (体面的编译器通过启用它们的优化标志,在你需要调试这样的优化代码时,你的大脑开始煎炸,完全释放出这种情况,从而优化地狱。)

那么问题是你可以在C级自己完成这个以击败编译器吗? 有趣的是,在创建表格和搜索它们时似乎很容易,使用goto跳转到变量点在标准C中是不可能的。因此,如果由于开销或代码结构而不打算使用函数指针,如果你没有使用GCC你就会陷入困境。 GCC有一个名为Labels as Values的非标准function,可帮助您获取标签指针。

要完成该示例,您可以使用“标签为值”function编写第二个switch语句,如下所示:

 const void *cases[] = {&&case_foo, &&case_bar, &&case_baz, ....}; goto *labels[n]; case_foo: foo(); goto switch_end; case_bar: bar(); goto switch_end; case_baz: baz(); goto switch_end; // and a lot other switch_end: 

当然,如果您正在讨论5000个案例,那么如果您编写一段代码来为您创建此代码会好得多 – 而且它可能只是维护此类软件的方法。

作为结尾说明; 这会改善你的日常工作吗? 不会。这会提高你的技能吗? 是的,从经验谈起,我曾经发现自己只是通过优化案例值来改进智能卡中的安全算法。 这是一个奇怪的世界。

尝试使用具有Delegate值的Dictionary类。 至少它使代码更具可读性。

Big switch语句,通常是自动生成的语句,可能需要很长时间才能编译。 但我喜欢编译器优化switch语句的想法。

拆分switch语句的一种方法是使用bucketing,

 int getIt(int input) { int bucket = input%16; switch(bucket) { case 1: return getItBucket1(input); case 2: return getItBucket2(input); ... ... } return -1; } 

所以在上面的代码中,我们将switch语句拆分为16个部分。 在自动生成的代码中更改桶的数量很容易。

此代码增加了一层间接或函数调用的运行时成本。 。 但考虑到在不同文件中定义的存储区,并行编译它们会更快。