-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Source rendering is really slow #9
Comments
Just so you know, that bug probably won't significantly impact syntect performance, but the eventual switch to fancy-regex might. Although syntect should highlight 15,000 lines of Rust per second according to my benchmarks, so if highlighting is taking too much time maybe you can either use syntect's features to only highlight what you need to display, or maybe the real culprit is this line of code which reallocates and copies the entire string, taking time quadratic in the length of the string. |
When trishume/syntect#182 gets merged I can switch https://github.com/oli-obk/priroda/blob/master/src/render/source.rs#L10-L21 to lazy_static, which is nicer.
That may be possible. (Edit: hopefully removed the quadratic complexity. Can't measure its impact at the moment) |
Huh, do you know how much code you usually need to highlight to render a page? Provided you're compiling in release mode you should get 15k lines/sec like I said, which should be sufficient for most applications, but do you need to highlight a ton of code per page? There's a few syntaxes and cases where syntect hits exponential backtracking in the regex engine and becomes super slow, if you're not highlighting a lot of code, could you post a gist of the test file you're using? |
200-2000 lines I think
Forgot to use release mode. It does solve the slowness for shortish files, but doesn't fix it for longer files.
It is from the rustc: https://gist.github.com/bjorn3/a7c6e9fbe570befd93ec463f14047248 (please note that my code inserts a comment not shown here to give a part of the code a different background. |
So that file takes about 60ms to highlight for me, which is about as fast as can be expected from syntect at this time. If an extra 60ms on your page loads because of highlighting on large files like that is a problem, then yah at the moment there's nothing I can suggest, other than maybe only highlighting a smaller region you need or some kind of caching. If you're seeing syntect take substantially longer than 60ms to highlight that file in release mode, then maybe the problem is somewhere else that can be fixed. |
Same for me. However I got 200ms in release mode for https://gist.github.com/bjorn3/4ea8ba8a703b93973f9feb8bf8eb4c36 (4800 lines) |
To quote myself:
It is down to ~80% in release mode |
I could try that, but I don't know where I can begin that region without beginning in the middle of a string. |
One option if you think that the tradeoff between page load time and having syntax highlighting tips over at some point, is you could disable highlighting only for files larger than a certain number of lines. I think a lot of users would probably prefer the typical <500 line file be highlighted even if their pages take an extra 30ms to load, but you know your use case better than me. Also judging by the description in the readme of what this project does, if interactive actions you want to be snappy like stepping are currently re-highlighting the file, you could consider caching the highlighted file to avoid re-highlighting every step. |
+1
Is there an api to map from source bytes to highlighted bytes? That would be needed to cache, without losing the ability to mark the currently executed part of the code. |
Two options for caching:
I'll think about introducing a feature in syntect to make this easier if I can think of a way, I think some of the static site generators also want a way to highlight parts of the code. Edit: if I find time today I might try my hand at writing the trickiest bit of logic and adding it as a helper to syntect, which is a function |
That could work! It is also cleaner than using comments. |
Just wrote it myself: // Split between i and i+1
fn split<'s, A: Clone>(mut s: &[(A, &'s str)], mut i: usize) -> (Vec<(A, &'s str)>, Vec<(A, &'s str)>) {
let mut before = Vec::new();
let mut after = Vec::new();
while s.len() > 0 && s[0].1.len() < i {
i -= s[0].1.len();
before.push(s[0].clone());
s = &s[1..];
}
if s.len() > 0 {
let (data, str) = s[0].clone();
s = &s[1..];
before.push((data.clone(), &str[..i]));
after.push((data, &str[i..]));
}
after.extend(s.iter().cloned());
(before, after)
} Edit: it has a bug :( |
Oh lol I wrote one almost immediately after I wrote that comment but didn't submit it since I hadn't written tests yet, sorry. Looks like we did it in almost exactly the same way, although I haven't tested if mine compiles or works yet: /// Split a highlighted line at a byte index in the line into a before and
/// after component. It's just a helper that does the somewhat tricky logic
/// including splitting a span if the index lies on a boundary.
///
/// This can be used to extract a chunk of the line out for special treatment
/// like wrapping it in an HTML tag for extra styling.
pub fn split_at(mut v: &[(Style, &str)], mut split_i: usize) -> (Vec<(Style, &str)>, Vec<(Style, &str)>) {
// Consume all tokens before the split
let before = Vec::new();
for tok in v {
if tok.1.len() > split_i {
break;
}
before.push(tok.clone());
split_i -= tok.1.len();
}
v = &v[before.len()..];
let after = Vec::new();
// If necessary, split the token the split falls inside
if v.len() > 0 && split_i > 0 {
let (sa, sb) = v[0].1.split_at(split_i);
before.push((v[0].0.clone(), sa));
after.push((v[0].0.clone(), sb));
v = &v[1..];
}
after.extend_from_slice(v);
return (before, after);
} Anyway, I'm going to try to get around to contributing it in a PR to syntect soon, along with some tests and a higher level helper that uses it to apply a |
You don't have to use Your version is better overall I think. |
When the cache is hit, source rendering now requires ~3ms :) |
Yay nice! This project looks pretty cool, good luck with it! I'll try and remember to tag you when I add the helper to syntect so you can remove the code from your project if you want, not that it would make any difference. Thanks for the digging about |
It is responsible for more than ~90% of cpu time during single stepping through
liballoc
.The text was updated successfully, but these errors were encountered: