{"id":695,"date":"2026-05-22T19:06:22","date_gmt":"2026-05-22T19:06:22","guid":{"rendered":"https:\/\/camilo.matajira.com\/?p=695"},"modified":"2026-05-22T19:14:36","modified_gmt":"2026-05-22T19:14:36","slug":"evaluating-adding-rust-pyo3-to-a-python-project","status":"publish","type":"post","link":"https:\/\/camilo.matajira.com\/?p=695","title":{"rendered":"Evaluating the Use of Rust (PyO3) in a Python Project"},"content":{"rendered":"\n<p>In this project, I evaluated the adoption of Rust (via PyO3) inside one of my personal projects, sysadmindb.<br>The idea was to replace the main regex parser from Python&#8217;s re, to Rust&#8217;s regex crate.<br>I benchmarked the current implementation and the proposed Rust version using pytest-benchmark.<br>The results showed that the &#8220;pure&#8221; Python implementation was faster than Rust&#8217;s (~1.4x).<br>After several trials and error, and further benchmarking, I concluded that for this specific regex (full of capturing groups)<br>Python&#8217;s re is faster that Rusts regex crate, hence, independent of the FFI boundary overhead, the python implementation would always be more performant.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">About SysadminDB<\/h3>\n\n\n\n<p>SysadminDB stores logs that can be queried using unix\/linux powertools such as grep, sed and awk.<br>The project is written in Python, and it contains a TCPServer to receive the logs, and an HTTPServer to query the logs.<br>The database is Sqlite.<\/p>\n\n\n\n<p>The main task of the TCPServer is to receive, parse and insert logs into the database.<br>The objective of this experiment was to improve the performance of this server by switching the regex parsing from Python to Rust.<br>This was chosen somewhat ad hoc, under the assumption that the migration would be relatively easy and provide a good ROI.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Code<\/h3>\n\n\n\n<p>This is the Python implementation:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#282A36\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#F8F8F2;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>from typing import List\nimport re\n\n\nclass Log:\n    def __init__(self, message: str):\n        pattern = \"\\&lt;(?P&lt;prival>&#91;0-9&#93;+)\\>(?P&lt;version>&#91;0-9&#93;)?\\s?\"\n        pattern += \"(?P&lt;date>(&#91;0-9&#93;{4}-&#91;0-9&#93;{2}-&#91;0-9&#93;{2}T&#91;0-9&#93;{2}:&#91;0-9&#93;{2}:&#91;0-9&#93;{2}\\.&#91;0-9&#93;+(Z|&#91;+-&#93;&#91;0-9&#93;{2}:&#91;0-9&#93;{2})|\\w{3}\\s&#91;0-9&#93;{2}\\s&#91;0-9&#93;{2}:&#91;0-9&#93;{2}:&#91;0-9&#93;{2}))\\s\"\n        pattern += \"(?P&lt;hostname>&#91;\\w.&#93;+)\\s\"\n        pattern += \"(?P&lt;appname>&#91;\\w.&#93;+)\\s?\"\n        pattern += \"\\[?(?P&lt;procid>&#91;0-9-&#93;+)?\\]?\\:?\\s?\"\n        pattern += \"(?P&lt;msgid>(-|\\w{2}&#91;0-9&#93;{2}))?\\s?\"\n        pattern += \"(?P&lt;structureddata>(\\&#91;.+\\&#93;|-))?\\s?(BOM)?\"\n        pattern += \"(?P&lt;msg>.+)?\"\n        match = re.match(pattern, message)\n        print(match)\n        try:\n            self.version = int(match.group(\"version\"))\n        except:\n            self.version = None\n        self.prival = int(match.group(\"prival\"))\n        self.date = match.group(\"date\")\n        self.hostname = match.group(\"hostname\")\n        self.appname = match.group(\"appname\")\n        self.procid = match.group(\"procid\")\n        self.msgid = match.group(\"msgid\")\n        self.structureddata = match.group(\"structureddata\")\n        try:\n            self.msg = match.group(\"msg\")\n        except:\n            self.msg = \"\"\n        self.original_msg = message<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dracula\" style=\"background-color: #282A36\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #FF79C6\">from<\/span><span style=\"color: #F8F8F2\"> typing <\/span><span style=\"color: #FF79C6\">import<\/span><span style=\"color: #F8F8F2\"> List<\/span><\/span>\n<span class=\"line\"><span style=\"color: #FF79C6\">import<\/span><span style=\"color: #F8F8F2\"> re<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #FF79C6\">class<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD\">Log<\/span><span style=\"color: #F8F8F2\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #FF79C6\">def<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #BD93F9\">__init__<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #BD93F9; font-style: italic\">self<\/span><span style=\"color: #F8F8F2\">, <\/span><span style=\"color: #FFB86C; font-style: italic\">message<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">str<\/span><span style=\"color: #F8F8F2\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        pattern <\/span><span style=\"color: #FF79C6\">=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F1FA8C\">\\&lt;(?P&lt;prival&gt;&#91;0-9&#93;+)\\&gt;(?P&lt;version&gt;&#91;0-9&#93;)?\\s?<\/span><span style=\"color: #E9F284\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        pattern <\/span><span style=\"color: #FF79C6\">+=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F1FA8C\">(?P&lt;date&gt;(&#91;0-9&#93;<\/span><span style=\"color: #BD93F9\">{4}<\/span><span style=\"color: #F1FA8C\">-&#91;0-9&#93;<\/span><span style=\"color: #BD93F9\">{2}<\/span><span style=\"color: #F1FA8C\">-&#91;0-9&#93;<\/span><span style=\"color: #BD93F9\">{2}<\/span><span style=\"color: #F1FA8C\">T&#91;0-9&#93;<\/span><span style=\"color: #BD93F9\">{2}<\/span><span style=\"color: #F1FA8C\">:&#91;0-9&#93;<\/span><span style=\"color: #BD93F9\">{2}<\/span><span style=\"color: #F1FA8C\">:&#91;0-9&#93;<\/span><span style=\"color: #BD93F9\">{2}<\/span><span style=\"color: #F1FA8C\">\\.&#91;0-9&#93;+(Z|&#91;+-&#93;&#91;0-9&#93;<\/span><span style=\"color: #BD93F9\">{2}<\/span><span style=\"color: #F1FA8C\">:&#91;0-9&#93;<\/span><span style=\"color: #BD93F9\">{2}<\/span><span style=\"color: #F1FA8C\">)|\\w<\/span><span style=\"color: #BD93F9\">{3}<\/span><span style=\"color: #F1FA8C\">\\s&#91;0-9&#93;<\/span><span style=\"color: #BD93F9\">{2}<\/span><span style=\"color: #F1FA8C\">\\s&#91;0-9&#93;<\/span><span style=\"color: #BD93F9\">{2}<\/span><span style=\"color: #F1FA8C\">:&#91;0-9&#93;<\/span><span style=\"color: #BD93F9\">{2}<\/span><span style=\"color: #F1FA8C\">:&#91;0-9&#93;<\/span><span style=\"color: #BD93F9\">{2}<\/span><span style=\"color: #F1FA8C\">))\\s<\/span><span style=\"color: #E9F284\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        pattern <\/span><span style=\"color: #FF79C6\">+=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F1FA8C\">(?P&lt;hostname&gt;&#91;\\w.&#93;+)\\s<\/span><span style=\"color: #E9F284\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        pattern <\/span><span style=\"color: #FF79C6\">+=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F1FA8C\">(?P&lt;appname&gt;&#91;\\w.&#93;+)\\s?<\/span><span style=\"color: #E9F284\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        pattern <\/span><span style=\"color: #FF79C6\">+=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F1FA8C\">\\[?(?P&lt;procid&gt;&#91;0-9-&#93;+)?\\]?\\:?\\s?<\/span><span style=\"color: #E9F284\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        pattern <\/span><span style=\"color: #FF79C6\">+=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F1FA8C\">(?P&lt;msgid&gt;(-|\\w<\/span><span style=\"color: #BD93F9\">{2}<\/span><span style=\"color: #F1FA8C\">&#91;0-9&#93;<\/span><span style=\"color: #BD93F9\">{2}<\/span><span style=\"color: #F1FA8C\">))?\\s?<\/span><span style=\"color: #E9F284\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        pattern <\/span><span style=\"color: #FF79C6\">+=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F1FA8C\">(?P&lt;structureddata&gt;(\\&#91;.+\\&#93;|-))?\\s?(BOM)?<\/span><span style=\"color: #E9F284\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        pattern <\/span><span style=\"color: #FF79C6\">+=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F1FA8C\">(?P&lt;msg&gt;.+)?<\/span><span style=\"color: #E9F284\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        match <\/span><span style=\"color: #FF79C6\">=<\/span><span style=\"color: #F8F8F2\"> re.match(pattern, message)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        <\/span><span style=\"color: #8BE9FD\">print<\/span><span style=\"color: #F8F8F2\">(match)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        <\/span><span style=\"color: #FF79C6\">try<\/span><span style=\"color: #F8F8F2\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">            <\/span><span style=\"color: #BD93F9; font-style: italic\">self<\/span><span style=\"color: #F8F8F2\">.version <\/span><span style=\"color: #FF79C6\">=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">int<\/span><span style=\"color: #F8F8F2\">(match.group(<\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F1FA8C\">version<\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F8F8F2\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        <\/span><span style=\"color: #FF79C6\">except<\/span><span style=\"color: #F8F8F2\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">            <\/span><span style=\"color: #BD93F9; font-style: italic\">self<\/span><span style=\"color: #F8F8F2\">.version <\/span><span style=\"color: #FF79C6\">=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #BD93F9\">None<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        <\/span><span style=\"color: #BD93F9; font-style: italic\">self<\/span><span style=\"color: #F8F8F2\">.prival <\/span><span style=\"color: #FF79C6\">=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">int<\/span><span style=\"color: #F8F8F2\">(match.group(<\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F1FA8C\">prival<\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F8F8F2\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        <\/span><span style=\"color: #BD93F9; font-style: italic\">self<\/span><span style=\"color: #F8F8F2\">.date <\/span><span style=\"color: #FF79C6\">=<\/span><span style=\"color: #F8F8F2\"> match.group(<\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F1FA8C\">date<\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F8F8F2\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        <\/span><span style=\"color: #BD93F9; font-style: italic\">self<\/span><span style=\"color: #F8F8F2\">.hostname <\/span><span style=\"color: #FF79C6\">=<\/span><span style=\"color: #F8F8F2\"> match.group(<\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F1FA8C\">hostname<\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F8F8F2\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        <\/span><span style=\"color: #BD93F9; font-style: italic\">self<\/span><span style=\"color: #F8F8F2\">.appname <\/span><span style=\"color: #FF79C6\">=<\/span><span style=\"color: #F8F8F2\"> match.group(<\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F1FA8C\">appname<\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F8F8F2\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        <\/span><span style=\"color: #BD93F9; font-style: italic\">self<\/span><span style=\"color: #F8F8F2\">.procid <\/span><span style=\"color: #FF79C6\">=<\/span><span style=\"color: #F8F8F2\"> match.group(<\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F1FA8C\">procid<\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F8F8F2\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        <\/span><span style=\"color: #BD93F9; font-style: italic\">self<\/span><span style=\"color: #F8F8F2\">.msgid <\/span><span style=\"color: #FF79C6\">=<\/span><span style=\"color: #F8F8F2\"> match.group(<\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F1FA8C\">msgid<\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F8F8F2\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        <\/span><span style=\"color: #BD93F9; font-style: italic\">self<\/span><span style=\"color: #F8F8F2\">.structureddata <\/span><span style=\"color: #FF79C6\">=<\/span><span style=\"color: #F8F8F2\"> match.group(<\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F1FA8C\">structureddata<\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F8F8F2\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        <\/span><span style=\"color: #FF79C6\">try<\/span><span style=\"color: #F8F8F2\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">            <\/span><span style=\"color: #BD93F9; font-style: italic\">self<\/span><span style=\"color: #F8F8F2\">.msg <\/span><span style=\"color: #FF79C6\">=<\/span><span style=\"color: #F8F8F2\"> match.group(<\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F1FA8C\">msg<\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F8F8F2\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        <\/span><span style=\"color: #FF79C6\">except<\/span><span style=\"color: #F8F8F2\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">            <\/span><span style=\"color: #BD93F9; font-style: italic\">self<\/span><span style=\"color: #F8F8F2\">.msg <\/span><span style=\"color: #FF79C6\">=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #E9F284\">&quot;&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        <\/span><span style=\"color: #BD93F9; font-style: italic\">self<\/span><span style=\"color: #F8F8F2\">.original_msg <\/span><span style=\"color: #FF79C6\">=<\/span><span style=\"color: #F8F8F2\"> message<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>This is my final equivalent code in Rust (after several iterations):<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#282A36\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#F8F8F2;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>use pyo3::prelude::*;\n\n\/\/\/ A Python module implemented in Rust.\n#&#91;pymodule&#93;\nmod sysadmindb_rs {\n    use pyo3::exceptions::PyValueError;\n    use pyo3::prelude::*;\n\n    use regex::Regex;\n    use std::sync::OnceLock;\n\n    fn log_pattern() -> &amp;'static Regex {\n        static RE: OnceLock&lt;Regex> = OnceLock::new();\n        RE.get_or_init(|| Regex::new(r\"&lt;(?&lt;prival>&#91;0-9&#93;+)>(?&lt;version>&#91;0-9&#93;)?\\s?(?&lt;date>(&#91;0-9&#93;{4}-&#91;0-9&#93;{2}-&#91;0-9&#93;{2}T&#91;0-9&#93;{2}:&#91;0-9&#93;{2}:&#91;0-9&#93;{2}\\.&#91;0-9&#93;+(Z|&#91;+-&#93;&#91;0-9&#93;{2}:&#91;0-9&#93;{2})|\\w{3}\\s&#91;0-9&#93;{2}\\s&#91;0-9&#93;{2}:&#91;0-9&#93;{2}:&#91;0-9&#93;{2}))\\s(?&lt;hostname>&#91;\\w.&#93;+)\\s(?&lt;appname>&#91;\\w.&#93;+)\\s?\\[?(?&lt;procid>&#91;0-9-&#93;+)?\\]?\\:?\\s?(?&lt;msgid>(-|\\w{2}&#91;0-9&#93;{2}))?\\s?(?&lt;structureddata>(\\&#91;.+\\&#93;|-))?\\s?(BOM)?(?&lt;msg>.+)?\").unwrap())\n    }\n\n    #&#91;pyclass&#93;\n    struct Log {\n        #&#91;pyo3(get)&#93;\n        version: Option&lt;u32>,\n        #&#91;pyo3(get)&#93;\n        prival: u32,\n        #&#91;pyo3(get)&#93;\n        date: String,\n        #&#91;pyo3(get)&#93;\n        hostname: String,\n        #&#91;pyo3(get)&#93;\n        appname: String,\n        #&#91;pyo3(get)&#93;\n        procid: String,\n        #&#91;pyo3(get)&#93;\n        msgid: String,\n        #&#91;pyo3(get)&#93;\n        structureddata: String,\n        #&#91;pyo3(get)&#93;\n        msg: String,\n    }\n    #&#91;pymethods&#93;\n    impl Log {\n        #&#91;new&#93;\n        fn new(line: &amp;str) -> PyResult&lt;Self> {\n            match parse_log(line) {\n                Ok(log) => Ok(log),\n                Err(_) => Err(PyValueError::new_err(\"Cannot parse\")),\n            }\n        }\n    }\n    fn parse_log(line: &amp;str) -> Result&lt;Log, String> {\n        let Some(caps) = log_pattern().captures(&amp;line) else {\n            return Err(\"sorry\".to_string());\n        };\n\n        Ok(Log {\n            prival: caps&#91;\"prival\"&#93;.parse().unwrap(),\n            version: caps.name(\"version\").map(|m| m.as_str().parse().unwrap()),\n            date: caps&#91;\"date\"&#93;.to_owned(),\n            hostname: caps&#91;\"hostname\"&#93;.to_owned(),\n            appname: caps&#91;\"appname\"&#93;.to_owned(),\n            procid: caps&#91;\"procid\"&#93;.to_owned(),\n            msgid: caps&#91;\"msgid\"&#93;.to_owned(),\n            structureddata: caps&#91;\"structureddata\"&#93;.to_owned(),\n            msg: caps\n                .name(\"msg\")\n                .map(|m| m.as_str().to_owned())\n                .unwrap_or_default(),\n        })\n    }<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dracula\" style=\"background-color: #282A36\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #FF79C6\">use<\/span><span style=\"color: #F8F8F2\"> pyo3<\/span><span style=\"color: #FF79C6\">::<\/span><span style=\"color: #F8F8F2\">prelude<\/span><span style=\"color: #FF79C6\">::*<\/span><span style=\"color: #F8F8F2\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6272A4\">\/\/\/ A Python module implemented in Rust.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">#&#91;pymodule&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #FF79C6\">mod<\/span><span style=\"color: #F8F8F2\"> sysadmindb_rs {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #FF79C6\">use<\/span><span style=\"color: #F8F8F2\"> pyo3<\/span><span style=\"color: #FF79C6\">::<\/span><span style=\"color: #F8F8F2\">exceptions<\/span><span style=\"color: #FF79C6\">::<\/span><span style=\"color: #8BE9FD; font-style: italic\">PyValueError<\/span><span style=\"color: #F8F8F2\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #FF79C6\">use<\/span><span style=\"color: #F8F8F2\"> pyo3<\/span><span style=\"color: #FF79C6\">::<\/span><span style=\"color: #F8F8F2\">prelude<\/span><span style=\"color: #FF79C6\">::*<\/span><span style=\"color: #F8F8F2\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #FF79C6\">use<\/span><span style=\"color: #F8F8F2\"> regex<\/span><span style=\"color: #FF79C6\">::<\/span><span style=\"color: #8BE9FD; font-style: italic\">Regex<\/span><span style=\"color: #F8F8F2\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #FF79C6\">use<\/span><span style=\"color: #F8F8F2\"> std<\/span><span style=\"color: #FF79C6\">::<\/span><span style=\"color: #F8F8F2\">sync<\/span><span style=\"color: #FF79C6\">::<\/span><span style=\"color: #8BE9FD; font-style: italic\">OnceLock<\/span><span style=\"color: #F8F8F2\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #FF79C6\">fn<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #50FA7B\">log_pattern<\/span><span style=\"color: #F8F8F2\">() <\/span><span style=\"color: #FF79C6\">-&gt;<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #FF79C6\">&amp;<\/span><span style=\"color: #F8F8F2\">&#39;<\/span><span style=\"color: #8BE9FD; font-style: italic\">static<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">Regex<\/span><span style=\"color: #F8F8F2\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        <\/span><span style=\"color: #FF79C6\">static<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #BD93F9\">RE<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">OnceLock<\/span><span style=\"color: #F8F8F2\">&lt;<\/span><span style=\"color: #8BE9FD; font-style: italic\">Regex<\/span><span style=\"color: #F8F8F2\">&gt; <\/span><span style=\"color: #FF79C6\">=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">OnceLock<\/span><span style=\"color: #FF79C6\">::<\/span><span style=\"color: #50FA7B\">new<\/span><span style=\"color: #F8F8F2\">();<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        <\/span><span style=\"color: #BD93F9\">RE<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">get_or_init<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #FF79C6\">||<\/span><span style=\"color: #F8F8F2\"> Regex<\/span><span style=\"color: #FF79C6\">::<\/span><span style=\"color: #50FA7B\">new<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #F1FA8C\">r&quot;&lt;(?&lt;prival&gt;&#91;0-9&#93;+)&gt;(?&lt;version&gt;&#91;0-9&#93;)?\\s?(?&lt;date&gt;(&#91;0-9&#93;{4}-&#91;0-9&#93;{2}-&#91;0-9&#93;{2}T&#91;0-9&#93;{2}:&#91;0-9&#93;{2}:&#91;0-9&#93;{2}\\.&#91;0-9&#93;+(Z|&#91;+-&#93;&#91;0-9&#93;{2}:&#91;0-9&#93;{2})|\\w{3}\\s&#91;0-9&#93;{2}\\s&#91;0-9&#93;{2}:&#91;0-9&#93;{2}:&#91;0-9&#93;{2}))\\s(?&lt;hostname&gt;&#91;\\w.&#93;+)\\s(?&lt;appname&gt;&#91;\\w.&#93;+)\\s?\\[?(?&lt;procid&gt;&#91;0-9-&#93;+)?\\]?\\:?\\s?(?&lt;msgid&gt;(-|\\w{2}&#91;0-9&#93;{2}))?\\s?(?&lt;structureddata&gt;(\\&#91;.+\\&#93;|-))?\\s?(BOM)?(?&lt;msg&gt;.+)?&quot;<\/span><span style=\"color: #F8F8F2\">)<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">unwrap<\/span><span style=\"color: #F8F8F2\">())<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    }<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    #&#91;pyclass&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #FF79C6\">struct<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">Log<\/span><span style=\"color: #F8F8F2\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        #&#91;pyo3(get)&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        version<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">Option<\/span><span style=\"color: #F8F8F2\">&lt;<\/span><span style=\"color: #8BE9FD; font-style: italic\">u32<\/span><span style=\"color: #F8F8F2\">&gt;,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        #&#91;pyo3(get)&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        prival<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">u32<\/span><span style=\"color: #F8F8F2\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        #&#91;pyo3(get)&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        date<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">String<\/span><span style=\"color: #F8F8F2\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        #&#91;pyo3(get)&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        hostname<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">String<\/span><span style=\"color: #F8F8F2\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        #&#91;pyo3(get)&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        appname<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">String<\/span><span style=\"color: #F8F8F2\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        #&#91;pyo3(get)&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        procid<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">String<\/span><span style=\"color: #F8F8F2\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        #&#91;pyo3(get)&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        msgid<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">String<\/span><span style=\"color: #F8F8F2\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        #&#91;pyo3(get)&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        structureddata<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">String<\/span><span style=\"color: #F8F8F2\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        #&#91;pyo3(get)&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        msg<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">String<\/span><span style=\"color: #F8F8F2\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    #&#91;pymethods&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #FF79C6\">impl<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">Log<\/span><span style=\"color: #F8F8F2\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        #&#91;new&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        <\/span><span style=\"color: #FF79C6\">fn<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #50FA7B\">new<\/span><span style=\"color: #F8F8F2\">(line<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #FF79C6\">&amp;<\/span><span style=\"color: #8BE9FD; font-style: italic\">str<\/span><span style=\"color: #F8F8F2\">) <\/span><span style=\"color: #FF79C6\">-&gt;<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">PyResult<\/span><span style=\"color: #F8F8F2\">&lt;<\/span><span style=\"color: #BD93F9; font-style: italic\">Self<\/span><span style=\"color: #F8F8F2\">&gt; {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">            <\/span><span style=\"color: #FF79C6\">match<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #50FA7B\">parse_log<\/span><span style=\"color: #F8F8F2\">(line) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">                <\/span><span style=\"color: #8BE9FD; font-style: italic\">Ok<\/span><span style=\"color: #F8F8F2\">(log) <\/span><span style=\"color: #FF79C6\">=&gt;<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">Ok<\/span><span style=\"color: #F8F8F2\">(log),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">                <\/span><span style=\"color: #8BE9FD; font-style: italic\">Err<\/span><span style=\"color: #F8F8F2\">(_) <\/span><span style=\"color: #FF79C6\">=&gt;<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">Err<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #8BE9FD; font-style: italic\">PyValueError<\/span><span style=\"color: #FF79C6\">::<\/span><span style=\"color: #50FA7B\">new_err<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #F1FA8C\">&quot;Cannot parse&quot;<\/span><span style=\"color: #F8F8F2\">)),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">            }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #FF79C6\">fn<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #50FA7B\">parse_log<\/span><span style=\"color: #F8F8F2\">(line<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #FF79C6\">&amp;<\/span><span style=\"color: #8BE9FD; font-style: italic\">str<\/span><span style=\"color: #F8F8F2\">) <\/span><span style=\"color: #FF79C6\">-&gt;<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">Result<\/span><span style=\"color: #F8F8F2\">&lt;<\/span><span style=\"color: #8BE9FD; font-style: italic\">Log<\/span><span style=\"color: #F8F8F2\">, <\/span><span style=\"color: #8BE9FD; font-style: italic\">String<\/span><span style=\"color: #F8F8F2\">&gt; {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        <\/span><span style=\"color: #FF79C6\">let<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">Some<\/span><span style=\"color: #F8F8F2\">(caps) <\/span><span style=\"color: #FF79C6\">=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #50FA7B\">log_pattern<\/span><span style=\"color: #F8F8F2\">()<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">captures<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #FF79C6\">&amp;<\/span><span style=\"color: #F8F8F2\">line) <\/span><span style=\"color: #FF79C6\">else<\/span><span style=\"color: #F8F8F2\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">            <\/span><span style=\"color: #FF79C6\">return<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">Err<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #F1FA8C\">&quot;sorry&quot;<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">to_string<\/span><span style=\"color: #F8F8F2\">());<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        };<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        <\/span><span style=\"color: #8BE9FD; font-style: italic\">Ok<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #8BE9FD; font-style: italic\">Log<\/span><span style=\"color: #F8F8F2\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">            prival<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> caps&#91;<\/span><span style=\"color: #F1FA8C\">&quot;prival&quot;<\/span><span style=\"color: #F8F8F2\">&#93;<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">parse<\/span><span style=\"color: #F8F8F2\">()<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">unwrap<\/span><span style=\"color: #F8F8F2\">(),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">            version<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> caps<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">name<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #F1FA8C\">&quot;version&quot;<\/span><span style=\"color: #F8F8F2\">)<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">map<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #FF79C6\">|<\/span><span style=\"color: #F8F8F2\">m<\/span><span style=\"color: #FF79C6\">|<\/span><span style=\"color: #F8F8F2\"> m<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">as_str<\/span><span style=\"color: #F8F8F2\">()<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">parse<\/span><span style=\"color: #F8F8F2\">()<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">unwrap<\/span><span style=\"color: #F8F8F2\">()),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">            date<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> caps&#91;<\/span><span style=\"color: #F1FA8C\">&quot;date&quot;<\/span><span style=\"color: #F8F8F2\">&#93;<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">to_owned<\/span><span style=\"color: #F8F8F2\">(),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">            hostname<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> caps&#91;<\/span><span style=\"color: #F1FA8C\">&quot;hostname&quot;<\/span><span style=\"color: #F8F8F2\">&#93;<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">to_owned<\/span><span style=\"color: #F8F8F2\">(),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">            appname<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> caps&#91;<\/span><span style=\"color: #F1FA8C\">&quot;appname&quot;<\/span><span style=\"color: #F8F8F2\">&#93;<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">to_owned<\/span><span style=\"color: #F8F8F2\">(),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">            procid<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> caps&#91;<\/span><span style=\"color: #F1FA8C\">&quot;procid&quot;<\/span><span style=\"color: #F8F8F2\">&#93;<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">to_owned<\/span><span style=\"color: #F8F8F2\">(),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">            msgid<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> caps&#91;<\/span><span style=\"color: #F1FA8C\">&quot;msgid&quot;<\/span><span style=\"color: #F8F8F2\">&#93;<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">to_owned<\/span><span style=\"color: #F8F8F2\">(),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">            structureddata<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> caps&#91;<\/span><span style=\"color: #F1FA8C\">&quot;structureddata&quot;<\/span><span style=\"color: #F8F8F2\">&#93;<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">to_owned<\/span><span style=\"color: #F8F8F2\">(),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">            msg<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> caps<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">                <\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">name<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #F1FA8C\">&quot;msg&quot;<\/span><span style=\"color: #F8F8F2\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">                <\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">map<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #FF79C6\">|<\/span><span style=\"color: #F8F8F2\">m<\/span><span style=\"color: #FF79C6\">|<\/span><span style=\"color: #F8F8F2\"> m<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">as_str<\/span><span style=\"color: #F8F8F2\">()<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">to_owned<\/span><span style=\"color: #F8F8F2\">())<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">                <\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">unwrap_or_default<\/span><span style=\"color: #F8F8F2\">(),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        })<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    }<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">The Benchmark<\/h3>\n\n\n\n<p>To benchmark, pytest-benchmark was used.<br>The test was to parse a single log line.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#282A36\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#F8F8F2;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>class TestMessage:\n    def test_init_benchmark(self, benchmark):\n        original_message = \"&lt;34>1 2003-10-11T22:14:15.003Z mymachine.example.com su - ID47 - BOM'su root' failed for lonvick on \/dev\/pts\/8\"\n        benchmark(lambda: log.Log(original_message))\n\n    def test_init_benchmark_rs(self, benchmark):\n        import sysadmindb_rs\n\n        original_message = \"&lt;34>1 2003-10-11T22:14:15.003Z mymachine.example.com su - ID47 - BOM'su root' failed for lonvick on \/dev\/pts\/8\"\n        benchmark(lambda: sysadmindb_rs.Log(original_message))<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dracula\" style=\"background-color: #282A36\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #FF79C6\">class<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD\">TestMessage<\/span><span style=\"color: #F8F8F2\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #FF79C6\">def<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #50FA7B\">test_init_benchmark<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #BD93F9; font-style: italic\">self<\/span><span style=\"color: #F8F8F2\">, <\/span><span style=\"color: #FFB86C; font-style: italic\">benchmark<\/span><span style=\"color: #F8F8F2\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        original_message <\/span><span style=\"color: #FF79C6\">=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F1FA8C\">&lt;34&gt;1 2003-10-11T22:14:15.003Z mymachine.example.com su - ID47 - BOM&#39;su root&#39; failed for lonvick on \/dev\/pts\/8<\/span><span style=\"color: #E9F284\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        benchmark(<\/span><span style=\"color: #FF79C6\">lambda<\/span><span style=\"color: #F8F8F2\">: log.Log(original_message))<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #FF79C6\">def<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #50FA7B\">test_init_benchmark_rs<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #BD93F9; font-style: italic\">self<\/span><span style=\"color: #F8F8F2\">, <\/span><span style=\"color: #FFB86C; font-style: italic\">benchmark<\/span><span style=\"color: #F8F8F2\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        <\/span><span style=\"color: #FF79C6\">import<\/span><span style=\"color: #F8F8F2\"> sysadmindb_rs<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        original_message <\/span><span style=\"color: #FF79C6\">=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #E9F284\">&quot;<\/span><span style=\"color: #F1FA8C\">&lt;34&gt;1 2003-10-11T22:14:15.003Z mymachine.example.com su - ID47 - BOM&#39;su root&#39; failed for lonvick on \/dev\/pts\/8<\/span><span style=\"color: #E9F284\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">        benchmark(<\/span><span style=\"color: #FF79C6\">lambda<\/span><span style=\"color: #F8F8F2\">: sysadmindb_rs.Log(original_message))<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Initial Results<\/h3>\n\n\n\n<p>Several optimization attempts were made, during which I corrected several issues like:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Not using the &#8220;release&#8221; version of the compile library in Rust.<\/li>\n\n\n\n<li>Removing print and println! statements.<\/li>\n\n\n\n<li>Creating a static variable in Rust to mimic Python&#8217;s re.compile()<\/li>\n<\/ul>\n\n\n\n<p>After correcting all that, the results are the following: Rust was ~1.33x slower than Python.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#282A36\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#F8F8F2;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>---------------------------------------------------------------------------------------------------------------------------------\nName (time in us)                Min                   Max                  Mean              StdDev                Median       \n---------------------------------------------------------------------------------------------------------------------------------\ntest_init_benchmark           1.4840 (1.0)        118.6160 (4.60)         1.8705 (1.0)        0.7506 (1.53)         1.7690 (1.0) \ntest_init_benchmark_rs        2.0340 (1.37)        25.7780 (1.0)          2.4947 (1.33)       0.4906 (1.0)          2.3780 (1.34)\n---------------------------------------------------------------------------------------------------------------------------------<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dracula\" style=\"background-color: #282A36\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #FF79C6\">---------------------------------------------------------------------------------------------------------------------------------<\/span><\/span>\n<span class=\"line\"><span style=\"color: #8BE9FD; font-style: italic\">Name<\/span><span style=\"color: #F8F8F2\"> (time <\/span><span style=\"color: #FF79C6\">in<\/span><span style=\"color: #F8F8F2\"> us)                <\/span><span style=\"color: #8BE9FD; font-style: italic\">Min<\/span><span style=\"color: #F8F8F2\">                   <\/span><span style=\"color: #8BE9FD; font-style: italic\">Max<\/span><span style=\"color: #F8F8F2\">                  <\/span><span style=\"color: #8BE9FD; font-style: italic\">Mean<\/span><span style=\"color: #F8F8F2\">              <\/span><span style=\"color: #8BE9FD; font-style: italic\">StdDev<\/span><span style=\"color: #F8F8F2\">                <\/span><span style=\"color: #8BE9FD; font-style: italic\">Median<\/span><span style=\"color: #F8F8F2\">       <\/span><\/span>\n<span class=\"line\"><span style=\"color: #FF79C6\">---------------------------------------------------------------------------------------------------------------------------------<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">test_init_benchmark           <\/span><span style=\"color: #BD93F9\">1.4840<\/span><span style=\"color: #F8F8F2\"> (<\/span><span style=\"color: #BD93F9\">1.0<\/span><span style=\"color: #F8F8F2\">)        <\/span><span style=\"color: #BD93F9\">118.6160<\/span><span style=\"color: #F8F8F2\"> (<\/span><span style=\"color: #BD93F9\">4.60<\/span><span style=\"color: #F8F8F2\">)         <\/span><span style=\"color: #BD93F9\">1.8705<\/span><span style=\"color: #F8F8F2\"> (<\/span><span style=\"color: #BD93F9\">1.0<\/span><span style=\"color: #F8F8F2\">)        <\/span><span style=\"color: #BD93F9\">0.7506<\/span><span style=\"color: #F8F8F2\"> (<\/span><span style=\"color: #BD93F9\">1.53<\/span><span style=\"color: #F8F8F2\">)         <\/span><span style=\"color: #BD93F9\">1.7690<\/span><span style=\"color: #F8F8F2\"> (<\/span><span style=\"color: #BD93F9\">1.0<\/span><span style=\"color: #F8F8F2\">) <\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">test_init_benchmark_rs        <\/span><span style=\"color: #BD93F9\">2.0340<\/span><span style=\"color: #F8F8F2\"> (<\/span><span style=\"color: #BD93F9\">1.37<\/span><span style=\"color: #F8F8F2\">)        <\/span><span style=\"color: #BD93F9\">25.7780<\/span><span style=\"color: #F8F8F2\"> (<\/span><span style=\"color: #BD93F9\">1.0<\/span><span style=\"color: #F8F8F2\">)          <\/span><span style=\"color: #BD93F9\">2.4947<\/span><span style=\"color: #F8F8F2\"> (<\/span><span style=\"color: #BD93F9\">1.33<\/span><span style=\"color: #F8F8F2\">)       <\/span><span style=\"color: #BD93F9\">0.4906<\/span><span style=\"color: #F8F8F2\"> (<\/span><span style=\"color: #BD93F9\">1.0<\/span><span style=\"color: #F8F8F2\">)          <\/span><span style=\"color: #BD93F9\">2.3780<\/span><span style=\"color: #F8F8F2\"> (<\/span><span style=\"color: #BD93F9\">1.34<\/span><span style=\"color: #F8F8F2\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #FF79C6\">---------------------------------------------------------------------------------------------------------------------------------<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>These results were unexpected, so I started experimenting with additional optimizations like:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Changing String&#8217;s .to_owned() to &amp;str, and to string slices.<\/li>\n\n\n\n<li>Modified the regex to use less captures.<\/li>\n\n\n\n<li>Change the return types of the rust function to pay less PyO3 overhead.<br>Yet, neither of those optimizations provided a meaningful impact.<\/li>\n<\/ul>\n\n\n\n<p>Later, following some advice I received from Reddit, and started benchmarking the system more precisely to better understand were the overhead originated.<br>I benchmarked:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Benchmark the time to instantiate the Log Class and the Log Struct with fake data.<\/li>\n\n\n\n<li>Benchmark the time to parse and create a Log Struct, but not returning it to Python (no second FFI overhead)<\/li>\n\n\n\n<li>Benchmark the matching (and capturing) in Python and Rust independently.<\/li>\n<\/ul>\n\n\n\n<p>To benchmark the Rust code in isolation I used Criterion, for the other benchmarks I continued using pytest-benchmark.<br>The Criterion code is the following:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#282A36\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#F8F8F2;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>use criterion::{Criterion, black_box, criterion_group, criterion_main};\nuse sysadmindb_rs::sysadmindb_rs::match_only;\n\nfn benchmark_parse(c: &amp;mut Criterion) {\n    let line = \"&lt;34>1 2003-10-11T22:14:15.003Z mymachine.example.com su - ID47 - BOM'su root' failed for lonvick on \/dev\/pts\/8\";\n\n    c.bench_function(\"match_only\", |b| b.iter(|| match_only(black_box(line))));\n}\n\ncriterion_group!(benches, benchmark_parse);\ncriterion_main!(benches);<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dracula\" style=\"background-color: #282A36\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #FF79C6\">use<\/span><span style=\"color: #F8F8F2\"> criterion<\/span><span style=\"color: #FF79C6\">::<\/span><span style=\"color: #F8F8F2\">{<\/span><span style=\"color: #8BE9FD; font-style: italic\">Criterion<\/span><span style=\"color: #F8F8F2\">, black_box, criterion_group, criterion_main};<\/span><\/span>\n<span class=\"line\"><span style=\"color: #FF79C6\">use<\/span><span style=\"color: #F8F8F2\"> sysadmindb_rs<\/span><span style=\"color: #FF79C6\">::<\/span><span style=\"color: #F8F8F2\">sysadmindb_rs<\/span><span style=\"color: #FF79C6\">::<\/span><span style=\"color: #F8F8F2\">match_only;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #FF79C6\">fn<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #50FA7B\">benchmark_parse<\/span><span style=\"color: #F8F8F2\">(c<\/span><span style=\"color: #FF79C6\">:<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #FF79C6\">&amp;mut<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">Criterion<\/span><span style=\"color: #F8F8F2\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #FF79C6\">let<\/span><span style=\"color: #F8F8F2\"> line <\/span><span style=\"color: #FF79C6\">=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #F1FA8C\">&quot;&lt;34&gt;1 2003-10-11T22:14:15.003Z mymachine.example.com su - ID47 - BOM&#39;su root&#39; failed for lonvick on \/dev\/pts\/8&quot;<\/span><span style=\"color: #F8F8F2\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    c<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">bench_function<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #F1FA8C\">&quot;match_only&quot;<\/span><span style=\"color: #F8F8F2\">, <\/span><span style=\"color: #FF79C6\">|<\/span><span style=\"color: #F8F8F2\">b<\/span><span style=\"color: #FF79C6\">|<\/span><span style=\"color: #F8F8F2\"> b<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">iter<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #FF79C6\">||<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #50FA7B\">match_only<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #50FA7B\">black_box<\/span><span style=\"color: #F8F8F2\">(line))));<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #50FA7B\">criterion_group!<\/span><span style=\"color: #F8F8F2\">(benches, benchmark_parse);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #50FA7B\">criterion_main!<\/span><span style=\"color: #F8F8F2\">(benches);<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The results are the following:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#282A36\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#F8F8F2;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n  \u2502         Benchmark         \u2502   Mean    \u2502            What it measures             \u2502\n  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n  \u2502 fake_log_rs               \u2502 ~145 ns   \u2502 #&#91;pyclass&#93; struct creation only         \u2502\n  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n  \u2502 match_only_rs (criterion) \u2502 ~1,855 ns \u2502 Pure Rust regex captures(), no FFI      \u2502\n  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n  \u2502 match_only_python         \u2502 ~1,371 ns \u2502 Python re.match()                       \u2502\n  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n  \u2502 match_only_rs (pytest)    \u2502 ~2,129 ns \u2502 Rust regex + Single FFI crossing        \u2502\n  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n  \u2502 test_init_benchmark       \u2502 ~2,431 ns \u2502 Full Python parse                       \u2502\n  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n  \u2502 parse_no_return_rs        \u2502 ~2,563 ns \u2502 Rust regex + struct, no FFI marshalling \u2502\n  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n  \u2502 test_init_benchmark_rs    \u2502 ~4,233 ns \u2502 Full Rust parse + FFI                   \u2502\n  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dracula\" style=\"background-color: #282A36\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F8F8F2\">  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">  \u2502         <\/span><span style=\"color: #8BE9FD; font-style: italic\">Benchmark<\/span><span style=\"color: #F8F8F2\">         \u2502   <\/span><span style=\"color: #8BE9FD; font-style: italic\">Mean<\/span><span style=\"color: #F8F8F2\">    \u2502            <\/span><span style=\"color: #8BE9FD; font-style: italic\">What<\/span><span style=\"color: #F8F8F2\"> it measures             \u2502<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">  \u2502 fake_log_rs               \u2502 ~<\/span><span style=\"color: #BD93F9\">145<\/span><span style=\"color: #F8F8F2\"> ns   \u2502 #&#91;pyclass&#93; <\/span><span style=\"color: #FF79C6\">struct<\/span><span style=\"color: #F8F8F2\"> creation only         \u2502<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">  \u2502 match_only_rs (criterion) \u2502 ~<\/span><span style=\"color: #BD93F9\">1<\/span><span style=\"color: #F8F8F2\">,<\/span><span style=\"color: #BD93F9\">855<\/span><span style=\"color: #F8F8F2\"> ns \u2502 <\/span><span style=\"color: #8BE9FD; font-style: italic\">Pure<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">Rust<\/span><span style=\"color: #F8F8F2\"> regex <\/span><span style=\"color: #50FA7B\">captures<\/span><span style=\"color: #F8F8F2\">(), no <\/span><span style=\"color: #BD93F9\">FFI<\/span><span style=\"color: #F8F8F2\">      \u2502<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">  \u2502 match_only_python         \u2502 ~<\/span><span style=\"color: #BD93F9\">1<\/span><span style=\"color: #F8F8F2\">,<\/span><span style=\"color: #BD93F9\">371<\/span><span style=\"color: #F8F8F2\"> ns \u2502 <\/span><span style=\"color: #8BE9FD; font-style: italic\">Python<\/span><span style=\"color: #F8F8F2\"> re<\/span><span style=\"color: #FF79C6\">.<\/span><span style=\"color: #50FA7B\">match<\/span><span style=\"color: #F8F8F2\">()                       \u2502<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">  \u2502 match_only_rs (pytest)    \u2502 ~<\/span><span style=\"color: #BD93F9\">2<\/span><span style=\"color: #F8F8F2\">,<\/span><span style=\"color: #BD93F9\">129<\/span><span style=\"color: #F8F8F2\"> ns \u2502 <\/span><span style=\"color: #8BE9FD; font-style: italic\">Rust<\/span><span style=\"color: #F8F8F2\"> regex <\/span><span style=\"color: #FF79C6\">+<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">Single<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #BD93F9\">FFI<\/span><span style=\"color: #F8F8F2\"> crossing        \u2502<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">  \u2502 test_init_benchmark       \u2502 ~<\/span><span style=\"color: #BD93F9\">2<\/span><span style=\"color: #F8F8F2\">,<\/span><span style=\"color: #BD93F9\">431<\/span><span style=\"color: #F8F8F2\"> ns \u2502 <\/span><span style=\"color: #8BE9FD; font-style: italic\">Full<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">Python<\/span><span style=\"color: #F8F8F2\"> parse                       \u2502<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">  \u2502 parse_no_return_rs        \u2502 ~<\/span><span style=\"color: #BD93F9\">2<\/span><span style=\"color: #F8F8F2\">,<\/span><span style=\"color: #BD93F9\">563<\/span><span style=\"color: #F8F8F2\"> ns \u2502 <\/span><span style=\"color: #8BE9FD; font-style: italic\">Rust<\/span><span style=\"color: #F8F8F2\"> regex <\/span><span style=\"color: #FF79C6\">+<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #FF79C6\">struct<\/span><span style=\"color: #F8F8F2\">, no <\/span><span style=\"color: #BD93F9\">FFI<\/span><span style=\"color: #F8F8F2\"> marshalling \u2502<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">  \u2502 test_init_benchmark_rs    \u2502 ~<\/span><span style=\"color: #BD93F9\">4<\/span><span style=\"color: #F8F8F2\">,<\/span><span style=\"color: #BD93F9\">233<\/span><span style=\"color: #F8F8F2\"> ns \u2502 <\/span><span style=\"color: #8BE9FD; font-style: italic\">Full<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #8BE9FD; font-style: italic\">Rust<\/span><span style=\"color: #F8F8F2\"> parse <\/span><span style=\"color: #FF79C6\">+<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #BD93F9\">FFI<\/span><span style=\"color: #F8F8F2\">                   \u2502<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Analysis<\/h3>\n\n\n\n<p>For the following analysis, let&#8217;s assume that results from criterion and pytest-benchmark are accurate.<br>Let&#8217;s also assume that the differences between estimates are statistically significant.<br>This disclaimer is important because the benchmark durations are extremely small and therefore heavily influenced by background activity on the machine running the tests.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Rust (match_only_rs (criterion)) took 1.4x longer to perform the regex and capture groups, than Python&#8217;s re (match_only_python).<\/li>\n\n\n\n<li>This alone suggests that rewriting this regex in Rust is not beneficial. If the Rust regex implementation itself is slower, then adding FFI overhead will only worsen the results.<\/li>\n\n\n\n<li>The cost of single FFI crossing can be estimated in 274ns (match_only_rs (pytest) &#8211; match_only_rs (criterion)).<\/li>\n\n\n\n<li>The cost of returning the struct and converting it into Python objects is ~1670 ns (test_init_benchmar_rs- parse_no_return_rs).<br>This cost could be potentially reduced by optimizing the data structures and manually converting the struct fields. Yet, as shown before, this is not worthwhile.<\/li>\n\n\n\n<li>The cost of creating the Log struct in Rust is 145ns.<\/li>\n\n\n\n<li>The total performance difference between the full Python implementation and the Rust implementation is 1802ns (test_init_benchmar_rs &#8211; test_init_benchmark).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Conclusion<\/h3>\n\n\n\n<p>This project demonstrated that &#8220;not everything that glitters is Rust&#8221;.<br>Under certain circumstances, Rust&#8217;s regex crate can be slows than Python&#8217;s re module.<br>In scenarios like this, regardless of the effort spent optimizing data structures or minimizing FFI overhead, if the core rewritten logic is not faster, the rest of the optimization work becomes irrelevant.<br>My recommendation for future Python\/Rust integration projects is to benchmark both implementations in isolation first.<br>If the Rust implementation is fast enough to amortize the FFI overhead with sufficient margin left over, then it makes sense to continue building the glue code between Python and Rust.<br>Otherwise, it is better to search for a more suitable optimization target.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this project, I evaluated the adoption of Rust (via PyO3) inside one of my personal projects, sysadmindb.The idea was to replace the main regex parser from Python&#8217;s re, to Rust&#8217;s regex crate.I benchmarked the current implementation and the proposed Rust version using pytest-benchmark.The results showed that the &#8220;pure&#8221; Python implementation was faster than Rust&#8217;s&#8230;<\/p>\n","protected":false},"author":1,"featured_media":699,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-695","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-projects"],"_links":{"self":[{"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=\/wp\/v2\/posts\/695","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=695"}],"version-history":[{"count":3,"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=\/wp\/v2\/posts\/695\/revisions"}],"predecessor-version":[{"id":701,"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=\/wp\/v2\/posts\/695\/revisions\/701"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=\/wp\/v2\/media\/699"}],"wp:attachment":[{"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=695"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=695"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=695"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}