zed/crates/gpui/src/shared_string.rs
Piotr Osiewicz 5447821715
gpui/perf: Use SharedString on API boundary of line layout (#22566)
This commit is all about strings, not about line layout at all. When
laying out text, we use a line layout cache to avoid roundtrips to
system layout engine where possible. This makes it so that we might end
up not needing an owned version of text to insert into the cache, as we
might get a cached version.

The API boundary of line layout accepted text to be laid out as &str. It
then performed cache lookup (which didn't require having an owned
version) and only resorted to making an owned version when needed. As it
turned out though, exact cache hits are quite rare and we end up needing
owned version more often than not. The callers of line layout either
dealt with SharedStrings or owned Strings. Due to coercing them into
&str, we were ~always copying text into a new string (unless there was a
same-frame-hit). This is a bit wasteful, thus this PR generifies the API
a bit to make it easier to reuse existing string allocations if there
are any.

Benchmark scenario: scrolling down page-by-page through editor_tests (I
ran the same scenario twice):

![1](https://github.com/user-attachments/assets/8cd09692-2699-41d9-b211-83554d93902f)

![2](https://github.com/user-attachments/assets/d11f7c22-2315-4261-8189-2356baf5d2f7)


Release Notes:

- N/A
2025-01-02 11:06:01 +00:00

128 lines
3 KiB
Rust

use derive_more::{Deref, DerefMut};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::{borrow::Borrow, sync::Arc};
use util::arc_cow::ArcCow;
/// A shared string is an immutable string that can be cheaply cloned in GPUI
/// tasks. Essentially an abstraction over an `Arc<str>` and `&'static str`,
#[derive(Deref, DerefMut, Eq, PartialEq, PartialOrd, Ord, Hash, Clone)]
pub struct SharedString(ArcCow<'static, str>);
impl SharedString {
/// Creates a static [`SharedString`] from a `&'static str`.
pub const fn new_static(str: &'static str) -> Self {
Self(ArcCow::Borrowed(str))
}
}
impl JsonSchema for SharedString {
fn schema_name() -> String {
String::schema_name()
}
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
String::json_schema(gen)
}
}
impl Default for SharedString {
fn default() -> Self {
Self(ArcCow::Owned(Arc::default()))
}
}
impl AsRef<str> for SharedString {
fn as_ref(&self) -> &str {
&self.0
}
}
impl Borrow<str> for SharedString {
fn borrow(&self) -> &str {
self.as_ref()
}
}
impl std::fmt::Debug for SharedString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::fmt::Display for SharedString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.as_ref())
}
}
impl PartialEq<String> for SharedString {
fn eq(&self, other: &String) -> bool {
self.as_ref() == other
}
}
impl PartialEq<SharedString> for String {
fn eq(&self, other: &SharedString) -> bool {
self == other.as_ref()
}
}
impl PartialEq<str> for SharedString {
fn eq(&self, other: &str) -> bool {
self.as_ref() == other
}
}
impl<'a> PartialEq<&'a str> for SharedString {
fn eq(&self, other: &&'a str) -> bool {
self.as_ref() == *other
}
}
impl From<&SharedString> for SharedString {
fn from(value: &SharedString) -> Self {
value.clone()
}
}
impl From<SharedString> for Arc<str> {
fn from(val: SharedString) -> Self {
match val.0 {
ArcCow::Borrowed(borrowed) => Arc::from(borrowed),
ArcCow::Owned(owned) => owned.clone(),
}
}
}
impl<T: Into<ArcCow<'static, str>>> From<T> for SharedString {
fn from(value: T) -> Self {
Self(value.into())
}
}
impl From<SharedString> for String {
fn from(val: SharedString) -> Self {
val.0.to_string()
}
}
impl Serialize for SharedString {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.as_ref())
}
}
impl<'de> Deserialize<'de> for SharedString {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(SharedString::from(s))
}
}