lasso

lasso

高效灵活的Rust字符串内化库

lasso是一个Rust实现的字符串内化库,支持多线程和单线程操作。它提供Rodeo等多种内化器,实现O(1)复杂度的字符串内化和解析。lasso具有内存占用小、可自定义键范围、支持多种哈希算法等特点,适用于内存敏感场景。此外,它还支持no_std环境和序列化功能。

lasso字符串内化多线程单线程RodeoGithub开源项目

CI 安全审计 覆盖率 Docs.rs Crates.io

一个多线程和单线程的字符串驻留工具,允许以最小的内存占用缓存字符串,并将其与唯一的[键]关联,可随时用于检索。Rodeo提供O(1)复杂度的驻留和解析操作,可转换为RodeoReader以实现无竞争的解析,支持键到字符串和字符串到键的操作。还可以转换为仅支持键到字符串操作的RodeoResolver,以实现最低的内存使用。

我应该使用哪个驻留器?

对于单线程工作负载,建议使用Rodeo,而多线程应用程序应使用ThreadedRodeo。这两种方式是驻留字符串的唯一方法,但大多数应用程序在完成字符串驻留后,需要在RodeoReaderRodeoResolver之间做出选择。如果用户仍需要获取字符串的键,则必须使用RodeoReader(尽管仍可以转换为RodeoResolver)。对于只需要键到字符串解析的用户,RodeoResolver提供无竞争访问,且内存使用量最小。注意,要使用ThreadedRodeo,需要启用multi-threaded功能。

驻留器线程安全驻留字符串字符串到键键到字符串无竞争内存使用
RodeoN/A中等
ThreadedRodeo最多
RodeoReader中等
RodeoResolver最少

Cargo 功能

默认情况下,lasso只有一个依赖项hashbrown,并且只暴露Rodeo。使用Hashbrown是因为标准库的hashmap中的raw_entry API目前还不稳定。原始hashmap API用于哈希表中的自定义哈希,这大大减少了内存使用。要使用ThreadedRodeo,必须启用multi-threaded功能。

  • multi-threaded - 启用ThreadedRodeo,用于多线程任务的驻留器
  • ahasher - 使用ahashRandomState作为默认哈希器
  • no-std - 为RodeoThreadedRodeo启用no_std + alloc支持
    • 自动启用以下必需功能:
      • ahasher - no_std哈希函数
  • serialize - 为所有Spur类型和所有驻留器实现SerializeDeserialize
  • inline-more - 用#[inline]注释外部API

示例:使用Rodeo

use lasso::Rodeo; let mut rodeo = Rodeo::default(); let key = rodeo.get_or_intern("Hello, world!"); // 轻松检索键的值并查找值的键 assert_eq!("Hello, world!", rodeo.resolve(&key)); assert_eq!(Some(key), rodeo.get("Hello, world!")); // 再次驻留相同的字符串将产生相同的键 let key2 = rodeo.get_or_intern("Hello, world!"); assert_eq!(key, key2);

示例:使用ThreadedRodeo

use lasso::ThreadedRodeo; use std::{thread, sync::Arc}; let rodeo = Arc::new(ThreadedRodeo::default()); let key = rodeo.get_or_intern("Hello, world!"); // 轻松检索键的值并查找值的键 assert_eq!("Hello, world!", rodeo.resolve(&key)); assert_eq!(Some(key), rodeo.get("Hello, world!")); // 再次驻留相同的字符串将产生相同的键 let key2 = rodeo.get_or_intern("Hello, world!"); assert_eq!(key, key2); // ThreadedRodeo可以在线程间共享 let moved = Arc::clone(&rodeo); let hello = thread::spawn(move || { assert_eq!("Hello, world!", moved.resolve(&key)); moved.get_or_intern("Hello from the thread!") }) .join() .unwrap(); assert_eq!("Hello, world!", rodeo.resolve(&key)); assert_eq!("Hello from the thread!", rodeo.resolve(&hello));

示例:创建RodeoReader

use lasso::Rodeo; // Rodeo和ThreadedRodeo在这里可以互换 let mut rodeo = Rodeo::default(); let key = rodeo.get_or_intern("Hello, world!"); assert_eq!("Hello, world!", rodeo.resolve(&key)); let reader = rodeo.into_reader(); // Reader保留了父对象的所有字符串 assert_eq!("Hello, world!", reader.resolve(&key)); assert_eq!(Some(key), reader.get("Hello, world!")); // 现在Reader可以在线程间共享,无论是由哪种Rodeo创建的

示例:创建RodeoResolver

use lasso::Rodeo; // Rodeo和ThreadedRodeo在这里可以互换 let mut rodeo = Rodeo::default(); let key = rodeo.get_or_intern("Hello, world!"); assert_eq!("Hello, world!", rodeo.resolve(&key)); let resolver = rodeo.into_resolver(); // Resolver保留了父对象的所有字符串 assert_eq!("Hello, world!", resolver.resolve(&key)); // 现在Resolver可以在线程间共享,无论是由哪种Rodeo创建的

示例:创建自定义范围的键

有时你希望你的键只占用(或不占用)某个特定的值范围,这样你就可以有自定义的[空位]。这允许你在原本未使用的空间中打包更多数据,这对内存敏感的应用程序至关重要。

use lasso::{Key, Rodeo}; // 首先创建我们的键类型,这将作为我们驻留器的句柄 #[derive(Copy, Clone, PartialEq, Eq)] struct NicheKey(u32); // 这将为我们保留上面255个值作为空位 const NICHE: usize = 0xFF000000; // 实现`Key`是不安全的,并且要求任何给予`try_from_usize`的内容在后续调用`into_usize`时必须产生相同的`usize` unsafe impl Key for NicheKey { fn into_usize(self) -> usize { self.0 as usize } fn try_from_usize(int: usize) -> Option<Self> { if int < NICHE { // 该值不在我们的空位范围内,所以可以使用 Some(Self(int as u32)) } else { // 该值与我们的空位冲突,所以返回`None` None } } } // 为确保我们遵守`Key`的安全合约,让我们做两个小测试 #[test] fn value_in_range() { let key = NicheKey::try_from_usize(0).unwrap(); assert_eq!(key.into_usize(), 0); let key = NicheKey::try_from_usize(NICHE - 1).unwrap(); assert_eq!(key.into_usize(), NICHE - 1); } #[test] fn value_out_of_range() { let key = NicheKey::try_from_usize(NICHE); assert!(key.is_none()); let key = NicheKey::try_from_usize(u32::max_value() as usize); assert!(key.is_none()); } // 现在我们完成了,可以创建使用我们自定义键的`Rodeo`或`ThreadedRodeo`! let mut rodeo: Rodeo<NicheKey> = Rodeo::new(); let key = rodeo.get_or_intern("It works!"); assert_eq!(rodeo.resolve(&key), "It works!");

示例:使用FromIterator创建

use lasso::Rodeo; use core::iter::FromIterator; // 适用于`Rodeo`和`ThreadedRodeo` let rodeo = Rodeo::from_iter(vec![ "one string", "two string", "red string", "blue string", ]); assert!(rodeo.contains("one string")); assert!(rodeo.contains("two string")); assert!(rodeo.contains("red string")); assert!(rodeo.contains("blue string"));
use lasso::Rodeo; use core::iter::FromIterator; // 适用于`Rodeo`和`ThreadedRodeo` let rodeo: Rodeo = vec!["one string", "two string", "red string", "blue string"] .into_iter() .collect(); assert!(rodeo.contains("one string")); assert!(rodeo.contains("two string")); assert!(rodeo.contains("red string")); assert!(rodeo.contains("blue string"));

基准测试

基准测试使用Criterion.rs进行
操作系统:Windows 10
CPU:Ryzen 9 3900X,3800Mhz
内存:3200Mhz
Rustc:稳定版 1.44.1

Rodeo

STD RandomState

方法时间吞吐量
resolve1.9251 μs13.285 GiB/s
try_resolve1.9214 μs13.311 GiB/s
resolve_unchecked1.4356 μs17.816 GiB/s
get_or_intern(空)60.350 μs433.96 MiB/s
get_or_intern(已填充)57.415 μs456.15 MiB/s
try_get_or_intern(空)58.978 μs444.06 MiB/s
try_get_or_intern(已填充)57.421 μs456.10 MiB/s
get(空)37.288 μs702.37 MiB/s
get(已填充)55.095 μs475.36 MiB/s

AHash

方法时间吞吐量
try_resolve1.9282 μs13.264 GiB/s
resolve1.9404 μs13.181 GiB/s
resolve_unchecked1.4328 μs17.851 GiB/s
get_or_intern(空)38.029 μs688.68 MiB/s
get_or_intern(已填充)33.650 μs778.30 MiB/s
try_get_or_intern(空)39.392 μs664.84 MiB/s
try_get_or_intern(已填充)33.435 μs783.31 MiB/s
get(空)12.565 μs2.0356 GiB/s
get(已填充)26.545 μs986.61 MiB/s

FXHash

方法时间吞吐量
resolve1.9014 μs13.451 GiB/s
try_resolve1.9278 μs13.267 GiB/s
resolve_unchecked1.4449 μs17.701 GiB/s
get_or_intern(空)32.523 μs805.27 MiB/s
get_or_intern(已填充)30.281 μs864.88 MiB/s
try_get_or_intern(空)31.630 μs828.00 MiB/s
try_get_or_intern(已填充)31.002 μs844.78 MiB/s
get(空)12.699 μs2.0141 GiB/s
get(已填充)29.220 μs896.28 MiB/s

ThreadedRodeo

STD RandomState

方法时间(1线程)吞吐量(1线程)时间(24线程)吞吐量(24线程)
resolve54.336 μs482.00 MiB/s364.27 μs71.897 MiB/s
try_resolve54.582 μs479.82 MiB/s352.67 μs74.261 MiB/s
get_or_intern(空)266.03 μs98.447 MiB/s不适用不适用
get_or_intern(已填充)103.04 μs254.17 MiB/s441.42 μs59.331 MiB/s
try_get_or_intern(空)261.80 μs100.04 MiB/s不适用不适用
try_get_or_intern(已填充)102.61 μs255.25 MiB/s447.42 μs58.535 MiB/s
get(空)80.346 μs325.96 MiB/s不适用不适用
get(已填充)92.669 μs282.62 MiB/s439.24 μs59.626 MiB/s

AHash

方法时间(1线程)吞吐量(1线程)时间(24线程)吞吐量(24线程)
resolve22.261 μs1.1489 GiB/s265.46 μs98.658 MiB/s
try_resolve22.378 μs1.1429 GiB/s268.58 μs97.513 MiB/s
get_or_intern(空)157.86 μs165.91 MiB/s不适用不适用
get_or_intern(已填充)56.320 μs465.02 MiB/s357.13 μs73.335 MiB/s
try_get_or_intern(空)161.46 μs162.21 MiB/s不适用不适用
try_get_or_intern(已填充)55.874 μs468.73 MiB/s360.25 μs72.698 MiB/s
get(空)43.520 μs601.79 MiB/s不适用不适用
get(已填充)53.720 μs487.52 MiB/s360.66 μs72.616 MiB/s

FXHash

方法时间(1线程)吞吐量(1线程)时间(24线程)吞吐量(24线程)
try_resolve17.289 μs1.4794 GiB/s238.29 μs109.91 MiB/s
resolve19.833 μs1.2896 GiB/s237.05 μs110.48 MiB/s
get_or_intern(空)130.97 μs199.97 MiB/s不适用不适用
get_or_intern(已填充)42.630 μs614.35 MiB/s301.60 μs86.837 MiB/s
try_get_or_intern(空)129.30 μs202.55 MiB/s不适用不适用
try_get_or_intern(已填充)42.508 μs616.12 MiB/s337.29 μs77.648 MiB/s
get(空)28.001 μs935.30 MiB/s不适用不适用
get(已填充)37.700 μs694.68 MiB/s292.15 μs89.645 MiB/s

RodeoReader

STD RandomState

方法时间(1线程)吞吐量(1线程)时间(24线程)吞吐量(24线程)
resolve1.9398 μs13.185 GiB/s4.3153 μs5.9269 GiB/s
try_resolve1.9315 μs13.242 GiB/s4.1956 μs6.0959 GiB/s
resolve_unchecked1.4416 μs17.741 GiB/s3.1204 μs8.1964 GiB/s
get(空)38.886 μs673.50 MiB/s不适用不适用
get(已填充)56.271 μs465.42 MiB/s105.12 μs249.14 MiB/s

AHash

方法时间 (1 线程)吞吐量 (1 线程)时间 (24 线程)吞吐量 (24 线程)
resolve1.9404 μs13.181 GiB/s4.1881 μs6.1069 GiB/s
try_resolve1.8932 μs13.509 GiB/s4.2410 μs6.0306 GiB/s
resolve_unchecked1.4128 μs18.103 GiB/s3.1691 μs8.0703 GiB/s
get (空)11.952 μs2.1399 GiB/s不适用不适用
get (已填充)27.093 μs966.65 MiB/s56.269 μs465.44 MiB/s

FXHash

方法时间 (1 线程)吞吐量 (1 线程)时间 (24 线程)吞吐量 (24 线程)
resolve1.8987 μs13.471 GiB/s4.2117 μs6.0727 GiB/s
try_resolve1.9103 μs13.389 GiB/s4.2254 μs6.0529 GiB/s
resolve_unchecked1.4469 μs17.677 GiB/s3.0923 μs8.2709 GiB/s
get (空)12.994 μs1.9682 GiB/s不适用不适用
get (已填充)29.745 μs880.49 MiB/s52.387 μs499.93 MiB/s

RodeoResolver

方法时间 (1 线程)吞吐量 (1 线程)时间 (24 线程)吞吐量 (24 线程)
resolve1.9416 μs13.172 GiB/s3.9114 μs6.5387 GiB/s
try_resolve1.9264 μs13.277 GiB/s3.9289 μs6.5097 GiB/s
resolve_unchecked1.6638 μs15.372 GiB/s3.1741 μs8.0578 GiB/s

编辑推荐精选

博思AIPPT

博思AIPPT

AI一键生成PPT,就用博思AIPPT!

博思AIPPT,新一代的AI生成PPT平台,支持智能生成PPT、AI美化PPT、文本&链接生成PPT、导入Word/PDF/Markdown文档生成PPT等,内置海量精美PPT模板,涵盖商务、教育、科技等不同风格,同时针对每个页面提供多种版式,一键自适应切换,完美适配各种办公场景。

AI办公办公工具AI工具博思AIPPTAI生成PPT智能排版海量精品模板AI创作热门
潮际好麦

潮际好麦

AI赋能电商视觉革命,一站式智能商拍平台

潮际好麦深耕服装行业,是国内AI试衣效果最好的软件。使用先进AIGC能力为电商卖家批量提供优质的、低成本的商拍图。合作品牌有Shein、Lazada、安踏、百丽等65个国内外头部品牌,以及国内10万+淘宝、天猫、京东等主流平台的品牌商家,为卖家节省将近85%的出图成本,提升约3倍出图效率,让品牌能够快速上架。

iTerms

iTerms

企业专属的AI法律顾问

iTerms是法大大集团旗下法律子品牌,基于最先进的大语言模型(LLM)、专业的法律知识库和强大的智能体架构,帮助企业扫清合规障碍,筑牢风控防线,成为您企业专属的AI法律顾问。

SimilarWeb流量提升

SimilarWeb流量提升

稳定高效的流量提升解决方案,助力品牌曝光

稳定高效的流量提升解决方案,助力品牌曝光

Sora2视频免费生成

Sora2视频免费生成

最新版Sora2模型免费使用,一键生成无水印视频

最新版Sora2模型免费使用,一键生成无水印视频

Transly

Transly

实时语音翻译/同声传译工具

Transly是一个多场景的AI大语言模型驱动的同声传译、专业翻译助手,它拥有超精准的音频识别翻译能力,几乎零延迟的使用体验和支持多国语言可以让你带它走遍全球,无论你是留学生、商务人士、韩剧美剧爱好者,还是出国游玩、多国会议、跨国追星等等,都可以满足你所有需要同传的场景需求,线上线下通用,扫除语言障碍,让全世界的语言交流不再有国界。

讯飞绘文

讯飞绘文

选题、配图、成文,一站式创作,让内容运营更高效

讯飞绘文,一个AI集成平台,支持写作、选题、配图、排版和发布。高效生成适用于各类媒体的定制内容,加速品牌传播,提升内容营销效果。

热门AI辅助写作AI工具讯飞绘文内容运营AI创作个性化文章多平台分发AI助手
TRAE编程

TRAE编程

AI辅助编程,代码自动修复

Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。

AI工具TraeAI IDE协作生产力转型热门
商汤小浣熊

商汤小浣熊

最强AI数据分析助手

小浣熊家族Raccoon,您的AI智能助手,致力于通过先进的人工智能技术,为用户提供高效、便捷的智能服务。无论是日常咨询还是专业问题解答,小浣熊都能以快速、准确的响应满足您的需求,让您的生活更加智能便捷。

imini AI

imini AI

像人一样思考的AI智能体

imini 是一款超级AI智能体,能根据人类指令,自主思考、自主完成、并且交付结果的AI智能体。

下拉加载更多