This commit is contained in:
shenjack 2024-03-03 17:12:29 +08:00
parent 315417b8a8
commit becfc8c634
Signed by: shenjack
GPG Key ID: 7B1134A979775551
8 changed files with 223 additions and 150 deletions

1
Cargo.lock generated
View File

@ -468,6 +468,7 @@ dependencies = [
"chrono",
"clap",
"colored",
"lazy_static",
"tracing",
"tracing-subscriber",
]

View File

@ -14,6 +14,7 @@ tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["time"] }
chrono = "0.4.34"
colored = "2.1.0"
lazy_static = "1.4.0"
[features]
default = ["simd"]

127
src/cacluate.rs Normal file
View File

@ -0,0 +1,127 @@
use crate::name::{Namer, TeamNamer};
use std::{io::Write, path::PathBuf};
use base16384::Base16384Utf8;
use tracing::{info, warn};
use colored::Colorize;
pub fn show_name(namer: &Namer) -> String {
format!(
"HP|{} 攻|{} 防|{} 速|{} 敏|{} 魔|{} 抗|{} 智|{} 八围:{}",
namer.name_prop[0],
namer.name_prop[1],
namer.name_prop[2],
namer.name_prop[3],
namer.name_prop[4],
namer.name_prop[5],
namer.name_prop[6],
namer.name_prop[7],
namer.get_property()
)
}
/// 根据 u64 生成对应的 name
/// 转换成 base 16384
/// 禁用:
/// U00 ~ U1F ,换行,制表符 等
/// ? , 问号
/// U2000 - U202F , unicode特殊空格 等
/// 不可以空格开头
#[inline(always)]
pub fn gen_name(id: u64) -> String {
let id_bytes = id.to_be_bytes();
Base16384Utf8::encode(id_bytes.as_slice())
}
pub struct CacluateConfig {
/// 开始的 id
pub start: u64,
/// 结束的 id
pub end: u64,
/// 线程数
pub thread_count: u32,
/// 八围预期值
pub prop_expect: u32,
/// 八围允许范围
pub prop_allow: u32,
/// 队伍名称
pub team: String,
/// 预期状态输出时间间隔 (秒)
pub report_interval: u64,
}
#[inline(always)]
pub fn cacl(config: CacluateConfig, id: u64, outfile: &PathBuf) {
// 初始猜测的时间间隔
let mut report_interval = 10000; // 第一次猜测测 1w 次, 获取初始数据
let mut run_speed = 0.0;
let mut start_time = std::time::Instant::now();
let mut k: u64 = 0;
let mut get_count: u32 = 0;
// 提前准备好 team_namer
let team_namer = TeamNamer::new(&config.team).unwrap();
for i in (config.start + id..config.end).step_by(config.thread_count as usize) {
let name = gen_name(i as u64);
let namer = Namer::new_from_team_namer_unchecked(&team_namer, name.as_str());
let prop = namer.get_property();
if (prop + config.prop_allow as f32) > config.prop_expect as f32 {
get_count += 1;
let name = gen_name(i as u64);
let full_name = format!("{}@{}", name, config.team);
info!("Id:{:>15}|{}|{}", i, full_name, show_name(&namer));
// 写入 (写到最后一行)
match std::fs::OpenOptions::new()
.append(true)
.create(true)
.open(outfile)
.and_then(|mut file| file.write(format!("{}\n", full_name).as_bytes()))
{
Ok(_) => {}
Err(e) => {
warn!("写入文件<{:?}>失败: {}", outfile, e);
}
}
}
k += 1;
if k >= report_interval as u64 {
let now = std::time::Instant::now();
let d_t: std::time::Duration = now.duration_since(start_time);
let new_run_speed = k as f64 / d_t.as_secs_f64();
// 预估剩余时间
let wait_time = (config.end - i) / config.thread_count as u64 / new_run_speed as u64;
let wait_time = chrono::Duration::seconds(wait_time as i64);
// 转换成 时:分:秒
// 根据实际运行速率来调整 report_interval
report_interval = config.report_interval * new_run_speed as u64;
info!(
"|{:>2}|Id:{:>15}|{:6.2}/s {:>3.3}E/d {:>5.2}{}|{:<3}|预计:{}:{}:{}|",
id,
i,
new_run_speed,
new_run_speed * 8.64 / 1_0000.0,
d_t.as_secs_f64(),
// 根据对比上一段运行速度 输出 emoji
// ⬆️ ➡️ ⬇️
if new_run_speed > run_speed {
"⬆️".green()
} else if new_run_speed < run_speed {
// 橙色
"⬇️".red()
} else {
"➡️".blue()
},
get_count,
wait_time.num_hours(),
wait_time.num_minutes() % 60,
wait_time.num_seconds() % 60
);
run_speed = new_run_speed;
start_time = std::time::Instant::now();
k = 0;
}
}
}

View File

@ -74,4 +74,32 @@ pub fn predict_13(name: &Namer) -> f64 {
sum
}
/*function Poly(x) {
var xp = new Array()
for (let y = 0; y < 1034; y++) {
var l = 44
var i = 0, p = 0, q = 0, r = 0
var j = y
for (let k = 0; k < 45; k++) {
i++;
if (i > 2) p++;
q = j;
j = j - l + p;
if (j < 0) break;
}
if (i == 1) r = x[q]
if (i > 1) {
r = x[p] * x[p + q]
}
xp[y] = r
}
return xp
} */
pub fn poly(name: &Namer) -> [f64; 1034] {
let mut result = [0.0; 1034];
result
}
pub fn predict_20(name: &Namer) -> f64 { 0.0 }

View File

@ -1,5 +1,5 @@
const MODEL: [f64; 1035] = [
-10030.80226727643822,
pub const BASE: f64 = -10030.80226727643822;
pub const MODEL: [f64; 1034] = [
4.23812347791656,
64.98665601677689,
2.08468428104832,
@ -1036,8 +1036,8 @@ const MODEL: [f64; 1035] = [
0.00530345515535,
];
const MODEL_QD: [f64; 1035] = [
106835.41068098737742,
pub const BASE_QD: f64 = 106835.41068098737742;
pub const MODEL_QD: [f64; 1034] = [
-113.56364767281147,
-122.71766831211856,
-318.24366544790598,

0
src/generate.rs Normal file
View File

View File

@ -1,42 +1,18 @@
#![feature(portable_simd)]
#![feature(slice_swap_unchecked)]
mod cacluate;
mod evaluate;
mod generate;
mod name;
use std::{io::Write, path::PathBuf};
use std::path::PathBuf;
use base16384::Base16384Utf8;
use clap::Parser;
use colored::Colorize;
use tracing::{info, warn};
/// 根据 u64 生成对应的 name
/// 转换成 base 16384
/// 禁用:
/// U00 ~ U1F ,换行,制表符 等
/// ? , 问号
/// U2000 - U202F , unicode特殊空格 等
/// 不可以空格开头
#[inline(always)]
pub fn gen_name(id: u64) -> String {
let id_bytes = id.to_be_bytes();
Base16384Utf8::encode(id_bytes.as_slice())
}
use crate::cacluate::CacluateConfig;
pub fn show_name(namer: &name::Namer) -> String {
format!(
"HP|{} 攻|{} 防|{} 速|{} 敏|{} 魔|{} 抗|{} 智|{} 八围:{}",
namer.name_prop[0],
namer.name_prop[1],
namer.name_prop[2],
namer.name_prop[3],
namer.name_prop[4],
namer.name_prop[5],
namer.name_prop[6],
namer.name_prop[7],
namer.get_property()
)
}
#[allow(non_upper_case_globals)]
const allow_d: u32 = 10;
@ -63,80 +39,16 @@ pub struct Command {
pub report_interval: u64,
}
/// 大概的预计速度
/// 来自 5600X 的运行效率
pub const GUESS_SPEED: u64 = 623772;
#[inline(always)]
fn cacl(config: Command, id: u64, outfile: &PathBuf) {
// 初始猜测的时间间隔
let mut report_interval = config.report_interval * GUESS_SPEED;
let mut run_speed = GUESS_SPEED as f64;
let mut start_time = std::time::Instant::now();
let mut k: u64 = 0;
let mut get_count: u32 = 0;
// 提前准备好 team_namer
let team_namer = name::TeamNamer::new_unchecked(&config.team);
for i in (config.start + id..config.end).step_by(config.thread_count as usize) {
let name = gen_name(i as u64);
let namer = name::Namer::new_from_team_namer_unchecked(&team_namer, name.as_str());
let prop = namer.get_property();
if (prop + allow_d as f32) > config.prop_expect as f32 {
get_count += 1;
let name = gen_name(i as u64);
let full_name = format!("{}@{}", name, config.team);
info!("Id:{:>15}|{}|{}", i, full_name, show_name(&namer));
// 写入 (写到最后一行)
match std::fs::OpenOptions::new()
.append(true)
.create(true)
.open(outfile)
.and_then(|mut file| file.write(format!("{}\n", full_name).as_bytes()))
{
Ok(_) => {}
Err(e) => {
warn!("写入文件<{:?}>失败: {}", outfile, e);
}
}
}
k += 1;
if k >= report_interval as u64 {
let now = std::time::Instant::now();
let d_t: std::time::Duration = now.duration_since(start_time);
let new_run_speed = k as f64 / d_t.as_secs_f64();
// 预估剩余时间
let wait_time = (config.end - i) / config.thread_count as u64 / new_run_speed as u64;
let wait_time = chrono::Duration::seconds(wait_time as i64);
// 转换成 时:分:秒
// 根据实际运行速率来调整 report_interval
report_interval = config.report_interval * new_run_speed as u64;
info!(
"|{:>2}|Id:{:>15}|{:6.2}/s {:>3.3}E/d {:>5.2}{}|{:<3}|预计:{}:{}:{}|",
id,
i,
new_run_speed,
new_run_speed * 8.64 / 1_0000.0,
d_t.as_secs_f64(),
// 根据对比上一段运行速度 输出 emoji
// ⬆️ ➡️ ⬇️
if new_run_speed > run_speed {
"⬆️".green()
} else if new_run_speed < run_speed {
// 橙色
"⬇️".red()
} else {
"➡️".blue()
},
get_count,
wait_time.num_hours(),
wait_time.num_minutes() % 60,
wait_time.num_seconds() % 60
);
run_speed = new_run_speed;
start_time = std::time::Instant::now();
k = 0;
impl Command {
pub fn as_cacl_config(&self) -> CacluateConfig {
CacluateConfig {
start: self.start,
end: self.end,
thread_count: self.thread_count,
prop_expect: self.prop_expect,
prop_allow: allow_d,
team: self.team.clone(),
report_interval: self.report_interval,
}
}
}
@ -170,12 +82,12 @@ fn main() {
for i in 0..cli_arg.thread_count {
n += 1;
let cli = cli_arg.clone();
let config = cli_arg.as_cacl_config();
let out_path = out_path.clone();
let thread_name = format!("thread_{}", i);
threads.push(std::thread::spawn(move || {
info!("线程 {} 开始计算", thread_name);
cacl(cli, n, &out_path);
cacluate::cacl(config, n, &out_path);
info!("线程 {} 结束计算", thread_name);
}));
}

View File

@ -1,10 +1,26 @@
use std::cmp::min;
#[cfg(feature = "simd")]
use std::simd::cmp::SimdPartialOrd;
#[cfg(feature = "simd")]
use std::simd::u8x64;
use tracing::warn;
use lazy_static::lazy_static;
// let simd_181 = u8x64::splat(181);
// let simd_199 = u8x64::splat(199);
// let simd_128 = u8x64::splat(128);
// let simd_53 = u8x64::splat(53);
// let simd_63 = u8x64::splat(63);
// let simd_32 = u8x64::splat(32);
lazy_static! {
static ref SIMD_199: u8x64 = u8x64::splat(199);
static ref SIMD_181: u8x64 = u8x64::splat(181);
static ref SIMD_160: u8x64 = u8x64::splat(160);
static ref SIMD_128: u8x64 = u8x64::splat(128);
static ref SIMD_63: u8x64 = u8x64::splat(63);
static ref SIMD_53: u8x64 = u8x64::splat(53);
static ref SIMD_32: u8x64 = u8x64::splat(32);
}
#[inline(always)]
pub fn median<T>(x: T, y: T, z: T) -> T
@ -137,18 +153,14 @@ impl Namer {
let name_len = name_bytes.len();
let b_name_len = name_len + 1;
for _ in 0..2 {
// 手动处理 0 的问题
// 手动swap
let mut s = 0_u8;
val.swap(s as usize, 0);
unsafe { val.swap_unchecked(s as usize, 0) };
let mut k = 0;
for i in 0..256 {
// s = s.wrapping_add(name_bytes[i % name_len]);
s = s.wrapping_add(match i % b_name_len {
0 => 0,
k => name_bytes[k - 1],
});
s = s.wrapping_add(if k == 0 { 0 } else { name_bytes[k - 1] });
s = s.wrapping_add(val[i]);
val.swap(i, s as usize);
unsafe { val.swap_unchecked(i, s as usize) }
k = if k == b_name_len - 1 { 0 } else { k + 1 };
}
}
// simd 优化
@ -156,33 +168,27 @@ impl Namer {
{
let mut simd_val = val.clone();
let mut simd_val_b = [0_u8; 256];
let mut simd_target = [false; 256];
let simd_181 = u8x64::splat(181);
let simd_160 = u8x64::splat(160);
let simd_63 = u8x64::splat(63);
let simd_88 = u8x64::splat(88);
let simd_217 = u8x64::splat(217);
for i in (0..256).step_by(64) {
// 一次性加载64个数字
let mut x = u8x64::from_slice(&simd_val[i..]);
x = x * simd_181 + simd_160;
x = x * *SIMD_181 + *SIMD_160;
// 写入到 simd_val
x.copy_to_slice(&mut simd_val[i..]);
// 提前判断 > 88 && < 217
let mask = x.simd_ge(simd_88) & x.simd_lt(simd_217);
// 写入到 simd_target
let mask: [bool; 64] = mask.to_array();
simd_target[i..i + 64].copy_from_slice(&mask);
x = x & simd_63;
x.copy_to_slice(&mut simd_val_b[i..]);
let y = x & *SIMD_63;
y.copy_to_slice(&mut simd_val_b[i..]);
}
let mut mod_count = 0;
for i in 0..96 {
if simd_target[i] {
name_base[mod_count as usize] = simd_val_b[i];
if simd_val[i] > 88 && simd_val[i] < 217 {
// name_base[mod_count as usize] = simd_val_b[i];
unsafe {
*name_base.get_unchecked_mut(mod_count as usize) =
*simd_val_b.get_unchecked(i);
}
mod_count += 1;
}
if mod_count > 30 {
@ -191,8 +197,12 @@ impl Namer {
}
if mod_count < 31 {
for i in 96..256 {
if simd_target[i] {
name_base[mod_count as usize] = simd_val_b[i];
if simd_val[i] > 88 && simd_val[i] < 217 {
// name_base[mod_count as usize] = simd_val_b[i];
unsafe {
*name_base.get_unchecked_mut(mod_count as usize) =
*simd_val_b.get_unchecked(i);
}
mod_count += 1;
}
if mod_count > 30 {
@ -252,18 +262,12 @@ impl Namer {
{
let mut simd_val = self.val.clone();
let mut simd_val_b = self.val.clone();
let simd_181 = u8x64::splat(181);
let simd_199 = u8x64::splat(199);
let simd_128 = u8x64::splat(128);
let simd_53 = u8x64::splat(53);
let simd_63 = u8x64::splat(63);
let simd_32 = u8x64::splat(32);
for i in (0..256).step_by(64) {
let mut x = u8x64::from_slice(&simd_val[i..]);
let mut y = u8x64::from_slice(&simd_val_b[i..]);
x = x * simd_181 + simd_199 & simd_128;
y = y * simd_53 & simd_63 ^ simd_32;
x = x * *SIMD_181 + *SIMD_199 & *SIMD_128;
y = y * *SIMD_53 & *SIMD_63 ^ *SIMD_32;
x.copy_to_slice(&mut simd_val[i..]);
y.copy_to_slice(&mut simd_val_b[i..]);
}
@ -393,10 +397,6 @@ mod test {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
assert_eq!(namer.name_base.to_vec(), base_name_vec);
namer.update_skill();
// update skill 之后才会是完整的 name
let full_base_name_vec: Vec<u8> = vec![
53, 0, 40, 4, 58, 61, 37, 46, 56, 51, 21, 20, 27, 17, 15, 26, 13, 30, 52, 63, 36, 30,
57, 34, 22, 37, 35, 6, 12, 25, 50, 49, 59, 23, 49, 27, 51, 58, 39, 28, 60, 20, 31, 36,
@ -405,6 +405,10 @@ mod test {
41, 55, 5, 34, 3, 7, 33, 33, 45, 16, 16, 32, 43, 18, 44, 22, 14, 17, 10, 11, 53, 18,
44, 19, 52, 2, 32, 12, 8, 2, 54, 26, 48, 8, 3, 63, 54, 19, 25,
];
assert_eq!(namer.name_base.to_vec(), base_name_vec);
namer.update_skill();
// update skill 之后才会是完整的 name
assert_eq!(namer.name_base.to_vec(), full_base_name_vec);
}