Added python-rust article.
Signed-off-by: Pavel Kirilin <win10@list.ru>
This commit is contained in:
@ -45,6 +45,6 @@ deploy:
|
|||||||
--wait
|
--wait
|
||||||
--create-namespace
|
--create-namespace
|
||||||
--atomic
|
--atomic
|
||||||
--timeout 2m
|
--timeout 3m
|
||||||
--namespace "$NAMESPACE"
|
--namespace "$NAMESPACE"
|
||||||
-f "$HELM_CONFIG"
|
-f "$HELM_CONFIG"
|
||||||
|
@ -37,7 +37,7 @@ deploy
|
|||||||
|
|
||||||
Запуск таких конфигураций выглядит следующим образом:
|
Запуск таких конфигураций выглядит следующим образом:
|
||||||
```bash
|
```bash
|
||||||
docker-compose \
|
$ docker-compose \
|
||||||
-f "deploy/docker-compose.yml" \
|
-f "deploy/docker-compose.yml" \
|
||||||
-f "deploy/docker-compose.db.yml" \
|
-f "deploy/docker-compose.db.yml" \
|
||||||
-f "deploy/docker-compose.dev.yml" \
|
-f "deploy/docker-compose.dev.yml" \
|
||||||
@ -101,10 +101,10 @@ services:
|
|||||||
Теперь запустим всё это чудо.
|
Теперь запустим всё это чудо.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
d-test docker-compose \
|
$ docker-compose \
|
||||||
-f "./deploy/docker-compose.yml" \
|
-f "./deploy/docker-compose.yml" \
|
||||||
--project-directory "." \
|
--project-directory "." \
|
||||||
run --rm script
|
run --rm script
|
||||||
```
|
```
|
||||||
|
|
||||||
Вот что будет выведено на экран.
|
Вот что будет выведено на экран.
|
||||||
@ -129,11 +129,11 @@ services:
|
|||||||
Теперь добавим ещё один файл в нашу команду запуска.
|
Теперь добавим ещё один файл в нашу команду запуска.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
d-test docker-compose \
|
$ docker-compose \
|
||||||
-f "deploy/docker-compose.yml" \
|
-f "deploy/docker-compose.yml" \
|
||||||
-f "deploy/docker-compose.dev.yml" \
|
-f "deploy/docker-compose.dev.yml" \
|
||||||
--project-directory "." \
|
--project-directory "." \
|
||||||
run --rm script
|
run --rm script
|
||||||
```
|
```
|
||||||
|
|
||||||
Вот что будет выведено на экран:
|
Вот что будет выведено на экран:
|
||||||
|
@ -133,3 +133,7 @@ Hi!
|
|||||||
|
|
||||||
А что если я вот не хочу чтобы команда создавала и проверяла файлы?
|
А что если я вот не хочу чтобы команда создавала и проверяла файлы?
|
||||||
Для этого пишут `.PHONY: ${target}`. Например у нас так объявлен таргет `run` и, даже если файл с названием `run` будет присутствовать в директории цель не будет выполняться.
|
Для этого пишут `.PHONY: ${target}`. Например у нас так объявлен таргет `run` и, даже если файл с названием `run` будет присутствовать в директории цель не будет выполняться.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
До новых встреч.
|
||||||
|
@ -232,15 +232,14 @@ $ poetry publish -u "user" -p "password"
|
|||||||
|
|
||||||
```console
|
```console
|
||||||
$ poetry add \
|
$ poetry add \
|
||||||
$ flake8 \
|
flake8 \
|
||||||
$ black \
|
black \
|
||||||
$ isort \
|
isort \
|
||||||
$ mypy \
|
mypy \
|
||||||
$ pre-commit \
|
pre-commit \
|
||||||
$ yesqa \
|
yesqa \
|
||||||
$ autoflake \
|
autoflake \
|
||||||
$ wemake-python-styleguide --dev
|
wemake-python-styleguide --dev
|
||||||
---> 100%
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Теперь добавим конфигурационных файлов в корень проекта.
|
Теперь добавим конфигурационных файлов в корень проекта.
|
||||||
@ -581,3 +580,7 @@ $ ab_solver 1 2
|
|||||||
```
|
```
|
||||||
|
|
||||||
Если запаблишить проект, то у пользователя тоже установится ваша cli-программа.
|
Если запаблишить проект, то у пользователя тоже установится ваша cli-программа.
|
||||||
|
|
||||||
|
А на этом всё.
|
||||||
|
|
||||||
|
До новых встреч.
|
||||||
|
479
content/ru/python-speedup-with-rust.md
Normal file
479
content/ru/python-speedup-with-rust.md
Normal file
@ -0,0 +1,479 @@
|
|||||||
|
---
|
||||||
|
title: Ускоряем Python используя Rust.
|
||||||
|
description: Как встроить Rust в проект на Python.
|
||||||
|
position: 2
|
||||||
|
category: 'Python'
|
||||||
|
---
|
||||||
|
|
||||||
|
# Описание проблемы
|
||||||
|
|
||||||
|
Каким бы Python удобным не был, скоростью он похвастаться никак не может.
|
||||||
|
И для некоторых задач это достаточно критично.
|
||||||
|
|
||||||
|
Например, не так давно у меня была задача достать много информации
|
||||||
|
из файлов логов. Проблема в том, что один лог-файл занимает от 3-4 ГБ. А файлов
|
||||||
|
таких много и информацию достать требуется быстро. Изначальный вариант на Python
|
||||||
|
был написан за минут 15-20, но скорость его работы занимала 30-40 минут на один файл. После переписывания одной функции на Rust, скрипт отработал за 1 минуту.
|
||||||
|
|
||||||
|
Давайте напишем свой проект со встроенной функцией на Rust.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
Задача проекта будет следующей:
|
||||||
|
|
||||||
|
Дан файл лога запросов на некоторый сервер. Требуется
|
||||||
|
найти количество запросов к определённому файлу и сумму переданных байт.
|
||||||
|
|
||||||
|
Для демонстрации мы напишем 2 функции, одна будет использовать наивное решение задачи на питоне,
|
||||||
|
вторая будет выполнять парсинг на расте.
|
||||||
|
|
||||||
|
Сам файл лог имеет следующую структуру:
|
||||||
|
```
|
||||||
|
"$line_num" "-" "$method $url" "$current_time" "$bytes_sent"
|
||||||
|
```
|
||||||
|
|
||||||
|
где,
|
||||||
|
* $line_num - номер строчки;
|
||||||
|
* $method - HTTP метод;
|
||||||
|
* $url - URL до файла, содержащий один из сгенерированных идентификаторов;
|
||||||
|
* $current_time - текущее время (время выполнения запроса);
|
||||||
|
* $bytes_sent - сколько байт было отправлено.
|
||||||
|
|
||||||
|
Данный формат логов отчасти подражает формату [G-Core labs](https://gcorelabs.com/support/articles/115000511685/).
|
||||||
|
|
||||||
|
# Проект на Python
|
||||||
|
|
||||||
|
Сначала мы напишем весь функционал на Python.
|
||||||
|
Для этого создадим проект `python-rust` по [гайду из блога](/project-start).
|
||||||
|
|
||||||
|
Для создания CLI я буду использовать [typer](https://pypi.org/project/typer/).
|
||||||
|
|
||||||
|
Весь код проекта доступен в [репозитории](https://github.com/s3rius/blog_examples/tree/master/python-rust).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ poetry add typer
|
||||||
|
```
|
||||||
|
|
||||||
|
## Генератор логов
|
||||||
|
|
||||||
|
Для тестов напишем генератор лог-файла.
|
||||||
|
|
||||||
|
Данный генератор должен работать следующим образом.
|
||||||
|
Я указываю сколько строк лог-файла я хочу увидеть,
|
||||||
|
сколько уникальных id файлов использовать и название файла, куда писать.
|
||||||
|
|
||||||
|
Функция же генерирует лог в заданном формате.
|
||||||
|
|
||||||
|
```python{}[python_rust/main.py]
|
||||||
|
import secrets
|
||||||
|
import time
|
||||||
|
import uuid
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import typer
|
||||||
|
|
||||||
|
tpr = typer.Typer()
|
||||||
|
|
||||||
|
|
||||||
|
def quote(somthing: Any) -> str:
|
||||||
|
"""
|
||||||
|
Quote string.
|
||||||
|
|
||||||
|
:param somthing: any string.
|
||||||
|
:return: quoted string.
|
||||||
|
"""
|
||||||
|
return f'"{somthing}"'
|
||||||
|
|
||||||
|
|
||||||
|
@tpr.command()
|
||||||
|
def generator( # noqa: WPS210
|
||||||
|
output: Path,
|
||||||
|
lines: int = 2_000_000, # noqa: WPS303
|
||||||
|
ids: int = 1000,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Test log generator.
|
||||||
|
|
||||||
|
:param ids: how many file id's to generate.
|
||||||
|
:param output: output file path.
|
||||||
|
:param lines: how many lines to write, defaults to 2_000_000
|
||||||
|
"""
|
||||||
|
ids_pool = [uuid.uuid4().hex for _ in range(ids)]
|
||||||
|
|
||||||
|
with open(output, "w") as out_file:
|
||||||
|
for line_num in range(lines):
|
||||||
|
item_id = secrets.choice(ids_pool)
|
||||||
|
prefix = secrets.token_hex(60)
|
||||||
|
url = f"GET /{prefix}/{item_id}.jpg"
|
||||||
|
current_time = int(time.time())
|
||||||
|
bytes_sent = secrets.randbelow(800) # noqa: WPS432
|
||||||
|
line = [
|
||||||
|
quote(line_num),
|
||||||
|
quote("-"),
|
||||||
|
quote(url),
|
||||||
|
quote(current_time),
|
||||||
|
quote(bytes_sent),
|
||||||
|
]
|
||||||
|
out_file.write(" ".join(line))
|
||||||
|
out_file.write("\n")
|
||||||
|
typer.secho("Log successfully generated.", fg=typer.colors.GREEN)
|
||||||
|
|
||||||
|
|
||||||
|
@tpr.command()
|
||||||
|
def parser(input_file: Path, rust: bool = False) -> None:
|
||||||
|
"""
|
||||||
|
Parse given log file.
|
||||||
|
|
||||||
|
:param input_file: path of input file.
|
||||||
|
:param rust: use rust parser implementation.
|
||||||
|
"""
|
||||||
|
typer.secho("Not implemented", err=True, fg=typer.colors.RED)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
"""Main program entrypoint."""
|
||||||
|
tpr()
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Для удобства работы добавим в pyproject.toml информацию о командах.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[tool.poetry.scripts]
|
||||||
|
pyrust = "python_rust.main:main"
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь мы можем вызывать нашу программу.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ pyrust --help
|
||||||
|
Usage: pyrust [OPTIONS] COMMAND [ARGS]...
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--install-completion [bash|zsh|fish|powershell|pwsh]
|
||||||
|
Install completion for the specified shell.
|
||||||
|
--show-completion [bash|zsh|fish|powershell|pwsh]
|
||||||
|
Show completion for the specified shell, to
|
||||||
|
copy it or customize the installation.
|
||||||
|
--help Show this message and exit.
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
generator Test log generator.
|
||||||
|
parser Parse given log file.
|
||||||
|
```
|
||||||
|
|
||||||
|
Работает отлично.
|
||||||
|
|
||||||
|
С помощью данного генератора сгенерируем файл на 8 милионов строк.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ pyrust generator test.log --lines 8000000
|
||||||
|
Log successfully generated.
|
||||||
|
```
|
||||||
|
|
||||||
|
У нас получился файл на 1.5G. Для начального теста этого будет достаточно.
|
||||||
|
|
||||||
|
## Реализация парсера на Python
|
||||||
|
|
||||||
|
Представленный формат разделяет ифнормацию кавычками, поэтому
|
||||||
|
для сплита строк мы будем использовать встроенный в python модуль [shlex](https://docs.python.org/3/library/shlex.html).
|
||||||
|
|
||||||
|
Функция парсинга логов будет выглядить следующим образом:
|
||||||
|
|
||||||
|
|
||||||
|
```python{}[python_rust/py_parser.py]
|
||||||
|
import shlex
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, Tuple
|
||||||
|
|
||||||
|
|
||||||
|
def parse_python( # noqa: WPS210
|
||||||
|
filename: Path,
|
||||||
|
) -> Dict[str, Tuple[int, int]]:
|
||||||
|
"""
|
||||||
|
Parse log file with python.
|
||||||
|
|
||||||
|
:param filename: log file.
|
||||||
|
:return: parsed data.
|
||||||
|
"""
|
||||||
|
parsed_data = {}
|
||||||
|
with open(filename, "r") as input_file:
|
||||||
|
for line in input_file:
|
||||||
|
spl = shlex.split(line)
|
||||||
|
# Splitting method and actual url.
|
||||||
|
url = spl[2].split()[1]
|
||||||
|
# Splitting url by /
|
||||||
|
# This split will turn this "/test/aaa.png"
|
||||||
|
# into "aaa".
|
||||||
|
file_id = url.split("/")[-1].split(".")[0]
|
||||||
|
file_info = parsed_data.get(file_id)
|
||||||
|
# If information about file isn't found.
|
||||||
|
if file_info is None:
|
||||||
|
downloads, bytes_sent = 0, 0
|
||||||
|
else:
|
||||||
|
downloads, bytes_sent = file_info
|
||||||
|
# Incrementing counters.
|
||||||
|
downloads += 1
|
||||||
|
bytes_sent += int(spl[4])
|
||||||
|
# Saving back.
|
||||||
|
parsed_data[file_id] = (downloads, bytes_sent)
|
||||||
|
return parsed_data
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Давайте импортируем её, модифицируем команду парсинга и замерим скорость работы.
|
||||||
|
|
||||||
|
|
||||||
|
```python{}[python_rust/main.py]
|
||||||
|
@tpr.command()
|
||||||
|
def parser(input_file: Path, rust: bool = False) -> None:
|
||||||
|
"""
|
||||||
|
Parse given log file.
|
||||||
|
|
||||||
|
:param input_file: path of input file.
|
||||||
|
:param rust: use rust parser implementation.
|
||||||
|
"""
|
||||||
|
if rust:
|
||||||
|
typer.secho("Not implemented", input_file, color=typer.colors.RED)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
parsed_data = parse_python(input_file)
|
||||||
|
|
||||||
|
typer.secho(
|
||||||
|
f"Found {len(parsed_data)} files", # noqa: WPS237
|
||||||
|
color=typer.colors.CYAN,
|
||||||
|
)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Как можно видеть из кода, мы просто подсчитываем итоговое количество найденных файлов.
|
||||||
|
|
||||||
|
Посмотрим сколько времени займёт парсинг нашего сгенерированного файла.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ time pyrust parser "test.log"
|
||||||
|
Found 1000 files
|
||||||
|
pyrust parser test.log 2443.42s user 2.10s system 99% cpu 40:59.30 total
|
||||||
|
```
|
||||||
|
|
||||||
|
Обработка данного файла заняла 40 минут. Это довольно печальный результат.
|
||||||
|
Самое время попробовать использовать Rust.
|
||||||
|
|
||||||
|
# Интеграция Rust
|
||||||
|
|
||||||
|
Для интеграции Rust в python код я буду использовать [PyO3](https://github.com/PyO3/pyo3) для биндингов и [maturin](https://github.com/PyO3/maturin) для сборки проекта.
|
||||||
|
|
||||||
|
Начнём с инициализации проекта и установки сборщика. В корневой папке проекта
|
||||||
|
создайте папку проекта с растом.
|
||||||
|
|
||||||
|
<b-message type="is-info" has-icon>
|
||||||
|
|
||||||
|
В теории можно использовать maturin как основной инструмент сборки проекта,
|
||||||
|
но это лишает вас всех прелестей poetry. Поэтому удобнее использовать Rust в
|
||||||
|
подпроекте и указать его как зависимость в списке зависимостей своего проекта.
|
||||||
|
|
||||||
|
</b-message>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ cargo new --lib rusty_log_parser
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь создадим `pyproject.toml` и напишем туда описание нашего python пакета.
|
||||||
|
|
||||||
|
```toml{}[pyproject.toml]
|
||||||
|
[tool.poetry]
|
||||||
|
name = "rusty_log_parser"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Log file parser with Rust core"
|
||||||
|
authors = ["Pavel Kirilin <win10@list.ru>"]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["maturin>=0.12,<0.13"]
|
||||||
|
build-backend = "maturin"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Также поправим `Cargo.toml`.
|
||||||
|
|
||||||
|
```toml{}[Cargo.toml]
|
||||||
|
[lib]
|
||||||
|
# Название модуля
|
||||||
|
name = "rusty_log_parser"
|
||||||
|
# Обязательный тип крейта, чтобы можно было использовать его
|
||||||
|
# в питоне.
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# Библиотека биндингов для питона.
|
||||||
|
pyo3 = { version = "0.15.1", features = ["extension-module"] }
|
||||||
|
# Библиотека для сплита. Повторяет функционал shlex.
|
||||||
|
shell-words = "1.0.0"
|
||||||
|
```
|
||||||
|
После этих нехитрых изменений попробуем собрать проект.
|
||||||
|
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ cd rusty_log_parser
|
||||||
|
$ cargo build
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь напишем сам парсер логов в Rust.
|
||||||
|
|
||||||
|
```rust{}[rusty_log_parser/src/lib.rs]
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io;
|
||||||
|
use std::io::BufRead;
|
||||||
|
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
/// Parse log file in rust.
|
||||||
|
/// This function parses log file and returns HashMap with Strings as keys and
|
||||||
|
/// Tuple of two u128 as values.
|
||||||
|
#[pyfunction]
|
||||||
|
fn parse_rust(filename: &str) -> PyResult<HashMap<String, (u128, u128)>> {
|
||||||
|
let mut result_map = HashMap::new();
|
||||||
|
// Iterating over file.
|
||||||
|
for log in io::BufReader::new(File::open(filename)?).lines().flatten() {
|
||||||
|
// Splitting log string.
|
||||||
|
if let Ok(mut spl) = shell_words::split(&log) {
|
||||||
|
// Getting file id.
|
||||||
|
let file_id_opt = spl.get_mut(2).and_then(|http_req| {
|
||||||
|
// Splitting method and URL.
|
||||||
|
http_req.split(' ').into_iter().nth(1).and_then(|url| {
|
||||||
|
// Splitting by / and getting the last split.
|
||||||
|
url.split('/')
|
||||||
|
.into_iter()
|
||||||
|
.last()
|
||||||
|
// Split file id and extension.
|
||||||
|
.and_then(|item_url| item_url.split('.').into_iter().next())
|
||||||
|
// Turning &str into String.
|
||||||
|
.map(String::from)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
// Getting number of bytes sent.
|
||||||
|
let bytes_sent_opt =
|
||||||
|
spl.get_mut(4)
|
||||||
|
// Parsing string to u128
|
||||||
|
.and_then(|bytes_str| match bytes_str.parse::<u128>() {
|
||||||
|
Ok(bytes_sent) => Some(bytes_sent),
|
||||||
|
Err(_) => None,
|
||||||
|
});
|
||||||
|
if file_id_opt.is_none() || bytes_sent_opt.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let file_id = file_id_opt.unwrap();
|
||||||
|
let bytes_sent = bytes_sent_opt.unwrap();
|
||||||
|
|
||||||
|
match result_map.get(&file_id) {
|
||||||
|
Some(&(downloads, total_bytes_sent)) => {
|
||||||
|
result_map.insert(file_id, (downloads + 1, total_bytes_sent + bytes_sent));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
result_map.insert(file_id, (1, bytes_sent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(result_map)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Python module implemented in Rust. The name of this function must match
|
||||||
|
/// the `lib.name` setting in the `Cargo.toml`, else Python will not be able to
|
||||||
|
/// import the module.
|
||||||
|
#[pymodule]
|
||||||
|
fn rusty_log_parser(_py: Python, m: &PyModule) -> PyResult<()> {
|
||||||
|
// Adding function to the module.
|
||||||
|
m.add_function(wrap_pyfunction!(parse_rust, m)?)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Это и будет наша функция, которую мы будем вызывать.
|
||||||
|
Теперь добавим `python` обёртку для нашего модуля.
|
||||||
|
|
||||||
|
Для этого в проекте `rusty_log_parser` надо создать
|
||||||
|
папку с тем же названием что и проект. В данном случае
|
||||||
|
это будет `rusty_log_parser`.
|
||||||
|
|
||||||
|
Внутри этой папки мы создадим `__init__.py`, в котором
|
||||||
|
опишем и задокументируем доступные функции модуля.
|
||||||
|
|
||||||
|
```python{}[rusty_log_parser/rusty_log_parser/__init__.py]
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, Tuple
|
||||||
|
|
||||||
|
from .rusty_log_parser import parse_rust as _parse_rust
|
||||||
|
|
||||||
|
|
||||||
|
def parse_rust(input_file: Path) -> Dict[str, Tuple[int, int]]:
|
||||||
|
"""
|
||||||
|
Parse log file using Rust as a backend.
|
||||||
|
|
||||||
|
:param input_file: log file to parse.
|
||||||
|
:return: Parsed
|
||||||
|
"""
|
||||||
|
return _parse_rust(str(input_file.expanduser()))
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Осталось только добавить свеженаписанный пакет как зависимости нашего проекта.
|
||||||
|
|
||||||
|
Для этого в корневой `pyproject.toml` надо добавить следующую зависимость.
|
||||||
|
|
||||||
|
|
||||||
|
```toml{}[pyproject.toml]
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
...
|
||||||
|
rusty_log_parser = { path = "./rusty_log_parser" }
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь мы можем использовать нашу функцию.
|
||||||
|
|
||||||
|
Перепишем нашу функцию парсера таким образом,
|
||||||
|
чтобы она использовала rust, когда был передан
|
||||||
|
соответствующий параметр.
|
||||||
|
|
||||||
|
```python{}[python_rust/main.py]
|
||||||
|
from rusty_log_parser import parse_rust
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
@tpr.command()
|
||||||
|
def parser(input_file: Path, rust: bool = False) -> None:
|
||||||
|
"""
|
||||||
|
Parse given log file.
|
||||||
|
|
||||||
|
:param input_file: path of input file.
|
||||||
|
:param rust: use rust parser implementation.
|
||||||
|
"""
|
||||||
|
if rust:
|
||||||
|
parsed_data = parse_rust(input_file)
|
||||||
|
else:
|
||||||
|
parsed_data = parse_python(input_file)
|
||||||
|
|
||||||
|
typer.secho(
|
||||||
|
f"Found {len(parsed_data)} files", # noqa: WPS237
|
||||||
|
color=typer.colors.CYAN,
|
||||||
|
)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь проведём итоговый замер.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ time pyrust parser test.log --rust
|
||||||
|
Found 1000 files
|
||||||
|
pyrust parser test.log --rust 20.44s user 0.35s system 99% cpu 20.867 total
|
||||||
|
```
|
||||||
|
|
||||||
|
Итого виден небольшой выйигрыш во времени в 2423 секунды, из чего следует,
|
||||||
|
что реализация на Rust быстрее всего в 122 раза.
|
||||||
|
|
||||||
|
Данной статьёй я не подталкиваю всех переписывать всё на Rust,
|
||||||
|
а просто ознакамливаю с возможностями. Но если всё же хочется,
|
||||||
|
то можно и попробовать.
|
||||||
|
|
||||||
|
До новых встреч.
|
@ -358,6 +358,8 @@ sudo systemctl start k3s.service
|
|||||||
|
|
||||||
# Ваше первое приложение в кластере.
|
# Ваше первое приложение в кластере.
|
||||||
|
|
||||||
|
Вы можете следовать статье, а можете подсмотреть весь код в [репозитории](https://github.com/s3rius/blog_examples/tree/master/req_counter).
|
||||||
|
|
||||||
### Сервер
|
### Сервер
|
||||||
|
|
||||||
Давайте создадим своё первое приложение.
|
Давайте создадим своё первое приложение.
|
||||||
|
@ -468,4 +468,4 @@ services:
|
|||||||
|
|
||||||
[](/images/traefik_imgs/traefik_web.png)
|
[](/images/traefik_imgs/traefik_web.png)
|
||||||
|
|
||||||
Разве это не круто?
|
До новых встреч.
|
||||||
|
@ -38,11 +38,14 @@ spec:
|
|||||||
httpGet:
|
httpGet:
|
||||||
path: /
|
path: /
|
||||||
port: http
|
port: http
|
||||||
|
initialDelaySeconds: 40
|
||||||
|
periodSeconds: 20
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /
|
path: /
|
||||||
port: http
|
port: http
|
||||||
initialDelaySeconds: 15
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 15
|
||||||
resources:
|
resources:
|
||||||
{{- toYaml .Values.resources | nindent 12 }}
|
{{- toYaml .Values.resources | nindent 12 }}
|
||||||
{{- with .Values.nodeSelector }}
|
{{- with .Values.nodeSelector }}
|
||||||
|
Reference in New Issue
Block a user