我有一个我想优化的功能,因为它需要13.4%的程序运行时间。
此函数返回一个相当大的容器,但大多数调用者不需要整个容器,因为它们只是在数据结构中搜索符合特定条件的元素,然后抛出容器。但是,有一些调用者使用整个容器。此外,返回的容器具有众所周知的最大大小,并且在每次调用函数时通常都非常接近该大小。
我希望优化此功能的一种方法是,当调用者只需要搜索特定项目时,不会生成整个数据结构,因为这样可以为这些调用者节省大约一半的时间,因为始终包含搜索的项目。是否可以这样做,并且对于需要整个容器的调用者仍然具有相同的功能?或者,我可以实现一个适用于一种类型的调用者的函数,另一个适用于其他类型的调用者,但让它们以某种方式共享逻辑吗?这就是整个设置的样子:
我想要优化的功能:
vector<Foo> Bar::generate() const {
vector<Foo> results; //Using a vector is arbitrary, it could be any container
results.reserve(100);
int n = 100;
while (n > 0 && this->shouldGenerate(n)) {
n--;
results.emplace_back(...);
}
return results
}
最常见的来电者:
Foo baz(Bar bar) {
vector<Foo> items = bar.generate();
auto it = find_if(items.begin(), items.end(), my_pred);
if (it == items.end()) {
return Foo();
} else {
return *it;
}
}
不太常见的来电者:
void Qux::storeGeneratedFoos(Bar bar) {
this->foos = bar.generate();
}
答案 0 :(得分:1)
您可以尝试在函数中使用static
向量,以避免每次重新分配空间,并返回对它的引用:
const vector<Foo> &Bar::generate() const {
static vector<Foo> results; //Using a vector is arbitrary, it could be any container
results.clear(); //clear from possible previous invocation
...
}
然后baz()
可以定义为
Foo baz(Bar bar) {
const vector<Foo> &items = bar.generate();
...
}
此解决方案无需修改其他功能。
要减少代数,可以按如下方式创建模板函数:
template<class UnaryPredicate>
Foo Bar::generate_if(UnaryPredicate pred) const {
Foo foo;
int n = 100;
while (n > 0 && this->shouldGenerate(n)) {
n--;
//instead of 'results.emplace_back(...);' do
foo = ...;
if (pred(foo))
return foo;
}
return Foo();
}
并将baz()
的定义更改为
Foo baz(Bar bar) {
return bar.generate_if(my_pred);
}
答案 1 :(得分:1)
我建议区分两种用例:这样,你可以更快地获得。由于运行时似乎是一个问题,我会尽量提高效率。
首先,概括发电机:
template <typename Storage>
void Bar::generate_impl(Storage & storage) const {
for (int n = 100;
n > 0 && shouldGenerate(n) and storage.go_on();
--n) {
storage.add(/* some newly built Foo */);
}
}
然后你可以有两种Storage
。第一个是创建vector
的原始用例。如您所见,它不会过早停止并存储每个传递的Foo
:
struct MemorizingStorage {
vector<Foo> data;
MemorizingStorage() { data.reserve(100); }
void add(Foo const & f) { data.emplace_back(f); }
bool go_on() const { return true; }
}; // MemorizingStorage
第二个将用于检查是否生成了一些Foo
的用例。此版本不会存储任何内容,但请记住是否添加了Foo
''是正确的:
struct CheckingStorage {
Foo const & item;
bool found_it;
CheckingStorage(Foo const & f) : item(f), found_it(false) {}
void add(Foo const & f) { found_it = found_it or (item == f); }
bool go_on() const { return not found_it; }
}; // CheckingStorage
对于您的用户,您使用Bar
s:
Storage
个版本
vector<Foo> Bar::generate() const {
MemorizingStorage storage;
generate_impl(storage);
return storage; // consider std::move if C++11 is applicable
}
bool Bar::is_generated(Foo const & item) const {
CheckingStorage storage(item);
generate_impl(storage);
return storage.found_it;
}
这是我能想到的最快的方式:
Foo
是否已生成时,它会提前停止。答案 2 :(得分:1)
一个选项是创建自定义迭代器以从Bar
获取项目。然后你可以直接在find_if
中使用自定义迭代器,或者你可以使用它来使用两个迭代器构造函数或vector
直接初始化assign
项:
class BarIterator : public std::iterator<std::input_iterator_tag, Foo> {
const Bar* bar;
int n;
public:
BarIterator(const Bar& bar, int n);
Foo operator*() const;
bool operator==(const BarIterator& other) const;
bool operator!=(const BarIterator& other) const;
BarIterator& operator++(){ n--; return *this; }
};
class Bar {
...
public:
BarIterator begin() const { return {*this, 100}; }
BarIterator end() const { return {*this, 0}; }
friend class BarIterator;
};
Foo baz(const Bar& bar) {
auto it = std::find_if(bar.begin(), bar.end(), my_pred);
if (it == bar.end()) {
return Foo();
} else {
return *it;
}
}
class Quz {
std::vector<Foo> foos;
public:
void storeGeneratedFoos(const Bar& bar) {
foos.assign(bar.begin(), bar.end());
}
};