< 返回版块

Reiase 发表于 2024-03-18 00:46

Tags:cli,config

Hyperparameter

Hyperparameter 是一个为 Rust 和 Python 设计的高性能配置系统,它支持以下特性:

  1. 高性能:提供快速的参数访问,允许用户在代码中自由读写参数,无需担心性能问题。
  2. 作用域管理:通过作用域管理参数的定义和使用,确保参数值的隔离和安全。
  3. 命令行集成:支持在应用的命令行中自动展示所有参数及其帮助信息。

最小示例

以下是一个简单示例,展示如何使用 Hyperparameter 构建一个命令行程序:

use clap::Parser;
use hyperparameter::*;

#[derive(Parser)]
#[command(after_long_help=generate_params_help())]
struct CommandLineArgs {
    /// 在命令行以`-D key=value`格式指定参数
    #[arg(short = 'D', long)]
    define: Vec<String>,
}

fn main() {
    let args = CommandLineArgs::parse();
    with_params! {
        params ParamScope::from(&args.define); // 从命令行接收全部参数

        // 读取参数`example.param1`,若未指定则使用默认值`1`.
        println!("param1={}", get_param!(example.param1, 1));
        // 读取参数`example.param2`,在执行`<app> --help`时输出帮助信息.
        println!("param2={}", get_param!(example.param2, false, "help for example.param2"));
    }
}

当执行clap_mini --help时,在帮助信息结尾出现了Hyperparameters一节,说明了超参名称及其帮助信息:

Usage: clap_mini [OPTIONS]

Options:
  -D, --define <DEFINE>
          Specifies hyperparameters in the format `-D key=value` via the command line

  -h, --help
          Print help (see a summary with '-h')

Hyperparameters:
  example.param2
        help for example.param2

根据提示,可以使用-D example.param2=<value>来指定参数取值:

$ clap_mini # 默认取值
param1=1
param2=false

$ clap_mini -D example.param2=true
param1=1
param2=true

结合配置文件使用

Hyperparameter 也支持与配置文件结合使用。以下示例展示了如何整合配置文件、命令行参数和用户自定义配置:

use std::path::Path;

use clap::Parser;
use config::{self, File};
use hyperparameter::*;

#[derive(Parser)]
#[command(after_long_help=generate_params_help())]
struct CommandLineArgs {
    /// 在命令行以`-D key=value`格式指定参数
    #[arg(short = 'D', long)]
    define: Vec<String>,

    /// 在命令行以`-C <path>`格式指定配置文件
    #[arg(short = 'C', long, default_value = "examples/rust/cfg.toml")]
    config: String,
}

fn main() {
    let args = CommandLineArgs::parse();
    let config_path = Path::new(&args.config);
    let config = config::Config::builder()
        .add_source(File::from(config_path))
        .build().unwrap();

    println!("param1={} // No scope", get_param!(example.param1, "default".to_string()));

    with_params! { // 配置文件参数作用域
        params config.param_scope();

        println!("param1={} // cfg file scope", get_param!(example.param1, "default".to_string()));
        with_params! { // 命令行参数作用域
            params ParamScope::from(&args.define);

            println!("param1={} // cmdline args scope", get_param!(example.param1, "default".to_string(), "Example param1"));
            with_params! { // 用户自定义作用域
                set example.param1= "scoped".to_string();

                println!("param1={} // user-defined scope", get_param!(example.param1, "default".to_string()));
            }
        }
    }
}

直接执行命令clap_layered后得到如下输出:

param1=default     // No scope            # 未进入任何scope
param1=from config // cfg file scope      # 进入配置文件scope,参数取值受配置文件影响
param1=from config // cmdline args scope  # 进入命令行scope,命令行覆盖配置文件
param1=scoped      // user-defined scope  # 进入自定义scope,自定义取值覆盖命令行

可以看到:

  1. 嵌套的scope逐层覆盖,内层scope中参数覆盖外层scope;
  2. 命令行scope未指定参数,因此继承了外层scope的取值

若使用命令行指定example.param1的取值,则得到如下输入:

$ clap_layered -D example.param1="from cmdline"
param1=default  // No scope
param1=from config      // cfg file scope
param1=from cmdline     // cmdline args scope
param1=scoped   // user-defined scope

https://github.com/reiase/hyperparameter

评论区

写评论
作者 Reiase 2024-03-18 13:04

一般说高性能是指用了高性能领域的优化手段,比如无锁数据结构、编译期计算和SIMD加速等,而不是具体性能高多少。这种实现大多情况下会比用户自己手动优化要更加充分,封装出来也是希望帮助用户能以更低的开发成本获得更高的性能。

具体到Hyperparameter这个库,主要的高性能优化手段有三个:

  1. 无锁设计:参数存储使用thread local,并发环境不需要锁操作;
  2. 编译期哈希计算:运行期的参数读取无需计算哈希,开销仅相当于读取一个BTree<i64, Any>结构;
  3. 存储后端的选取进行过benchmark;

--
👇
zylthinking: 我就没见过啥东西没号称自己是高性能的

zylthinking 2024-03-18 09:25

我就没见过啥东西没号称自己是高性能的

1 共 2 条评论, 1 页