有效地生成静态地图

Generating a Static Map Efficiently

本文关键字:地图 静态 有效地      更新时间:2023-10-16

我正在用c++编写一个简单的解析器,使用字符串'触发器'到'处理程序'函数指针的映射,我的问题是实现生成和访问映射的最'静态'和最有效的方法是什么?

我首先考虑了一个方法,例如Parser::add_handler,它会将触发器/处理程序添加到解析器的映射,但是,据我所知,这将需要在每次程序运行时执行,而数据在编译时是已知的。(虽然从好的方面来说,它们只需要执行一次,而不是每个实例化的Parser。)

然后我想到使用一个虚拟方法,例如在Parser中的Parser::get_handlers,它将在派生类中实现,以返回该解析器的处理程序映射。这似乎是一个封装得更好的解决方案,尽管它需要对每个创建的解析器实例调用虚拟函数,并至少调用解析器的映射生成函数。

目前使用后一种方法似乎更可取,但它仍然会在每次执行时动态生成映射,是否有办法避免这种情况?

如果你不想动态地构建地图,你可以使用一个带std::lower_bound的排序静态数组在O(log n)时间内搜索它。

如果您有一个好的散列映射实现可用,您可能会发现填充它的开销小于运行时的性能增益,这取决于您需要执行多少次查找。

如果你在编译时知道所有的字符串和它们的处理程序,并且想在运行时匹配它们,如果你没有很多字符串并且它们的长度不是很长,如果这些字符串中可能的字符范围不是很大(例如在ASCII中),那么你可以在编译时使用模板或简单地通过自己硬编码来构建一个静态调度表。这种方法也可能导致糟糕的性能,这取决于字符串的类型和字符串的数量。

或者,您可以为每个字符串的长度创建一个数组,其中键是该字符串中的字符(其数字表示形式)的和。

但是我建议你从Google的dense_hash_map开始。不要过早地花太多时间优化软件。一旦你的程序完成了,如果你对性能不满意,使用profiler找到瓶颈并改进性能。

好运。

首先,它看起来有点小题大做,你是想浪费一些时间来优化一些不需要优化的东西,还是你只是好奇?

这取决于……如果函数名都是已知的,那么这就取决于函数的数量。您可以简单地以这种方式创建字符串指针数组,以便对指针进行排序,然后进行手动搜索。如果你的列表中只有几个函数,那么你可以做线性搜索。如果您有很多,那么出于性能原因,手动执行二进制搜索(但是指针必须按排序顺序排列)。

但是,对于实际工作,绝不要使用std::map!它是专门为您的情况设计的:插入是昂贵的,但是一旦创建了映射,搜索就非常快(二分搜索)。您将得到干净正确的代码。

如果想要超级高效,可以创建解析树作为Plain Old数据结构。如果您希望很好地完成这一点,您可以创建一个程序,它根据规范生成解析树(这就是lex/flex为您所做的),然后将生成的数据结构编译到您的程序中。

您不需要add_handler,因为所有数据都是在编译时添加的。

话虽如此,对于大多数任务,您可能不需要这种级别的优化,因此我总是建议首先获得正确的功能,然后再考虑如何使用这些方法进行优化。

根据您的需求有许多有趣的解决方案。
比第二种方法更快的方法是在每个触发器都有函数指针成员的类上模板Parser,因此根本没有运行时开销。事实上,这是唯一允许内联的解决方案。但是它有一个限制,一个类必须拥有所有的处理程序。

template<class handler>
struct Parser {
    void ParseLines() {
        if (lineBeginsWith('+')
            handler.lineBeginsPlus();
        if (lineBeginsWith('-')
            handler.lineBeginsMinus();
    }
};
struct LineHandlers {
    void lineBeginsPlus() {
        printf("handle + line");
    }
    void lineBeginsMinus() {
        printf("handle - line");
    }
};
struct OtherLineHandlers {
    void lineBeginsPlus() {
        printf("handle + line differently");
    }
    void lineBeginsMinus() {
        printf("handle - line differently");
    }
};
int main() {
    Parser<LineHandlers> ParserInstance;
    ParserInstance.ParseLines();
    Parser<OtherLineHandlers> OtherParserInstance;
    OtherParserInstance.ParseLines();
}