mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-10-29 16:05:35 +00:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5041aec2d | ||
|
|
7fc954a6dc | ||
|
|
cf3087d7ca | ||
|
|
7a8c7356fa | ||
|
|
f24735a86f |
@@ -11,8 +11,17 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const list = defineModel<string[]>({ required: true })
|
const list = defineModel<string[]>({ required: true })
|
||||||
|
|
||||||
|
const fallbackUrl = () => {
|
||||||
|
const protoKeys = Object.keys(props.protos)
|
||||||
|
const defaultProto = protoKeys.includes('tcp')
|
||||||
|
? 'tcp'
|
||||||
|
: (protoKeys[0] ?? 'tcp')
|
||||||
|
const defaultPort = props.protos[defaultProto] ?? 11010
|
||||||
|
return `${defaultProto}://0.0.0.0:${defaultPort}`
|
||||||
|
}
|
||||||
|
|
||||||
const addUrl = () => {
|
const addUrl = () => {
|
||||||
list.value.push(props.defaultUrl || 'tcp://0.0.0.0:11010')
|
list.value.push(props.defaultUrl || fallbackUrl())
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeUrl = (index: number) => {
|
const removeUrl = (index: number) => {
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { defineComponent, h, nextTick, ref } from 'vue'
|
||||||
|
import UrlListInput from '../src/components/UrlListInput.vue'
|
||||||
|
|
||||||
|
const ButtonStub = defineComponent({
|
||||||
|
name: 'Button',
|
||||||
|
emits: ['click'],
|
||||||
|
setup(_, { slots, emit }) {
|
||||||
|
return () => h('button', { onClick: (event: MouseEvent) => emit('click', event) }, slots.default?.())
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const UrlInputStub = defineComponent({
|
||||||
|
name: 'UrlInput',
|
||||||
|
setup(_, { slots }) {
|
||||||
|
return () => h('div', slots.actions?.())
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
function mountUrlListInput(protos: Record<string, number>, defaultUrl?: string) {
|
||||||
|
const urls = ref<string[]>([])
|
||||||
|
const wrapper = mount(defineComponent({
|
||||||
|
components: { UrlListInput },
|
||||||
|
setup() {
|
||||||
|
return { urls, protos, defaultUrl }
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<UrlListInput
|
||||||
|
v-model="urls"
|
||||||
|
:protos="protos"
|
||||||
|
:default-url="defaultUrl"
|
||||||
|
add-label="add_url"
|
||||||
|
/>
|
||||||
|
`,
|
||||||
|
}), {
|
||||||
|
global: {
|
||||||
|
stubs: {
|
||||||
|
Button: ButtonStub,
|
||||||
|
UrlInput: UrlInputStub,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return { wrapper, urls }
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('UrlListInput.vue add fallback', () => {
|
||||||
|
it('derives the fallback URL from protos when defaultUrl is not provided', async () => {
|
||||||
|
const { wrapper, urls } = mountUrlListInput({ tcp: 11010, udp: 11010 })
|
||||||
|
|
||||||
|
await wrapper.find('.cursor-pointer').trigger('click')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(urls.value).toEqual(['tcp://0.0.0.0:11010'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('falls back to the first available protocol when tcp is not present', async () => {
|
||||||
|
const { wrapper, urls } = mountUrlListInput({ udp: 22000 })
|
||||||
|
|
||||||
|
await wrapper.find('.cursor-pointer').trigger('click')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(urls.value).toEqual(['udp://0.0.0.0:22000'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('falls back to tcp default port when protos is empty', async () => {
|
||||||
|
const { wrapper, urls } = mountUrlListInput({})
|
||||||
|
|
||||||
|
await wrapper.find('.cursor-pointer').trigger('click')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(urls.value).toEqual(['tcp://0.0.0.0:11010'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('supports port-zero fallback from protos', async () => {
|
||||||
|
const { wrapper, urls } = mountUrlListInput({ tcp: 0, udp: 0 })
|
||||||
|
|
||||||
|
await wrapper.find('.cursor-pointer').trigger('click')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(urls.value).toEqual(['tcp://0.0.0.0:0'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('uses defaultUrl when provided', async () => {
|
||||||
|
const { wrapper, urls } = mountUrlListInput({ tcp: 11010 }, 'udp://0.0.0.0:22000')
|
||||||
|
|
||||||
|
await wrapper.find('.cursor-pointer').trigger('click')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(urls.value).toEqual(['udp://0.0.0.0:22000'])
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -32,6 +32,10 @@ path = "src/lib.rs"
|
|||||||
name = "tx_throughput"
|
name = "tx_throughput"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "packet_bytes_extraction"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
git-version = "0.3.9"
|
git-version = "0.3.9"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,47 @@
|
|||||||
# TX Throughput Benchmark
|
# Benchmarks
|
||||||
|
|
||||||
|
Criterion benchmarks for EasyTier hot paths.
|
||||||
|
|
||||||
|
| Bench | What it measures |
|
||||||
|
| --------------------------- | -------------------------------------------------------------------------------- |
|
||||||
|
| `tx_throughput` | End-to-end TX injection path through `peer_manager::send_msg_by_ip` |
|
||||||
|
| `packet_bytes_extraction` | `ZCPacket::payload_bytes` / `tunnel_payload_bytes` extraction (advance hot path) |
|
||||||
|
|
||||||
|
## Packet Bytes Extraction
|
||||||
|
|
||||||
|
Criterion benchmark for `ZCPacket` bytes extraction — the methods touched by the
|
||||||
|
`advance`-based slicing refactor. Measures `payload_bytes` and
|
||||||
|
`tunnel_payload_bytes` at two payload sizes (1280, 4096). Setup
|
||||||
|
(`ZCPacket::new_with_payload`) runs in the benchmark harness's preparation
|
||||||
|
phase and is excluded from the timed region, so the numbers reflect only the
|
||||||
|
extraction call.
|
||||||
|
|
||||||
|
### Quick start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo bench --bench packet_bytes_extraction
|
||||||
|
```
|
||||||
|
|
||||||
|
Smoke run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PACKET_BYTES_MEASUREMENT_SECS=2 \
|
||||||
|
PACKET_BYTES_WARMUP_SECS=1 \
|
||||||
|
PACKET_BYTES_SAMPLE_SIZE=10 \
|
||||||
|
cargo bench --bench packet_bytes_extraction -- --quiet
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment variables
|
||||||
|
|
||||||
|
| Variable | Default | Notes |
|
||||||
|
| ------------------------------- | ------- | ---------------------------- |
|
||||||
|
| `PACKET_BYTES_MEASUREMENT_SECS` | `10` | Criterion `measurement_time` |
|
||||||
|
| `PACKET_BYTES_WARMUP_SECS` | `3` | Criterion `warm_up_time` |
|
||||||
|
| `PACKET_BYTES_SAMPLE_SIZE` | `10` | Criterion `sample_size` (min 10) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## TX Throughput Benchmark
|
||||||
|
|
||||||
Criterion benchmark for EasyTier's TX injection path (`peer_manager::send_msg_by_ip`).
|
Criterion benchmark for EasyTier's TX injection path (`peer_manager::send_msg_by_ip`).
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
use std::hint::black_box;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use criterion::{BatchSize, Criterion, Throughput, criterion_group, criterion_main};
|
||||||
|
|
||||||
|
use easytier::tunnel::packet_def::ZCPacket;
|
||||||
|
|
||||||
|
const PAYLOAD_SIZES: &[usize] = &[1280, 4096];
|
||||||
|
|
||||||
|
fn env_parse<T: std::str::FromStr>(key: &str, default: T) -> T {
|
||||||
|
std::env::var(key)
|
||||||
|
.ok()
|
||||||
|
.and_then(|v| v.parse().ok())
|
||||||
|
.unwrap_or(default)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bench_payload_bytes(c: &mut Criterion) {
|
||||||
|
let mut group = c.benchmark_group("payload_bytes");
|
||||||
|
for &size in PAYLOAD_SIZES {
|
||||||
|
let data = vec![0u8; size];
|
||||||
|
group.throughput(Throughput::Bytes(size as u64));
|
||||||
|
group.bench_with_input(format!("{size}"), &data, |b, data| {
|
||||||
|
b.iter_batched(
|
||||||
|
|| ZCPacket::new_with_payload(black_box(data)),
|
||||||
|
|p| black_box(p).payload_bytes(),
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bench_tunnel_payload_bytes(c: &mut Criterion) {
|
||||||
|
let mut group = c.benchmark_group("tunnel_payload_bytes");
|
||||||
|
for &size in PAYLOAD_SIZES {
|
||||||
|
let data = vec![0u8; size];
|
||||||
|
group.throughput(Throughput::Bytes(size as u64));
|
||||||
|
group.bench_with_input(format!("{size}"), &data, |b, data| {
|
||||||
|
b.iter_batched(
|
||||||
|
|| ZCPacket::new_with_payload(black_box(data)),
|
||||||
|
|p| black_box(p).tunnel_payload_bytes(),
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn criterion_config() -> Criterion {
|
||||||
|
let measurement_secs = env_parse("PACKET_BYTES_MEASUREMENT_SECS", 10u64);
|
||||||
|
let warmup_secs = env_parse("PACKET_BYTES_WARMUP_SECS", 3u64);
|
||||||
|
let sample_size = env_parse("PACKET_BYTES_SAMPLE_SIZE", 10usize).max(10);
|
||||||
|
|
||||||
|
Criterion::default()
|
||||||
|
.measurement_time(Duration::from_secs(measurement_secs))
|
||||||
|
.warm_up_time(Duration::from_secs(warmup_secs))
|
||||||
|
.sample_size(sample_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group! {
|
||||||
|
name = benches;
|
||||||
|
config = criterion_config();
|
||||||
|
targets = bench_payload_bytes, bench_tunnel_payload_bytes
|
||||||
|
}
|
||||||
|
criterion_main!(benches);
|
||||||
@@ -24,7 +24,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use byteorder::WriteBytesExt as _;
|
use byteorder::WriteBytesExt as _;
|
||||||
use bytes::{BufMut, BytesMut};
|
use bytes::{Buf, BufMut, BytesMut};
|
||||||
use cidr::{Ipv4Inet, Ipv6Inet};
|
use cidr::{Ipv4Inet, Ipv6Inet};
|
||||||
use futures::{SinkExt, Stream, StreamExt, lock::BiLock, ready};
|
use futures::{SinkExt, Stream, StreamExt, lock::BiLock, ready};
|
||||||
use pin_project_lite::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
@@ -180,12 +180,13 @@ impl ZCPacketToBytes for TunZCPacketToBytes {
|
|||||||
assert!(payload_offset >= 4);
|
assert!(payload_offset >= 4);
|
||||||
|
|
||||||
let ret = if self.has_packet_info {
|
let ret = if self.has_packet_info {
|
||||||
let mut inner = inner.split_off(payload_offset - 4);
|
inner.advance(payload_offset - 4);
|
||||||
let proto = infer_proto(&inner[4..]);
|
let proto = infer_proto(&inner[4..]);
|
||||||
self.fill_packet_info(&mut inner[0..4], proto)?;
|
self.fill_packet_info(&mut inner[0..4], proto)?;
|
||||||
inner
|
inner
|
||||||
} else {
|
} else {
|
||||||
inner.split_off(payload_offset)
|
inner.advance(payload_offset);
|
||||||
|
inner
|
||||||
};
|
};
|
||||||
|
|
||||||
tracing::debug!(?ret, ?payload_offset, "convert zc packet to tun packet");
|
tracing::debug!(?ret, ?payload_offset, "convert zc packet to tun packet");
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use bytes::Buf;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use zerocopy::AsBytes;
|
use zerocopy::AsBytes;
|
||||||
@@ -486,7 +487,7 @@ impl ZCPacket {
|
|||||||
let total_len = payload_off + payload.len();
|
let total_len = payload_off + payload.len();
|
||||||
ret.inner.reserve(total_len);
|
ret.inner.reserve(total_len);
|
||||||
unsafe { ret.inner.set_len(total_len) };
|
unsafe { ret.inner.set_len(total_len) };
|
||||||
ret.mut_payload()[..payload.len()].copy_from_slice(payload);
|
ret.mut_payload().copy_from_slice(payload);
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,7 +588,8 @@ impl ZCPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn payload_bytes(mut self) -> BytesMut {
|
pub fn payload_bytes(mut self) -> BytesMut {
|
||||||
self.inner.split_off(self.payload_offset())
|
self.inner.advance(self.payload_offset());
|
||||||
|
self.inner
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn peer_manager_header(&self) -> Option<&PeerManagerHeader> {
|
pub fn peer_manager_header(&self) -> Option<&PeerManagerHeader> {
|
||||||
@@ -652,11 +654,12 @@ impl ZCPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn tunnel_payload_bytes(mut self) -> BytesMut {
|
pub fn tunnel_payload_bytes(mut self) -> BytesMut {
|
||||||
self.inner.split_off(
|
self.inner.advance(
|
||||||
self.packet_type
|
self.packet_type
|
||||||
.get_packet_offsets()
|
.get_packet_offsets()
|
||||||
.peer_manager_header_offset,
|
.peer_manager_header_offset,
|
||||||
)
|
);
|
||||||
|
self.inner
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert_type(mut self, target_packet_type: ZCPacketType) -> Self {
|
pub fn convert_type(mut self, target_packet_type: ZCPacketType) -> Self {
|
||||||
@@ -702,7 +705,8 @@ impl ZCPacket {
|
|||||||
return Self::new_from_buf(buf, target_packet_type);
|
return Self::new_from_buf(buf, target_packet_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::new_from_buf(self.inner.split_off(new_offset), target_packet_type)
|
self.inner.advance(new_offset);
|
||||||
|
Self::new_from_buf(self.inner, target_packet_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_bytes(self) -> Bytes {
|
pub fn into_bytes(self) -> Bytes {
|
||||||
@@ -748,8 +752,10 @@ impl ZCPacket {
|
|||||||
let foreign_hdr_len = hdr.get_header_len();
|
let foreign_hdr_len = hdr.get_header_len();
|
||||||
|
|
||||||
Self::new_from_buf(
|
Self::new_from_buf(
|
||||||
self.inner
|
{
|
||||||
.split_off(foreign_hdr_len + self.payload_offset()),
|
self.inner.advance(foreign_hdr_len + self.payload_offset());
|
||||||
|
self.inner
|
||||||
|
},
|
||||||
ZCPacketType::DummyTunnel,
|
ZCPacketType::DummyTunnel,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user