在进程/线程并发执行的过程中,进程/线程之间存在协作的关系,例如有互斥、同步的关系。
为了实现进程/线程间正确的协作,操作系统必须提供实现进程协作的措施和方法,主要的方法:
这两个都可以方便地实现进程/线程互斥,而信号量比锁的功能更强一些,它还可以方便地实现进程/线程同步。
Test-and-Set 和 Compare-and-Swap (CAS) 都是并发编程中的重要原子操作指令,通常用于同步和处理多线程或多进程环境中的竞争条件。它们的作用是确保某些操作在执行时不会被中断,以保持数据一致性和避免并发冲突。我们来详细了解这两个命令。
它的操作过程如下:
操作是原子的,即不会被中断或干扰,保证在并发环境下,多个线程或进程不能同时修改同一位置。
用途:
它的操作步骤如下:
CAS 也具有原子性,这意味着它在操作期间不会被中断。CAS 是实现无锁编程和避免线程同步问题的常用工具,尤其在需要高性能的并发程序中。
用途:
Test-and-Set
指令,XCHG
被用作实现类似功能的原子操作。CMPXCHG
指令,ARM 等其他架构也有类似的原子操作指令(如 LDREX 和 STREX)。std::atomic
是对 Test-and-Set
和 Compare-and-Swap
等底层原子操作的高级封装,使得 C++ 开发者可以在多线程环境中更方便、更安全地使用这些原子操作。
原子操作(std::atomic
)有如下特点:
std::atomic
只能用于一些简单的类型(如整数、指针和布尔类型),对于复杂的数据结构,仍然需要其他同步机制(如锁)。std::memory_order
对于一个线程里的同一个代码块内的无依赖关系的指令,处理器在运行时会重排序乱序执行。这对原子指令也是一样的。但实际生产中,多个原子操作之间,可能会有依赖关系,比如一个线程读取另一个线程写入的数据。为了确保这些依赖关系的正确性,需要使用内存顺序来控制处理器对指令的重排序。
std::atomic
的操作支持指定内存顺序,这控制了编译器和硬件对操作的优化和重排序。常见的内存顺序选项有:
std::memory_order_relaxed
:没有限制,运行时会乱序。std::memory_order_acquire
和 std::memory_order_release
:分别用于“获取”和“释放”同步,确保操作顺序。std::memory_order_seq_cst
:默认的内存顺序,保证在多线程程序中,所有原子操作按顺序执行,不允许重排序。但性能可能会略受影响。store
在原子操作中,store
用来将一个值存储到原子变量中。与普通赋值不同,store
会确保在多线程环境下,赋值操作是原子性的(即不会被打断)。
1 | std::atomic<int> counter(0); |
store
会保证在指定的内存顺序下将值存储到原子变量中,因此它是线程安全的。load
load
用于从原子变量中读取值。它会确保在多线程环境中,读取操作是线程安全的,并且可以指定内存顺序。
1 | int value = counter.load(std::memory_order_acquire); // 从原子变量读取 |
load
会确保读取的是正确同步后的值,避免在多线程场景下出现读取错误。compare_exchange_strong
compare_exchange_strong
(以及 compare_exchange_weak
)广泛应用于实现锁和无锁算法。它会尝试将原子变量的值与一个预期值进行比较,如果相同,则将其更新为新值。如果比较失败,原子变量的值不会更改,并且返回 false
。1 | std::atomic<int> value(0); |
exchange
exchange
是一种常见的原子操作,类似于 store
,但它会返回原子变量的先前值。
1 | int oldValue = counter.exchange(5); // 返回更新前的值,并将 counter 设置为 5 |
exchange
在多线程环境下是安全的,并且可以返回修改前的值,适用于某些需要获取旧值的场景。多线程同步的一种忙等待锁,线程反复检查锁变量是否可用。
1 | std::atomic_flag lock = ATOMIC_FLAG_INIT; |
把自己阻塞起来(内核态和用户态之间的切换进入阻塞状态,可能上下文切换),等待重新调度请求。
适用于大段的修改和同步。
eventfd
事件通知eventfd
是 Linux 提供的一种用于线程安全的事件通知机制,类似于文件描述符,可以通过读写操作来实现跨线程或跨进程的同步。通过 eventfd
,线程或进程可以通过某些事件(例如计数器增减、通知等)来触发其他线程或进程的动作。
eventfd
可以通过两种方式工作:
eventfd_write
增加计数器,其他线程或进程可以通过 eventfd_read
来读取这些计数器的值。eventfd
进行事件通知,线程或进程通过 eventfd_read
来等待并响应这些事件。eventfd_read
的功能 : eventfd_read
是一个用于从 eventfd
文件描述符读取事件的系统调用。它会从 eventfd
中读取一个 64 位无符号整数,并返回这个值。这个值通常表示某个事件的状态或计数器的值。
https://www.cswiki.top/pages/f398f1/#blocking-i-o
原文链接:https://blog.csdn.net/qq_15437629/article/details/79116590
LLVM Machine Code Analyzer 是一种性能分析工具,它使用llvm中可用的信息(如调度模型)静态测量特定CPU中机器代码的性能。
性能是根据吞吐量和处理器资源消耗来衡量的。该工具目前适用于在后端中使用LLVM调度模型的处理器。
该工具的主要目标不仅是预测代码在目标上运行时的性能,还帮助诊断潜在的性能问题。
给定汇编代码,llvm-mca可以估计每个周期的指令数(IPC)以及硬件资源压力。分析和报告风格的灵感来自英特尔的IACA工具。
https://github.com/llvm/llvm-project/tree/main/llvm/tools/llvm-mca
https://llvm.org/docs/CommandGuide/llvm-mca.html
1 | -mtriple=<target triple> |
1 | # 查看支持的arch |
1 | -output-asm-variant=<variant id> |
1 | -dispatch=<width> |
1 | -resource-pressure |
1 | Iterations: 300 |
1 | Instruction Info: |
显示了指令里队列每条指令的延迟和吞吐量的倒数。
RThroughput是指令吞吐量的倒数。在不考虑循环依赖的情况下,吞吐量是单周期能执行的同类型指令的最大数量。
1 | Resources: |
每次循环或者每条指令执行,消耗的资源周期数。从而找到高资源占用的部分。
可打印流水线情况
1 | Timeline view: |
影响因素包括:
1 | Cycles with backend pressure increase [ 91.52% ] |
llvm/lib/Target/X86/X86SchedSandyBridge.td
llvm/lib/Target/AArch64/AArch64SchedTSV110.td
执行时使用的平均或最大buffer entries (i.e., scheduler queue entries)
AMD Jaguar
JALU01 - A scheduler for ALU instructions.
JFPU01 - A scheduler floating point operations.
JLSAGU - A scheduler for address generation.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
5. Retire Control Unit
1. 在一个周期里有多少指令retired的占比(好吧,感觉有语病)
6. A re-order buffer (ROB) 的使用情况
7. Register File statistics
1. physical register file (PRF)
2. floating-point registers (JFpuPRF)
3. integer registers (JIntegerPRF)
## Instruction Flow
llvm-mca 假设指令在模拟开始之前已经全部解码并放入队列中。因此,指令提取和解码阶段没有被计算。未考虑前端的性能瓶颈。此外,llvm-mca 不模拟分支预测。
### Instruction Dispatch
处理器的默认 dispatch width值等于LLVM’s scheduling model里的IssueWidth值。
An instruction can be dispatched if:
* The size of the **dispatch group** is smaller than processor’s dispatch width.
* There are enough entries in the **reorder buffer**.
* There are enough **physical registers** to do register renaming.
* The schedulers are **not full**.
reorder buffer负责跟踪命令,使之按照程序顺序retired结束。其默认值为 MicroOpBufferSize 。
各种Buffered resources 被视作scheduler resources.
### Instruction Issue
每个处理器调度器实现一个指令缓冲区。指令必须在调度程序的缓冲区中等待,直到输入寄存器操作数可用。只有在那个时候,指令才符合执行的条件,并且可能会被发出(可能是乱序的)以供执行。 llvm-mca 在调度模型的帮助下计算指令延迟。
llvm-mca 的调度器旨在模拟多处理器调度器。调度器负责跟踪数据依赖关系,并动态选择指令消耗哪些处理器资源。它将处理器资源单元和资源组的管理委托给资源管理器。资源管理器负责选择指令消耗的资源单元。例如,如果一条指令消耗了一个资源组的1cy,则资源管理器从该组中选择一个可用单元;默认情况下,资源管理器使用循环选择器来保证资源使用在组的所有单元之间均匀分配。
llvm-mca’s scheduler internally groups instructions into three sets:
* WaitSet: a set of instructions whose operands are not ready.
* ReadySet: a set of instructions ready to execute.
* IssuedSet: a set of instructions executing.
### Write-Back and Retire Stage
retire control unit
1. When instructions are executed,the flags the instruction as “ready to retire.”
2. Instructions are retired in program order
3. free the physical registers
### Load/Store Unit and Memory Consistency Model
load/store unit (LSUnit)用来模拟乱序memory操作
The rules are:
1. A younger load is allowed to pass an older load only if there are no intervening stores or barriers between the two loads.
2. A younger load is allowed to pass an older store provided that the load does not alias with the store.
3. A younger store is not allowed to pass an older store.不能交换顺序的意思
4. A younger store is not allowed to pass an older load.
假设 loads do not alias (-noalias=true) store operations.Under this assumption, younger loads are always allowed to pass older stores. ???
LSUnit不打算跑alias analysis来预测何时load与store不相互alias???
in the case of write-combining memory, rule 3 could be relaxed to allow reordering of non-aliasing store operations.???
LSUnit不管的其余三点:
1. The LSUnit does not know when store-to-load forwarding may occur.
2. The LSUnit does not know anything about cache hierarchy and memory types.
3. The LSUnit does not know how to identify serializing operations and memory fences.
4. The LSUnit does not attempt to predict if a load or store hits or misses the L1 cache(不考虑cache命中,默认是命中L1,产生the load-to-use latency的最乐观开销)
llvm-mca 不知道序列化操作或内存屏障之类的指令。 LSUnit 保守地假设同时具有“MayLoad”和未建模副作用的指令的行为类似于“软”load-barrier。这意味着,它在不强制刷新load队列的情况下序列化加载。类似地,“MayStore”和具有未建模副作用的指令被视为store障碍。完整的memory-barrier是具有未建模副作用的“MayLoad”和“MayStore”指令。LLVM的实现是不准确的,但这是我们目前使用 LLVM 中可用的当前信息所能做的最好的事情。
load/store barrier会占用在load/store 队列里占用一项。
当load/store barrier是其队列里oldest项时,其会被执行

### In-order Issue and Execute
有序处理器被建模为单个 InOrderIssueStage 阶段。它绕过 Dispatch、Scheduler 和 Load/Store 单元。一旦它们的操作数寄存器可用并且满足资源要求,就会发出指令。根据LLVM的调度模型中IssueWidth参数的值,可以在一个周期内发出多条指令。一旦发出,指令就会被移到 IssuedInst 集,直到它准备好retire。 llvm-mca 确保按顺序提交写入。但是,如果 RetireOOO 属性for at least one of its writes为真,则允许指令提交写入并无序retire???
## Custom Behaviour 自定义行为
某些指令在该模型中并不能被准确的模拟。为了几条指令而修改模型不是个好的选择,一般通过**CustomBehaviour**类对某些指令进行特殊建模:自定义数据依赖,以及规避、单独处理特殊情况。
为此,llvm-mca设置了一个通用的以及多个特殊的**CustomBehaviour**类。下面两种情况下会使用通用类:
1. 开启了`-disable-cb`选项
2. 不存在针对某目标的特殊类(通用类也做不了什么,我什么也做不到😥)
但是注意目前只有in-order流水线实现了**CustomBehaviour**类,out-order流水线将来也会支持。
该类主要通过`checkCustomHazard()`函数来实现,通过当前指令和真正流水线中执行的指令,来判断当前指令需要等待几个周期才能发射。
如果想对没有实现的目标添加**CustomBehaviour**类,可以参考已有的实现,比如在`/llvm/lib/Target/AMDGPU/MCA/`目录下。
## Custom Views 自定义视图
关于自定义的视图的添加路径,如果**没有输出**从未在MC layer classes (MCSubtargetInfo, MCInstrInfo, etc.)里出现过的**新后端值**,请把实现加入`/tools/llvm-mca/View/`。相反,请加入`/lib/Target/<TargetName>/MCA/`目录。
关于Custom Views所需内容,需要写特殊的**CustomBehaviour**类来覆写`CustomBehaviour::getViews()`函数,根据位置的不同还有三种实现`getStartViews(), getPostInstrInfoViews(),getEndViews()`。
## 影响准确性的因素
调度模型不仅用于计算指令延迟和吞吐量,还用于了解可用的处理器资源以及如何模拟它们。
llvm mca进行分析的质量不可避免地受到**llvm中调度模型质量**的影响。
## 功能(能估计的值
1. IPC
2. 硬件资源压力resource-pressure
3. 一些额外Info?
1.
register-file-stats
-dispatch-stats
-scheduler-stats
-retire-stats
-instruction-info
instruction-tables
1
2
3
4
5
6
7
4. 吞吐量瓶颈?
### 支持对特定代码块的分析
1. 汇编代码,支持命名和嵌套
add %eax, %eax
1 |
|
int foo(int a, int b) {
__asm volatile(“# LLVM-MCA-BEGIN foo”);
a += 42;
__asm volatile(“# LLVM-MCA-END”);
a *= b;
return a;
}
但是,这会干扰循环矢量化等优化,并可能对生成的代码产生影响。具体影响请对比汇编代码。
Google学术搜llvm-mca,一堆论文。但是不急着看,因为没有预备知识,没有问题的去看论文。效率和收获很低的,而且会看不懂。
mc-ruler是整合了llvm-mca的cmake,可以打印指定部分的代码分析信息。如果之后要测试可能用得上。
如何和大神交流呢+提问的艺术
-O3
的优化,而且其顺序对结果也有影响。详细解释各阶段:
后端的实现分散在LLVM源代码树的不同目录中。代码生成背后的主要程序库位于lib目录和它的子文件夹CodeGen、MC、TableGen、和Target中, 具体参考文档
Tablegen位置在类似 llvm/lib/Target/X86/X86.td
的地方
程序优化选项 -O3 是通过启用 LLVM Pass Manager 并按照顺序执行包含多个具体优化 Pass 的过程实现的。包括:
这些 Pass 的执行范围涵盖 LLVM IR 与 LLVM 后端。
TableGen的输入文件使用扩展名“.td
”(TableGen的缩写),它们可以描述如下内容:
实现一个简单的LLVM IR后端,将LLVM IR转换为x86汇编代码,能line by line的输出。
参考LLVM官方文档中的“Writing an LLVM Backend”以及“TableGen Backends”
暂无
暂无
https://getting-started-with-llvm-core-libraries-zh-cn.readthedocs.io/zh_CN/latest/ch06.html#id2
Pass类是实现优化的主要资源。然而,我们从不直接使用它,而是通过清楚的子类使用它。当实现一个Pass时,你应该选择适合你的Pass的最佳粒度,适合此粒度的最佳子类,例如基于函数、模块、循环、强联通区域,等等。常见的这些子类如下:
ModulePass
:这是最通用的Pass;它一次分析整个模块,函数的次序不确定。它不限定使用者的行为,允许删除函数和其它修改。为了使用它,你需要写一个类继承ModulePass,并重载runOnModule()方法。FunctionPass
:这个子类允许一次处理一个函数,处理函数的次序不确定。这是应用最多的Pass类型。它禁止修改外部函数、删除函数、删除全局变量。为了使用它,需要写一个它的子类,重载runOnFunction()方法。BasicBlockPass
:这个类的粒度是基本块。FunctionPass类禁止的修改在这里也是禁止的。它还禁止修改或者删除外部基本块。使用者需要写一个类继承BasicBlockPass,并重载它的runOnBasicBlock()方法。被重载的入口函数runOnModule()、runOnFunction()、runOnBasicBlock()返回布尔值false,如果被分析的单元(模块、函数和基本块)保持不变,否则返回布尔值true。
1 | char PIMProf::AnnotationInjection::ID = 0; |
最简单框架hello.cpp如下,注意Important
一定需要:
1 |
|
参考官方文档。
An example of a project layout is provided below.
1 | <project dir>/ |
Contents of <project dir>/CMakeLists.txt
:
1 | find_package(LLVM REQUIRED CONFIG) |
Contents of <project dir>/<pass name>/CMakeLists.txt
:
1 | add_library(LLVMPassname MODULE Pass.cpp) |
运行cmake编译。产生LLVMPassname.so
文件
1 | mkdir build && cd build |
请阅读知乎的文章
1 | clang -c -emit-llvm main.c -o main.bc # 随意写一个C代码并编译到bc格式 |
把源代码编译成IR代码,然后用opt运行Pass实在麻烦且无趣。
1 | clang -Xclang -load -Xclang path/to/LLVMHello.so main.c -o main |
1 | void InjectSimMagic2(Module &M, Instruction *insertPt, uint64_t arg0, uint64_t arg1, uint64_t arg2) |
这段代码使用内联汇编嵌入到 LLVM IR 中,指令如下:
1 | mov $0, %rax |
其中:
由于直接打印的是llvm IR的表示,想要打印特定架构比如x86的汇编代码,其实需要进行llvm后端的转换。(取巧,可执行文件反汇编,然后根据插入的汇编桩划分)
1 |
暂无
暂无
复现PIMProf论文时,用到了使用 llvm pass来插入特殊汇编
伪指令 | 描述 |
---|---|
.file | 指定由哪个源文件生成的汇编代码。 |
.data | 表示数据段(section)的开始地址 |
.text | 指定下面的指令属于代码段。 |
.string | 表示数据段中的字符串常量。 |
.globl main | 指明标签main是一个可以在其它模块的代码中被访问的全局符号 。 |
.align | 数据对齐指令 |
.section | 段标记 |
.type | 设置一个符号的属性值 |
.type name , description
%function
表示该符号用来表示一个函数名%object
表示该符号用来表示一个数据对象至于其它的指示你可以忽略。
从最简单的C文件入手
1 | int main(){ |
运行gcc -S -O3 main.c -o main.s
,得到main.s
文件
1 | .file "simple.cpp" |
.section .rodata.str1.1,"aMS",@progbits,1
rodata.str1.1
是一个标号(label), 意思是只读数据段的字符串常量aMS
是一个属性值:@progbits
: 表示该段的类型是程序数据段(PROGBITS),这种类型的段包含程序的代码和数据。1
: 表示该段的对齐方式是2^1 = 2个字节(按字节对齐)。如果不写这个数字,默认对齐到当前机器的字长。.section .text.startup,"ax",@progbits
其中ax
表示该段是可分配的(allocatable)和可执行的(executable)。.section .note.GNU-stack
“指令用于告诉链接器是否允许在堆栈上执行代码。.section .note.gnu.property
“指令用于指定一些属性,这里是一个GNU特性标记。.text.startup” section
,其首地址为“.globl main
”。1 | .section .text.startup,"ax",@progbits |
_GLOBAL__sub_I_xxx
”的section中。ios_base::Init()
“,并注册了一个在程序退出时调用的析构函数 “__cxa_atexit
“。.init_array
“ section中,定义了一个”_GLOBAL__sub_I_main”的地址,这是在程序启动时需要调用的所有C++全局和静态对象的初始化函数列表,编译器链接这个列表并在程序启动时依次调用这些初始化函数。其中四条指令都定义了一些符号或变量,并分配了一些内存空间,这些在程序里的意义如下:
.quad _GLOBAL__sub_I_main
“:在程序启动时,将调用所有全局静态对象的构造函数。这些构造函数被放在一个名为”_GLOBAL__sub_I_xxx”的section中,而每个section都是由一个指向该section所有对象的地址列表所引用。这里的”.quad _GLOBAL__sub_I_main”是为了将”_GLOBAL__sub_I_main”函数的地址添加到该列表中。
.local _ZStL8__ioinit
“:这条指令定义了一个本地符号”_ZStL8__ioinit”,它表示C++标准输入输出的初始化过程。由于该符号是一个本地符号,所以只能在编辑该文件的当前单元中使用该符号。
.comm _ZStL8__ioinit,1,1
“:这条指令定义了一个名为”_ZStL8__ioinit”的未初始化的弱符号,并为该符号分配了1个大小的字节空间。这个弱符号定义了一个C++标准输入输出部分的全局状态对象。在全用动态库时,不同的动态库可能有自己的IO状态,所以为了确保C++输入输出的状态正确,需要为其指定一个单独的段来存储这些状态数据。在这里,”.comm _ZStL8__ioinit,1,1”将会为”_ZStL8__ioinit”符号分配一个字节大小的空间。
.hidden __dso_handle
“:这条指令定义了一个隐藏的符号 “__dso_handle”。这个符号是一个链接器生成的隐式变量,其定义了一个指向被当前动态库使用的全局数据对象的一个指针。该符号在被链接进来的库中是隐藏的,不会被其他库或者main函数本身调用,但是在main返回后,可以用来检查库是否已经被卸载。
这段代码是一些特殊的指令和数据,主要是用于向可执行文件添加一些元数据(metadata)。这些元数据可能包含各种信息,如调试信息、特定平台的指令集支持等等。
具体来说:
.long 1f - 0f
“建立了一个长整型数值,表示”1:”标签相对于当前指令地址(即0f)的偏移量。偏移量可以用来计算标签对应的指令地址,从而可用于跳转或计算指针偏移量。4f - 1f
“,即”4:”标签相对于”1:”标签的偏移量;.long 0xc0000002
“表示这是一个特殊的属性标记,标识这个文件可以在Linux平台上执行。它是用来告诉操作系统这个程序是用特定指令集编译的。.long 0x3
“表示另一个属性标记,表示这个文件可以加载到任意地址。总之,这些元数据可能对程序运行起到关键作用,但在大多数情况下可能都没有明显的作用,因此看起来没有用。
执行gcc -S -g testBigExe.cpp -o testDebug.s
,对比之前的汇编文件,由72行变成9760行。
1 | .LBE32: |
.loc 3 342 2
表示当前指令对应的源代码文件ID为3,在第342行,第2列(其中第1列是行号,第2列是第几个字符),同时is_stmt
为1表示这条指令是语句的起始位置。.loc 1 5 11
表示当前指令对应的源代码文件ID为1,在第5行,第11列,同时is_stmt
为0表示这条指令不是语句的起始位置。view .LVU4
表示当前指令所处的作用域(scope)是.LVU4。作用域是指该指令所在的函数、代码块等一段范围内的所有变量和对象的可见性。在这个例子中,.LVU4 是一个局部变量作用域,因为它是位于一个C++标准库头文件中的一个函数的起始位置。新增的这些 section 存储了 DWARF 调试信息。DWARF(Debugging With Attributed Record Formats)是一种调试信息的标准格式,包括代码中的变量、类型、函数、源文件的映射关系,以及代码的编译相关信息等等。
具体来说,这些 section 存储的内容如下:
.debug_info
:包含程序的调试信息,包括编译单元、类型信息、函数和变量信息等。.debug_abbrev
:包含了 .debug_info 中使用到的所有缩写名称及其对应的含义,用于压缩格式和提高效率。.debug_loc
:存储每个程序变量或表达式的地址范围及其地址寄存器、表达式规则等信息。在调试时用来确定变量或表达式的值和范围。.debug_aranges
:存储简化版本的地址范围描述,允许调试器加速地定位代码和数据的位置。.debug_ranges
:存储每个编译单元(CU)的地址范围,每个范围都是一个有限开区间。.debug_line
:存储源代码行号信息,包括每行的文件、行号、是否为语句起始位置等信息。.debug_str
:包含了所有字符串,如文件名、函数名等,由于每个调试信息的数据都是字符串,因此这是所有调试信息的基础。需要注意的是,这些 section 中的信息是根据编译器的配置和选项生成的,因此不同编译器可能会生成略有不同的调试信息。
暂无
Web Server: Nginx V.S. Apache2
常见的web服务器有Apache、nginx、IIS
虽然 Ryujinx 模拟器项目本身是开源免费且合法的,但它默认情况下并不能直接运行市面上发行的各种商业游戏,因为它并不包含 Switch 系统固件,也没有游戏 ROM。
而按照国外的法规,如果你用户购买了主机和游戏,将其内容 DUMP (提取) 出来自己使用是合法的。
所以,无论是 Ryujinx 还是 Yuzu 等模拟器,想要开玩都需要先完成
prod.keys
密钥文件以及 Switch 的系统固件 (Firmware).NSP
或者 .XCI
格式率先支持了ARM和苹果 M 系列芯片
prod.keys
文件放进到 Ryujinx 目录中的 system
文件夹里,重启模拟器不是
而是
yuzu,奶刃2好像都是用这个
将你原来的User文件夹和ROM文件夹拖到新版模拟器文件夹的根目录即可。
将 prod.keys
文件放在Yuzu\user\keys
Yuzu\user\keys
Yuzu\user\nand\system\Contents\registered
https://www.playdanji.com/yuzu
https://www.playdanji.com/yuzujinshouzhi
Early Access版本是需要花钱订阅才能下载的。
github的release直接下载zip后解压替换即可。
游戏右键,打开存档位置。
F11
key和中文的问题,建议用之前好的文件
https://zhuanlan.zhihu.com/p/406048136
30帧720P
https://switch520.com/23050.html
v模式,暗场景会过曝。
贴吧老哥的放入目录
1 | E:\gamePojie\NaiRen2\A3285 v2.0.2_yuzuEA2077\user\sdmc\atmosphere\contents |
但是没什么用。
ini配置原理是,如下图对应配置目录
放入如下修改的ini文件来修改画质
如下图成功
贴吧10楼:刚试了下,我把属性的mod选项关掉,然后把0100F3400332C000的画质mod删掉,效果一样有,所以效果应该只能在0100F3400332D001\画质mod\romfs\monolib\shader下的lib_nx.ini里改,其他的都没用
1 | red_sclX=2.0 |
2.0就是1440p,1.0就是原版720p。你们可以试试改其他的
60帧补丁实际效果远没有60帧而且一堆副作用,不用浪费时间了
按照B站设置,主要改了GLSL
游侠论坛的cemu模拟的效果就不错
龙神模拟器,会经常闪退,暂时不知道解决办法(Cache PPTC rebuild?)。yuzu没有闪退的问题
暂无
暂无
之前买了正版的switch,游戏也入了两三千。旷/荒野之息,奥德赛,奶刃2都通关了。可惜被妈妈没收了~
想研究一下,PC模拟,记录踩坑过程
BitTorrent (简称 BT) 协议是和点对点(point-to-point)的协议程序不同,它是用户群对用户群(peer-to-peer, 或简写为 P2P) 传输协议, 它被设计用来高效地分发文件 (尤其是对于大文件、多人同时下载时效率非常高)。该协议基于HTTP协议,属于TCP/IP应用层。
将文件划分成多块(默认256Kb一块),每块可以从网络中不同的用户的BT客户端处并行下载。
1 | BT 下载的文件都是别人上传给你的。 |
比特彗星,包括其他 BT 软件(迅雷除外,迅雷不是会员会限速,高速通道下载提高的速度一部分就是接触限速后获得的)都不会限制下载速度。
与迅雷不同,BT旨在“人人为我,我为人人”。用户和用户之间对等交换自己手中已有的资源。如果任何一方试图白嫖另外一方的资源,而自己不愿意上传自己的资源,那么那方就会被人视作吸血者而被踢出这个交换,下场是没有人会愿意和你交换数据,你的下载速度也就归零。
如果把上传速度限制为了10KB/s,10KB/s是BitComet上传最低限速,很大时候就这10KB会被包含DHT查询、向Tracker服务器注册,连接用户所产生的上传全部占满。在下载种子的时候,其他用户连上你是只能拿到1~2KB/s甚至一点都没有的。
现在的BT下载客户端都可以做到智能反吸血,所以基本想和交换数据的用户都把你当作Leecher(吸血鬼)Ban(封禁)处理了,故没有下载速度不足为奇。
一般来说,只要预留50KB/s的上传给其他网页浏览、聊天就可以了,在下载时应该尽量把上传留给那些和你交换资源的用户,这样才不会被他们视作你在吸血进而屏蔽你。
如果上传不足,就应该主动限制自己的下载速度,否则单位时间下载量远超过上传量反而会遭来更多的屏蔽,对下载速度提升更加不利。
.torrent
种子文件本质上是文本文件,包含Tracker信息和文件信息两部分。Tracker信息主要是BT下载中需要用到的Tracker服务器的地址和针对Tracker服务器的设置。
sudo XAUTHORITY=/home/qcjiang/.Xauthority qbittorrent
以qBit的docker为例,参考linuxsever的docker-compose如下:(qBit相对于Transmission有多线程IO的优势) 也可以使用其余docker镜像
1 | --- |
默认账号 admin
默认密码 adminadmin
然后通过webUI http://222.195.72.218:8080/
管理。
如果不想网络通过wireguard,而是本地可以如下设置
m站刷上传的时候,发现基本都是对方基本都是通过ipv6下载
uTP是一种基于UDP的协议,它可以根据网络拥塞情况自动调节传输速度,从而减少对其他网络应用的影响。
BT连接是一种基于TCP的协议,它可以保证数据的完整性和可靠性,但是可能会占用较多的网络带宽和资源。
在qBittorrent中,标志U K E P分别表示以下含义:
1 | U:表示你正在上传数据给对方,或者对方正在从你那里下载数据。 |
收集下载者信息的服务器,并将此信息提供给其他下载者,使下载者们相互连接起来,传输数据。
指一个下载任务中所有文件都被某下载者完整的下载,此时下载者成为一个种子。发布者本身发布的文件就是原始种子。
发布者提供下载任务的全部内容的行为;下载者下载完成后继续提供给他人下载的行为。
上傳資料量 / 下傳資料量的比率,是一種BT的良心度,沒實際作用.(一般为了良心,至少大于1)
BitComet的概念,相对于种子任务的上传能够控制。
长效种子就是你不开启任务做种,只要你启动了比特彗星,软件挂后台,当有其他用户也是用比特彗星下载你列表里的存在的文件时候就会被认为是长效种子 。
.DHT全称叫分布式哈希表(Distributed Hash Table),是一种分布式存储方法。在不需要服务器的情况下,每个客户端负责一个小范围的路由,并负责存储一小部分数据,从而实现整个DHT网络的寻址和存储。新版BitComet允许同行连接DHT网络和Tracker,也就是说在完全不连上Tracker服务器的情况下,也可以很好的下载,因为它可以在DHT网络中寻找下载同一文件的其他用户。
类似Tracker的根据种子特征码返回种子信息的网络。
在BitComet中,无须作任何设置即可自动连接并使用DHT网络,完全不需要用户干预。
Peer Exchange (PEX), 每个peer客户端的用户列表,可以互相交换通用。可以将其理解为“节点信息交换”。前面说到了 DHT 网络是没有中心服务器的,那么我们的客户端总不能满世界去喊:“我在下载这个文件,快来连我吧.”(很大声)。所以就通过各个 BT 客户端自带的节点去同步路由表实现 DHT 网络连接。
LSD(LPD)就是本地网络资源,内网下载,没什么几把用的东西,可能学校等私有网络好使
網絡業務提供商(Internet Service Provider,簡稱ISP),互聯網服務提供商,即向廣大用户綜合提供互聯網接入業務、信息業務、和增值業務的電信運營商。
1 | bittorrent.anti_leech_min_byte |
需要
文件下载后不能移动,不能删除,不能重命名(但可以在软件内改)。 一但BT 软件找不到文件,或删除了任务,就无法做种上传了。
可以在Bitcomet高级设置里设置时段限速
分享上传也需要频繁读取硬盘。
以Bitcomet为例,该软件就是通过磁盘缓存技术减小频繁随机读写对硬盘的损伤。
磁盘缓存就是利用物理内存作为缓冲,将下载下的数据先存放于内存中,然后定期的一次性写入硬盘,以减少对硬盘的写入操作,很大的程度上降低了磁盘碎片。
因为通常我们设置内存(磁盘缓存)为每任务XX兆,意味着,这个缓冲区可以存放数兆甚至几十兆的“块”,基本上可以杜绝碎片了。
现在BT软件都是自动设置缓存的,它是根据你物理内存的大小分配的。
路由器下载?