mirror of
https://github.com/NixOS/nix.git
synced 2025-11-16 07:22:43 +01:00
libexpr: Actually cache line information in PosTable
Previous code had a sneaky bug due to which no caching
actually happened:
```cpp
auto linesForInput = (*lines)[origin->offset];
```
That should have been:
```cpp
auto & linesForInput = (*lines)[origin->offset];
```
See [1].
Now that it also makes sense to make the cache bound in side
in order not to memoize all the sources without freeing any memory.
The default cache size has been chosen somewhat arbitrarily to be ~64k
origins. For reference, 25.05 nixpkgs has ~50k .nix files.
Simple benchmark:
```nix
let
pkgs = import <nixpkgs> { };
in
builtins.foldl' (acc: el: acc + el.line) 0 (
builtins.genList (x: builtins.unsafeGetAttrPos "gcc" pkgs) 10000
)
```
(After)
```
$ hyperfine "result/bin/nix eval -f ./test.nix"
Benchmark 1: result/bin/nix eval -f ./test.nix
Time (mean ± σ): 292.7 ms ± 3.9 ms [User: 131.0 ms, System: 120.5 ms]
Range (min … max): 288.1 ms … 300.5 ms 10 runs
```
(Before)
```
hyperfine "nix eval -f ./test.nix"
Benchmark 1: nix eval -f ./test.nix
Time (mean ± σ): 666.7 ms ± 6.4 ms [User: 428.3 ms, System: 191.2 ms]
Range (min … max): 659.7 ms … 681.3 ms 10 runs
```
If the origin happens to be a `all-packages.nix` or similar in size then the
difference is much more dramatic.
[1]: 22e3f0e987
This commit is contained in:
parent
4711720efe
commit
5ea81f5b8f
2 changed files with 43 additions and 13 deletions
|
|
@ -15,21 +15,35 @@ Pos PosTable::operator[](PosIdx p) const
|
|||
const auto offset = origin->offsetOf(p);
|
||||
|
||||
Pos result{0, 0, origin->origin};
|
||||
auto lines = this->lines.lock();
|
||||
auto linesForInput = (*lines)[origin->offset];
|
||||
auto linesCache = this->linesCache.lock();
|
||||
|
||||
if (linesForInput.empty()) {
|
||||
auto source = result.getSource().value_or("");
|
||||
const char * begin = source.data();
|
||||
for (Pos::LinesIterator it(source), end; it != end; it++)
|
||||
linesForInput.push_back(it->data() - begin);
|
||||
if (linesForInput.empty())
|
||||
linesForInput.push_back(0);
|
||||
/* Try the origin's line cache */
|
||||
const auto * linesForInput = linesCache->getOrNullptr(origin->offset);
|
||||
|
||||
auto fillCacheForOrigin = [](std::string_view content) {
|
||||
auto contentLines = Lines();
|
||||
|
||||
const char * begin = content.data();
|
||||
for (Pos::LinesIterator it(content), end; it != end; it++)
|
||||
contentLines.push_back(it->data() - begin);
|
||||
if (contentLines.empty())
|
||||
contentLines.push_back(0);
|
||||
|
||||
return contentLines;
|
||||
};
|
||||
|
||||
/* Calculate line offsets and fill the cache */
|
||||
if (!linesForInput) {
|
||||
auto originContent = result.getSource().value_or("");
|
||||
linesCache->upsert(origin->offset, fillCacheForOrigin(originContent));
|
||||
linesForInput = linesCache->getOrNullptr(origin->offset);
|
||||
}
|
||||
// as above: the first line starts at byte 0 and is always present
|
||||
auto lineStartOffset = std::prev(std::upper_bound(linesForInput.begin(), linesForInput.end(), offset));
|
||||
|
||||
result.line = 1 + (lineStartOffset - linesForInput.begin());
|
||||
assert(linesForInput);
|
||||
|
||||
// as above: the first line starts at byte 0 and is always present
|
||||
auto lineStartOffset = std::prev(std::upper_bound(linesForInput->begin(), linesForInput->end(), offset));
|
||||
result.line = 1 + (lineStartOffset - linesForInput->begin());
|
||||
result.column = 1 + (offset - *lineStartOffset);
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue