C 64位段基上下文开关的性能影响

C 64位段基上下文开关的性能影响,c,linux,performance,x86-64,cpu-registers,C,Linux,Performance,X86 64,Cpu Registers,我被这本手册的措辞弄糊涂了。具体而言,它指出: 用于64位段基的上下文开关相当昂贵。可能 使用段选择器设置32位基址是一种更快的选择 通过使用modify_LDT(2)或使用set_thread_区域(2)设置LDT 内核2.5或更高版本中的系统调用。只有在以下情况下才需要arch_prctl() 您希望设置大于4GB的基。记忆在第一 通过使用mmap(2)和 映射32位标志 这是否意味着使用此系统调用的进程的上下文切换将收到性能损失,或者确切的含义是什么 在浏览了Linux内核的源代码之后,对

我被这本手册的措辞弄糊涂了。具体而言,它指出:

用于64位段基的上下文开关相当昂贵。可能 使用段选择器设置32位基址是一种更快的选择 通过使用modify_LDT(2)或使用set_thread_区域(2)设置LDT 内核2.5或更高版本中的系统调用。只有在以下情况下才需要arch_prctl() 您希望设置大于4GB的基。记忆在第一 通过使用mmap(2)和 映射32位标志

这是否意味着使用此系统调用的进程的上下文切换将收到性能损失,或者确切的含义是什么

在浏览了Linux内核的源代码之后,对于4个GiB地址的地址,似乎使用了一个特定于模型的寄存器

发件人:

case ARCH\u SET\u FS:
/*通过GDT处理小基地,因为这样可以更快地
开关*/
if(addr线程,cpu);
负载段(fs、fs\U TLS\U SEL);
}
任务->线程.fsindex=FS\u TLS\u SEL;
任务->线程.fs=0;
}否则{
任务->线程.fsindex=0;
任务->线程.fs=addr;
如果(doit){
/*将选择器设置为0以避免混淆
__切换到*/
载荷段(fs,0);
ret=wrmsrl_安全(MSR_FS_基地,地址);
}
}
放置cpu();
打破

如何使用GDT比写入寄存器更快?此外,我假设更新FS和GS的代价仅在进程之间切换时支付,这意味着在没有其他进程计划运行时,通过系统调用进入内核不会产生额外的成本?

哇,这是12月份提出的问题,没有人回答?其中一些你可能已经知道了,如果是的话,我道歉

这只是因为进行wrmsr的步骤很慢。在任务切换时,只加载段寄存器更容易、更快

在非常现代的英特尔处理器上,添加“rdfsbase”、“wrfsbase”、“rdgsbase”和“wrgsbase”指令后,直接访问FS和GS的基址寄存器的难度大大降低。事实上,如果内核愿意,它可以允许从用户模式使用它们。您可能需要检查现代Linux内核是否利用wrfsbase使分配低于4GB的TLS区域变得不必要

我不知道它在Linux上是如何运行的,但从Windows7开始的WindowsNT将用户模式线程调度作为应用程序开发人员的可选功能。与Linux和Mac OS X一样,Windows使用段寄存器的基址(x86-64 Windows中的GS)在x86上实现线程本地存储。这个特性与fibers类似,只是一个程序可以以内核也能识别的方式将自己的线程上下文切换到另一个线程上下文

Windows中的用户模式调度是通过为每个可调度线程创建一个LDT来实现的,LDT的段指向TLS块(在Windows中称为“线程环境块”,或TEB)。然后,用户模式可以通过在上下文切换之外重新加载GS base来切换线程。这要求TEB低于2^32,正如Linux的arch_prctl的性能说明中所述——否则,用户模式调度将要求每次切换到不同线程时调用NT内核以执行wrmsr,从而破坏了整个用户模式调度

在Windows 8.1中,添加了对wrgsbase的支持,并且在用户模式下也启用了wrgsbase。8.1中的用户模式调度使用wrgsbase,而不是GS段重新加载和LDT(如果CPU有)

case ARCH_SET_FS:
        /* handle small bases via the GDT because that's faster to
           switch. */
        if (addr <= 0xffffffff) {
                set_32bit_tls(task, FS_TLS, addr);
                if (doit) {
                        load_TLS(&task->thread, cpu);
                        loadsegment(fs, FS_TLS_SEL);
                }
                task->thread.fsindex = FS_TLS_SEL;
                task->thread.fs = 0;
        } else {
                task->thread.fsindex = 0;
                task->thread.fs = addr;
                if (doit) {
                        /* set the selector to 0 to not confuse
                           __switch_to */
                        loadsegment(fs, 0);
                        ret = wrmsrl_safe(MSR_FS_BASE, addr);
                }
        }
        put_cpu();
        break;