当前位置:  开发笔记 > 前端 > 正文

是否可以在Linux x86 GAS程序集中创建没有系统调用的线程?

如何解决《是否可以在Linuxx86GAS程序集中创建没有系统调用的线程?》经验,为你挑选了4个好方法。

在学习"汇编语言"(在使用GNU作为汇编程序的x86架构上的linux中)时,其中一个时刻就是使用系统调用的可能性.这些系统调用非常方便,有时甚至是必需的,因为您的程序在用户空间中运行.
然而,系统调用在性能方面相当昂贵,因为它们需要中断(当然还有系统调用),这意味着必须从用户空间中的当前活动程序到内核空间中运行的系统进行上下文切换.

我想说的是:我目前正在实现一个编译器(用于大学项目),我想要添加的一个额外功能是支持多线程代码以提高编译程序的性能.因为一些多线程代码将由编译器本身自动生成,所以这几乎可以保证其中也会有很少的多线程代码.为了获得性能,我必须确保使用线程可以实现这一点.

但我担心的是,为了使用线程,我必须进行系统调用和必要的中断.因此,微小的(自动生成的)线程将受到进行这些系统调用所花费的时间的极大影响,这甚至可能导致性能损失......

因此,我的问题是双重的(在其下面有一个额外的奖励问题):

是否可以编写汇编代码,可以同时在多个内核上同时运行多个线程,无需系统调用?

如果我有非常小的线程(在线程的总执行时间很小),性能损失,或者根本不值得努力,我会获得性能提升吗?

我的猜测是没有系统调用就不可能有多线程汇编程序代码.即使是这种情况,您是否有建议(甚至更好:一些实际代码)尽可能高效地实现线程?



1> Nathan Fellm..:

简短的回答是,你做不到.编写汇编代码时,它会在一个且只有一个逻辑(即硬件)线程上顺序运行(或使用分支).如果您希望某些代码在另一个逻辑线程上执行(无论是在同一个内核上,在同一CPU上的不同内核上,还是在不同的CPU上),您需要让操作系统设置另一个线程的指令指针(CS:EIP)指向要运行的代码.这意味着使用系统调用来让操作系统执行您想要的操作.

用户线程不会为您提供所需的线程支持,因为它们都在相同的硬件线程上运行.

编辑:将 Ira Baxter的答案与Parlanse结合起来.如果确保程序在每个逻辑线程中都有一个运行的线程,那么您可以构建自己的调度程序而不依赖于操作系统.无论哪种方式,您都需要一个调度程序来处理从一个线程到另一个线程的跳转.在对调度程序的调用之间,没有用于处理多线程的特殊汇编指令.调度程序本身不能依赖于任何特殊程序集,而是依赖于每个线程中调度程序各部分之间的约定.

无论哪种方式,无论您是否使用操作系统,您仍然必须依赖某些调度程序来处理跨线程执行.



2> Ira Baxter..:

"医生,医生,我这样做会很疼".医生:"不要那样做".

简而言之,您可以在不调用昂贵的OS任务管理原语的情况下执行多线程编程.简单地忽略用于线程调度操作的OS.这意味着您必须编写自己的线程调度程序,并且永远不会将控制权传递回操作系统.(而且你必须更聪明地了解你的线程开销,而不是相当聪明的操作系统人员).我们选择这种方法正是因为windows进程/线程/光纤调用太昂贵而无法支持几百条指令的计算粒度.

我们的PARLANSE编程语言是一种并行编程语言:请参阅http://www.semdesigns.com/Products/Parlanse/index.html

PARLANSE在Windows下运行,提供并行"粒度"作为抽象并行性构造,并通过高度调整的手写调度程序和PARLANSE编译器生成的调度代码的组合来调度此类粒度,该代码考虑了粒度的上下文以最小化调度高架.例如,编译器确保粒度的寄存器在可能需要调度(例如,"等待")的点处不包含信息,因此调度器代码仅需要保存PC和SP.实际上,调度程序代码通常根本无法控制; 分叉的谷物只存储分叉的PC和SP,切换到编译器预先分配的堆栈并跳转到谷物代码.谷物的完工将重新启动.

通常有一个互锁来同步谷物,由编译器使用本机LOCK DEC指令实现,这些指令实现了计算信号量的数量.应用程序可以逻辑分叉数百万个谷物; 如果工作队列足够长,那么调度程序会限制父母谷物产生更多的工作,因此更多的工作将无济于事.调度程序实现了工作窃取,允许工作匮乏的CPU从相邻的CPU工作队列中获取准备好的粒子.这已经实现,可以处理多达32个CPU; 但我们有点担心x86供应商实际上可能会在未来几年内使用更多的产品!

PARLANSE是一个成熟的语言; 我们自1997年以来一直在使用它,并在其中实现了数百万行并行应用程序.


我的意思是PARLANSE做自己的线程调度.它将Windows线程多路复用在"粒子"之上; 当一个粒子完成执行时,它将控制传递给PARLANSE调度程序,该调度程序从PARLANSE每线程准备运行粒度队列中选择另一个可立即运行的粒子,或者尝试从准备好的队列队列中窃取一粒粒子,变成那颗粮.是的,确实要执行操作系统功能这些线程必须在操作系统上进行真正的调用,但预计这是非常罕见的(例如从文件中读取非常大的块),不,我无法阻止设备或时钟滴答中断.

3> 小智..:

实现用户模式线程.

从历史上看,线程模型被概括为N:M,也就是说在M个内核模型线程上运行的N个用户模式线程.现代用法是1:1,但它并不总是那样,它不一定是那样的.

您可以在单个内核线程中维护任意数量的用户模式线程.只是你有责任在它们之间经常切换它们看起来并发.你的线索当然是合作而不是先发制人; 你基本上在你自己的代码中散布了yield()调用,以确保定期切换.


这是一个错误的答案,因为OP专门指定在多个核上同时运行.
需要注意的是:像这样使用模式线程不会获得任何性能,因为它将在一个CPU核心上运行.要获得同步多线程,您真的需要内核的帮助.
关键是要使用调用堆栈.

4> Adam Rosenfi..:

如果要获得性能,则必须利用内核线程.只有内核可以帮助您在多个CPU核心上同时运行代码.除非您的程序受I/O限制(或执行其他阻塞操作),否则执行用户模式协作多线程(也称为光纤)不会获得任何性能.您将只执行额外的上下文切换,但是您的实际线程正在运行的一个CPU仍将以100%运行.

系统调用变得更快.现代CPU支持该sysenter指令,该指令明显快于旧int指令.另请参阅本文,了解Linux如何以最快的方式进行系统调用.

确保自动生成的多线程使线程运行足够长的时间以获得性能.不要试图并行化短代码,你只会浪费时间产生和加入线程.还要警惕记忆效应(虽然这些很难测量和预测) - 如果多个线程正在访问独立的数据集,它们的运行速度会比由于缓存一致性问题而重复访问相同的数据要快得多.

推荐阅读
mobiledu2402852413
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有