TCP、UDP与拥塞控制算法
前言
在 Windows11开启BBR2拥塞控制算法 中,将Windows的TCP拥塞控制算法从CUBIC换成了BBR2。TCP 拥塞控制到底在控制什么?这些算法有什么不同?
事实上,TCP一直在问自己:我应该同时在网络里“放多少数据”才不会把路堵死?一个名叫“拥塞窗口”(Congestion Window)的参数被设定,用于限制发送端能发送的最大数据量。大的cwnd会使得吞吐更高,但也更容易造成排队和丢包;更小的cwnd则会使得延迟更低,但是链路可能跑不满。
拥塞控制算法的本质,其实就是如何动态调整cwnd。如果看起来网络很空,那就增大一些cwnd,使得吞吐量提高;网络如果太“拥堵”,那就减小一点cwnd,使得延迟降低一点。
CUBIC:很平庸,也很稳
CUBIC像是“先加速、再减速、再加速”,目标是让 cwnd 在大窗口区间增长更快。
CUBIC 用一个三次函数来控制 cwnd 变化:
丢包后,cwnd 会降到某个值
然后增长会逐渐加速
接近上一次发生丢包时的窗口(Wmax)附近,增长会变慢(更谨慎)
超过 Wmax 后,再加速探测更高带宽
那么代价是什么呢?CUBIC是Los ...
自用音乐音量均衡脚本
前言
不同歌曲的音量大小不同,如果直接导入游戏做自定义电台的话很容易出现音乐时大时小的问题。因此,需要先将音量均衡。
傻瓜脚本
需要安装好ffmpeg且添加到PATH
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798import jsonimport reimport subprocessfrom pathlib import PathTARGET_I = -14TARGET_TP = -1.5TARGET_LRA = 12FFMPEG = "ffmpeg" # 或写 "ffmpeg.exe"def run(cmd): # ✅ 强制按 UTF-8 解码,避免 GBK 解码炸裂 p = subprocess.run ...
Windows11开启BBR2拥塞控制算法
前言
群里吹水时看到群友讨论相同配置的Windows网速和Linux网速哪个更快,得出结论是Linux获胜,毕竟Linux的TCP拥塞控制算法更好,采用的是BBR算法,而Windows一直是老古董CUBIC。
不过,在Windows11 22H2之后,也可以为TCP开启BBR2的拥塞控制算法了——尽管有一些小小的问题。
开启BBR2
首先查看自己电脑的默认控制算法:
123456789101112╭── PowerShell False 0ms [21:21]╰─ ~❯ Get-NetTCPSetting | Select SettingName, CongestionProviderSettingName CongestionProvider----------- ------------------AutomaticInternetCustom CUBICDatacenterCustom CUBICCompat NewRenoDatacenter CUBICIntern ...
最棒的VCD波形查看器:Surfer
前言
之前 折腾了半天GTKWave,还是调不好。高分辨率屏幕看没有优化缩放的波形,眼睛都要瞎了。
直到群友给我推荐了Surfer。居然还有在线Demo:Surfer Online
使用效果
可以看到,界面字体全部采用等宽字体,看信号也更加方便,不会IIlIlIIlIl分不清。
你甚至可以参考官方文档,用现成提供的指令集解码器,亦或是自己写,从而解析指令码,这样子就不用在内部引出ASCII:
这玩意甚至还有VSCode插件,虽然是WASM编译版。
真要挑它的缺点,可能就一个不支持模拟波形吧。还好做数字方向的看不到这些,哈哈。
从零开始学RISC:第十篇
让我访问!
对于L-Type和S-Type,我们只支持了lw/sw,它们的兄弟姐妹lb/lbu/lh/lhu/sb/sh还没支持。
我们需要准备一个模块专门用来处理这些非字节存取。在前面ID级传出的sl_type信号终于能派上用场了。
这里有个小技巧:定义存取类型时,用高位来分辨是读还是写,剩下的读写类型一定程度上可以合并。
存取控制模块 LoadStoreUnit.sv
考虑到数据的存取都在MEM级完成,我们需要将模块放置在MEM级。我们要传入来自EX级的待存数据,对其进行适当移位,好将数据存放到合适位置,并根据dram_we和sl_type来将1位写使能dram_we转换为4位的按位写使能dram_we_strbe。
对于读出的数据,还要判断是有符号还是无符号读取。
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848 ...
从零开始学RISC:第九篇
综合器说好,那才是真好
模块综合
写出的HDL代码需要送入综合器中,生成实际的门电路。综合器会在这个过程中推测你写的代码,并帮你进行一定的优化,最终生成文件。
这里选择开源的Yosys进行综合。官方没有发布适用于Windows的二进制可执行文件,但是Python的WASM库有,虽然是给WebAssembly环境准备的,需要电脑上有wasmtime才行。编写一个综合脚本:
1234567891011121314151617181920212223242526272829303132333435363738# 读取所有模块(不需要 -formal)read_slang ./user/src/include/defines.svhread_slang ./user/src/PC.svread_slang ./user/src/IROM.svread_slang ./user/src/PR_IF_ID.svread_slang ./user/src/Decoder.svread_slang ./user/src/imm_extender.svread_slang ./user/src/Regi ...
从零开始学RISC:第八篇
前递:快人一步
激烈的斗争
我们做完了吗?还没完。
在 之前 的测试里,程序没有任何数据冒险——在实际运行中这几乎是不可能的。我们简单修改一点:
1234567addi x1,x0,1addi x2,x0,2addi x3,x0,3addi x4,x0,4addi x1,x0,7addi x2,x0,21add x7,x1,x2 # 加入数据冒险
然后运行。发生了什么?我们期望看到x7输出的是x1和x2变化后相加的值,应当为28。但是计算结果输出了错误的3!
这就是最经典的“数据冒险”。我们看看发生了什么。
123456789 x1 x2 x7 IF ID EX MEM WB-------------------------------------------------------------------------------1 1 2 x x1=72 1 2 x x2=21 x1=73 1 2 x ...
从零开始学RISC:第七篇
访存 MEM级 设计
访存访存,顾名思义,就是要去访问内存。这个内存是“数据内存”,因为我们写的CPU采用的是哈佛架构,指令内存和数据内存分离。
数据内存 DRAM.sv
首先登场的是数据内存。非常简单,实例化一堆RDFF即可。为什么是RDFF?为了便于测验交作业,我们暂时把DRAM写成 “同步写、异步读” 的。
几乎所有厂商都不支持“同步写、异步读”的DRAM,综合器也无法将其综合为DRAM。对于Xilinx的IP核,其BRAM是同步读写的,有的甚至需要两拍。
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950`timescale 1ns / 1ps// DRAM 行为模型 - 用于仿真// 替代 Xilinx IP 核// [HACK] Vivado无法综合异步读的RAMmodule DRAM ( input logic clk, // 时钟 input logic [15:0] a, // 地址输入 (16位 ...
从零开始学RISC:第六篇
执行 EX级 设计
EX级以负责核心运算功能的ALU著称。可以说,它是CPU的灵魂。来一人一句ALU牛逼来。
算数逻辑单元 ALU.sv
ALU需要完成一系列R-Type、I-Type指令的运算。对于L-Type和S-Type,它们的处理逻辑其实差不多,都是相加——这样才能让ALU输出基地址和偏移量之和,算出目标地址。
同样地,使用字符串打印的方式来增强可读性,优化Debug体验,并更好的看到指令在流水线中的流动。同样使用DEBUG宏进行包装,在给综合器生成最终电路前取消宏定义即可。
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283`include "include/defines.svh"module ALU ( // 操作码 input logic [ 4:0] alu_op, / ...
从零开始学RISC:第五篇
译码 ID级 设计
提到译码级,那就得是译码器啊。纯组合逻辑,包你写到爽。
译码级还有什么?立即数移位也需要放在ID级,以减轻EX级的压力。
指令译码器 Decoder.sv
指令译码器为组合逻辑模块。我们需要哪些信号?
首先要把31位的指令分类:是RIBJSU的哪一种?之后,要根据分类,将寄存器地址拆分出来,准备送进同一级的寄存器堆来读取数据;要提取指令的funct3和funct7,以便在EX级告诉ALU“长的像的指令如何区分”;要根据指令类型,区分跳转类型和读取/写入类型,给后面的流水级进行处理;还要生成对应位的寄存器读信号,和分支指令判断信号,为后面处理数据冒险准备……要干的事情真不少。
一步一步全写上即可。推荐使用宏定义,大幅度提升可读性。
在模块内使用``ifdef DEBUG`配合上ASCII字符进行Debug是另一个小技巧。在综合时取消宏定义即可。
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061 ...



