perf: cache current line number in location

`location::line_num()` function used to be implemented by using
`std::count`, so each time the parser encounters a type mismatch,
`std::count` was called with almost whole file. It decelerates the
parsing process too much, so I decided to add `line_number_` member
variable to `location` and add `advance/retrace/reset` to `location`
in order to modify the position that is pointed.
This commit is contained in:
ToruNiina
2019-04-12 18:32:46 +09:00
parent ea13e40889
commit 6f950c9ec8
4 changed files with 108 additions and 69 deletions

View File

@@ -71,7 +71,7 @@ struct location final : public region_base
"container should be randomly accessible");
location(std::string name, Container cont)
: source_(std::make_shared<Container>(std::move(cont))),
: source_(std::make_shared<Container>(std::move(cont))), line_number_(0),
source_name_(std::move(name)), iter_(source_->cbegin())
{}
location(const location&) = default;
@@ -82,18 +82,54 @@ struct location final : public region_base
bool is_ok() const noexcept override {return static_cast<bool>(source_);}
const_iterator& iter() noexcept {return iter_;}
const_iterator iter() const noexcept {return iter_;}
// this const prohibits codes like `++(loc.iter())`.
const const_iterator iter() const noexcept {return iter_;}
const_iterator begin() const noexcept {return source_->cbegin();}
const_iterator end() const noexcept {return source_->cend();}
const_iterator begin() const noexcept {return source_->cbegin();}
const_iterator end() const noexcept {return source_->cend();}
// XXX At first, `location::line_num()` is implemented using `std::count` to
// count a number of '\n'. But with a long toml file (typically, 10k lines),
// it becomes intolerably slow because each time it generates error messages,
// it counts '\n' from thousands of characters. To workaround it, I decided
// to introduce `location::line_number_` member variable and synchronize it
// to the location changes the point to look. So an overload of `iter()`
// which returns mutable reference is removed and `advance()`, `retrace()`
// and `reset()` is added.
void advance(std::size_t n = 1) noexcept
{
this->line_number_ += std::count(this->iter_, this->iter_ + n, '\n');
this->iter_ += n;
return;
}
void retrace(std::size_t n = 1) noexcept
{
this->line_number_ -= std::count(this->iter_ - n, this->iter_, '\n');
this->iter_ -= n;
return;
}
void reset(const_iterator rollback) noexcept
{
// since c++11, std::distance works in both ways and returns a negative
// value if `first` is ahead from `last`.
if(0 <= std::distance(rollback, this->iter_)) // rollback < iter
{
this->line_number_ -= std::count(rollback, this->iter_, '\n');
}
else // iter < rollback [[unlikely]]
{
this->line_number_ += std::count(this->iter_, rollback, '\n');
}
this->iter_ = rollback;
return;
}
std::string str() const override {return make_string(1, *this->iter());}
std::string name() const override {return source_name_;}
std::string line_num() const override
{
return std::to_string(1+std::count(this->begin(), this->iter(), '\n'));
return std::to_string(this->line_number_);
}
std::string line() const override
@@ -132,6 +168,7 @@ struct location final : public region_base
private:
source_ptr source_;
std::size_t line_number_;
std::string source_name_;
const_iterator iter_;
};