首页 文章 精选 留言 我的

精选列表

搜索[速度],共10005篇文章
优秀的个人博客,低调大师

LinkedList插入速度比ArrayList快?你确定吗?

持续坚持原创输出,点击蓝字关注我吧 作者:小傅哥博客:https://bugstack.cn ❝ 沉淀、分享、成长,让自己和他人都能有所收获!😜 ❞ 目录 一、前言 二、面试题 三、数据结构 四、源码分析 1. 初始化 2. 插入 3. 删除 4. 遍历 五、总结 一、前言 你以为考你个数据结构是要造火箭? 🚕汽车75马力就够奔跑了,那你怎么还想要2.0涡轮+9AT呢?大桥两边的护栏你每次走的时候都会去摸吗?那怎么没有护栏的大桥你不敢上呢? 很多时候,你额外的能力才是自身价值的体现,不要以为你的能力就只是做个业务开发每天CRUD,并不是产品让你写CRUD,而是因为你的能力只能产品功能设计成CRUD。 就像数据结构、算法逻辑、源码技能,它都是可以为你的业务开发赋能的,也是写出更好、更易扩展程序的根基,所以学好这份知识非常有必要。 本文涉及了较多的代码和实践验证图稿,欢迎关注公众号:bugstack虫洞栈,回复下载得到一个链接打开后,找到ID:19🤫获取! 二、面试题 谢飞机,ArrayList资料看了吧?嗯,那行问问你哈🦀 「问」:ArrayList和LinkedList,都用在什么场景呢? 「答」:啊,这我知道了。ArrayList是基于数组实现、LinkedList是基于双向链表实现,所以基于数据结构的不同,遍历和查找多的情况下用ArrayList、插入和删除频繁的情况下用LinkedList。 「问」:嗯,那LinkedList的插入效率一定比ArrayList好吗? 「答」:对,好! 送你个飞机✈,回去等消息吧! 其实,飞机回答的也不是不对,只是不全面。出门后不甘心买瓶肥宅水又回来,跟面试官聊了2个点,要到了两张图,如下; 小傅哥 bugstack.cn & ArrayList头插、尾插、中间 小傅哥 bugstack.cn & LinkedList头插、尾插、中间 如图,分别是;10万、100万、1000万,数据在两种集合下不同位置的插入效果,「所以:」,不能说LinkedList插入就快,ArrayList插入就慢,还需要看具体的操作情况。 接下来我们带着数据结构和源码,具体分析下。 三、数据结构 Linked + List = 链表 + 列表 = LinkedList = 链表列表 小傅哥 bugstack.cn & LinkedList数据结构 LinkedList,是基于链表实现,由双向链条next、prev,把数据节点穿插起来。所以,在插入数据时,是不需要像我们上一章节介绍的ArrayList那样,扩容数组。 但,又不能说所有的插入都是高效,比如中间区域插入,他还需要遍历元素找到插入位置。具体的细节,我们在下文的源码分析中进行讲解,也帮谢飞机扫除疑惑。 四、源码分析 1. 初始化 与ArrayList不同,LinkedList初始化不需要创建数组,因为它是一个链表结构。而且也没有传给构造函数初始化多少个空间的入参,例如这样是不可以的,如下; 「但是」,构造函数一样提供了和ArrayList一些相同的方式,来初始化入参,如下这四种方式; @Testpublicvoidtest_init(){//初始化方式;普通方式LinkedList<String>list01=newLinkedList<String>();list01.add("a");list01.add("b");list01.add("c");System.out.println(list01);//初始化方式;Arrays.asListLinkedList<String>list02=newLinkedList<String>(Arrays.asList("a","b","c"));System.out.println(list02);//初始化方式;内部类LinkedList<String>list03=newLinkedList<String>()\\{{add("a");add("b");add("c");}\\};System.out.println(list03);//初始化方式;Collections.nCopiesLinkedList<Integer>list04=newLinkedList<Integer>(Collections.nCopies(10,0));System.out.println(list04);}//测试结果[a,b,c][a,b,c][a,b,c][0,0,0,0,0,0,0,0,0,0]Processfinishedwithexitcode0 这些方式都可以初始化操作,按需选择即可。 2. 插入 LinkedList的插入方法比较多,List中接口中默认提供的是add,也可以指定位置插入。但在LinkedList中还提供了头插addFirst和尾插addLast。 关于插入这部分就会讲到为什么;有的时候LinkedList插入更耗时、有的时候ArrayList插入更好。 2.1 头插 先来看一张数据结构对比图,回顾下ArrayList的插入也和LinkedList插入做下对比,如下; 小傅哥 bugstack.cn & 插入对比 看上图我们可以分析出几点; ArrayList 头插时,需要把数组元素通过 Arrays.copyOf的方式把数组元素移位,如果容量不足还需要扩容。 LinkedList 头插时,则不需要考虑扩容以及移位问题,直接把元素定位到首位,接点链条链接上即可。 2.1.1 源码 这里我们再对照下LinkedList头插的源码,如下; privatevoidlinkFirst(Ee){finalNode<E>f=first;finalNode<E>newNode=newNode<>(null,e,f);first=newNode;if(f==null)last=newNode;elsef.prev=newNode;size++;modCount++;} first,首节点会一直被记录,这样就非常方便头插。 插入时候会创建新的节点元素, new Node<>(null, e, f),紧接着把新的头元素赋值给first。 之后判断f节点是否存在,不存在则把头插节点作为最后一个节点、存在则用f节点的上一个链条prev链接。 最后记录size大小、和元素数量modCount。 modCount用在遍历时做校验,modCount != expectedModCount 2.1.2 验证 「ArrayList、LinkeList,头插源码验证」 @Testpublicvoidtest_ArrayList_addFirst(){ArrayList<Integer>list=newArrayList<Integer>();longstartTime=System.currentTimeMillis();for(inti=0;i<10000000;i++){list.add(0,i);}System.out.println("耗时:"+(System.currentTimeMillis()-startTime));}@Testpublicvoidtest_LinkedList_addFirst(){LinkedList<Integer>list=newLinkedList<Integer>();longstartTime=System.currentTimeMillis();for(inti=0;i<10000000;i++){list.addFirst(i);}System.out.println("耗时:"+(System.currentTimeMillis()-startTime));} 「比对结果:」 这里我们分别验证,10万、100万、1000万的数据量,在头插时的一个耗时情况。 如我们数据结构对比图中一样,ArrayList需要做大量的位移和复制操作,而LinkedList的优势就体现出来了,耗时只是实例化一个对象。 2.2 尾插 先来看一张数据结构对比图,回顾下ArrayList的插入也和LinkedList插入做下对比,如下; 小傅哥 bugstack.cn & 插入对比 看上图我们可以分析出几点; ArrayList 尾插时,是不需要数据位移的,比较耗时的是数据的扩容时,需要拷贝迁移。 LinkedList 尾插时,与头插相比耗时点会在对象的实例化上。 2.2.1 源码 这里我们再对照下LinkedList尾插的源码,如下; voidlinkLast(Ee){finalNode<E>l=last;finalNode<E>newNode=newNode<>(l,e,null);last=newNode;if(l==null)first=newNode;elsel.next=newNode;size++;modCount++;} 与头插代码相比几乎没有什么区别,只是first换成last 耗时点只是在创建节点上, Node<E> 2.2.2 验证 「ArrayList、LinkeList,尾插源码验证」 @Testpublicvoidtest_ArrayList_addLast(){ArrayList<Integer>list=newArrayList<Integer>();longstartTime=System.currentTimeMillis();for(inti=0;i<10000000;i++){list.add(i);}System.out.println("耗时:"+(System.currentTimeMillis()-startTime));}@Testpublicvoidtest_LinkedList_addLast(){LinkedList<Integer>list=newLinkedList<Integer>();longstartTime=System.currentTimeMillis();for(inti=0;i<1000000;i++){list.addLast(i);}System.out.println("耗时:"+(System.currentTimeMillis()-startTime));} 「比对结果:」 这里我们分别验证,10万、100万、1000万的数据量,在尾插时的一个耗时情况。 如我们数据结构对比图中一样,ArrayList 不需要做位移拷贝也就不那么耗时了,而LinkedList则需要创建大量的对象。 所以这里ArrayList尾插的效果更好一些。 2.3 中间插 先来看一张数据结构对比图,回顾下ArrayList的插入也和LinkedList插入做下对比,如下; 看上图我们可以分析出几点; ArrayList 中间插入,首先我们知道他的定位时间复杂度是O(1),比较耗时的点在于数据迁移和容量不足的时候扩容。 LinkedList 中间插入,链表的数据实际插入时候并不会怎么耗时,但是它定位的元素的时间复杂度是O(n),所以这部分以及元素的实例化比较耗时。 2.3.1 源码 这里看下LinkedList指定位置插入的源码; 「使用add(位置、元素)方法插入:」 publicvoidadd(intindex,Eelement){checkPositionIndex(index);if(index==size)linkLast(element);elselinkBefore(element,node(index));} 「位置定位node(index):」 Node<E>node(intindex){//assertisElementIndex(index);if(index<(size>>1)){Node<E>x=first;for(inti=0;i<index;i++)x=x.next;returnx;}else{Node<E>x=last;for(inti=size-1;i>index;i--)x=x.prev;returnx;}} size >> 1,这部分的代码判断元素位置在左半区间,还是右半区间,在进行循环查找。 「执行插入:」 voidlinkBefore(Ee,Node<E>succ){//assertsucc!=null;finalNode<E>pred=succ.prev;finalNode<E>newNode=newNode<>(pred,e,succ);succ.prev=newNode;if(pred==null)first=newNode;elsepred.next=newNode;size++;modCount++;} 找到指定位置插入的过程就比较简单了,与头插、尾插,相差不大。 整个过程可以看到,插入中比较耗时的点会在遍历寻找插入位置上。 2.3.2 验证 「ArrayList、LinkeList,中间插入源码验证」 @Testpublicvoidtest_ArrayList_addCenter(){ArrayList<Integer>list=newArrayList<Integer>();longstartTime=System.currentTimeMillis();for(inti=0;i<10000000;i++){list.add(list.size()>>1,i);}System.out.println("耗时:"+(System.currentTimeMillis()-startTime));}@Testpublicvoidtest_LinkedList_addCenter(){LinkedList<Integer>list=newLinkedList<Integer>();longstartTime=System.currentTimeMillis();for(inti=0;i<1000000;i++){list.add(list.size()>>1,i);}System.out.println("耗时:"+(System.currentTimeMillis()-startTime));} 「比对结果:」 这里我们分别验证,10万、100万、1000万的数据量,在中间插时的一个耗时情况。 可以看到Linkedlist在中间插入时,遍历寻找位置还是非常耗时了。所以不同的情况下,需要选择不同的List集合做业务。 3. 删除 讲了这么多插入的操作后,删除的知识点就很好理解了。与ArrayList不同,删除不需要拷贝元素,LinkedList是找到元素位置,把元素前后链连接上。基本如下图; 确定出要删除的元素x,把前后的链接进行替换。 如果是删除首尾元素,操作起来会更加容易,这也就是为什么说插入和删除快。但中间位置删除,需要遍历找到对应位置。 3.1 删除操作方法 序号 方法 描述 1 list.remove(); 与removeFirst()一致 2 list.remove(1); 删除Idx=1的位置元素节点,需要遍历定位 3 list.remove("a"); 删除元素="a"的节点,需要遍历定位 4 list.removeFirst(); 删除首位节点 5 list.removeLast(); 删除结尾节点 6 list.removeAll(Arrays.asList("a", "b")); 按照集合批量删除,底层是Iterator删除 「源码:」 @Testpublicvoidtest_remove(){LinkedList<String>list=newLinkedList<String>();list.add("a");list.add("b");list.add("c");list.remove();list.remove(1);list.remove("a");list.removeFirst();list.removeLast();list.removeAll(Arrays.asList("a","b"));} 3.2 源码 删除操作的源码都差不多,分为删除首尾节点与其他节点时候,对节点的解链操作。这里我们举例一个删除其他位置的源码进行学习,如下; 「list.remove("a");」 publicbooleanremove(Objecto){if(o==null){for(Node<E>x=first;x!=null;x=x.next){if(x.item==null){unlink(x);returntrue;}}}else{for(Node<E>x=first;x!=null;x=x.next){if(o.equals(x.item)){unlink(x);returntrue;}}}returnfalse;} 这一部分是元素定位,和 unlink(x)解链。循环查找对应的元素,这部分没有什么难点。 「unlink(x)解链」 Eunlink(Node<E>x){//assertx!=null;finalEelement=x.item;finalNode<E>next=x.next;finalNode<E>prev=x.prev;if(prev==null){first=next;}else{prev.next=next;x.prev=null;}if(next==null){last=prev;}else{next.prev=prev;x.next=null;}x.item=null;size--;modCount++;returnelement;} 这部分源码主要有以下几个知识点; 获取待删除节点的信息;元素item、元素下一个节点next、元素上一个节点prev。 如果上个节点为空则把待删除元素的下一个节点赋值给首节点,否则把待删除节点的下一个节点,赋值给待删除节点的上一个节点的子节点。 同样待删除节点的下一个节点next,也执行2步骤同样操作。 最后是把删除节点设置为null,并扣减size和modeCount数量。 4. 遍历 接下来说下遍历,ArrayList与LinkedList的遍历都是通用的,基本包括5种方式。 这里我们先初始化出待遍历的集合1千万数据; intxx=0;@Beforepublicvoidinit(){for(inti=0;i<10000000;i++){list.add(i);}} 4.1 普通for循环 @Testpublicvoidtest_LinkedList_for0(){longstartTime=System.currentTimeMillis();for(inti=0;i<list.size();i++){xx+=list.get(i);}System.out.println("耗时:"+(System.currentTimeMillis()-startTime));} 4.2 增强for循环 @Testpublicvoidtest_LinkedList_for1(){longstartTime=System.currentTimeMillis();for(Integeritr:list){xx+=itr;}System.out.println("耗时:"+(System.currentTimeMillis()-startTime));} 4.3 Iterator遍历 @Testpublicvoidtest_LinkedList_Iterator(){longstartTime=System.currentTimeMillis();Iterator<Integer>iterator=list.iterator();while(iterator.hasNext()){Integernext=iterator.next();xx+=next;}System.out.println("耗时:"+(System.currentTimeMillis()-startTime))} 4.4 forEach循环 @Testpublicvoidtest_LinkedList_forEach(){longstartTime=System.currentTimeMillis();list.forEach(integer->{xx+=integer;});System.out.println("耗时:"+(System.currentTimeMillis()-startTime));} 4.5 stream(流) @Testpublicvoidtest_LinkedList_stream(){longstartTime=System.currentTimeMillis();list.stream().forEach(integer->{xx+=integer;});System.out.println("耗时:"+(System.currentTimeMillis()-startTime));} 「那么」,以上这5种遍历方式谁最慢呢?按照我们的源码学习分析下吧,欢迎留下你的答案在评论区! 五、总结 ArrayList与LinkedList都有自己的使用场景,如果你不能很好的确定,那么就使用ArrayList。但如果你能确定你会在集合的首位有大量的插入、删除以及获取操作,那么可以使用LinkedList,因为它都有相应的方法; addFirst、 addLast、 removeFirst、 removeLast、 getFirst、 getLast,这些操作的时间复杂度都是O(1),非常高效。 LinkedList的链表结构不一定会比ArrayList节省空间,首先它所占用的内存不是连续的,其次他还需要大量的实例化对象创造节点。虽然不一定节省空间,但链表结构也是非常优秀的数据结构,它能在你的程序设计中起着非常优秀的作用,例如可视化的链路追踪图,就是需要链表结构,并需要每个节点自旋一次,用于串联业务。 程序的精髓往往就是数据结构的设计,这能为你的程序开发提供出非常高的效率改变。可能目前你还不能用到,但万一有一天你需要去造🚀火箭了呢? bugstack虫洞栈 沉淀、分享、成长,让自己和他人都能有所收获! 作者小傅哥多年从事一线互联网Java开发,从19年开始编写工作和学习历程的技术汇总,旨在为大家提供一个较清晰详细的核心技能学习文档。如果本文能为您提供帮助,请给予支持(关注、点赞、分享)! 感谢支持小傅哥原创,欢迎点击在看和转发 本文分享自微信公众号 - bugstack虫洞栈(bugstack)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

Dubbo 远程代码执行漏洞通告,速度升级

云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 0x01 漏洞背景 2020年06月23日, 360CERT监测发现 Apache Dubbo 官方 发布了 Apache Dubbo 远程代码执行的风险通告,该漏洞编号为 CVE-2020-1948,漏洞等级:高危。 Apache Dubbo 是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。 Apache Dubbo Provider 存在反序列化漏洞,攻击者可以通过RPC请求发送无法识别的服务名称或方法名称以及一些恶意参数有效载荷,当恶意参数被反序列化时,可以造成远程代码执行。该漏洞的相关技术细节已公开。 对此,360CERT建议广大用户及时安装最新补丁,做好资产自查以及预防工作,以免遭受黑客攻击。 0x02 风险等级 360CERT对该漏洞的评定结果如下 0x03 漏洞详情 Apache Dubbo Provider 存在反序列化漏洞,攻击者可以通过RPC请求发送无法识别的服务名称或方法名称以及一些恶意参数有效载荷,当恶意参数被反序列化时,可以造成远程代码执行。 0x04 影响版本 Dubbo 2.7.0 – 2.7.6Dubbo 2.6.0 – 2.6.7Dubbo 2.5.x (官方不再维护) 0x05 修复建议 通用修补建议:建议广大用户及时升级到2.7.7或更高版本,下载地址为:https://github.com/apache/dubbo/releases/tag/dubbo-2.7.7 0x06 相关空间测绘数据 360安全大脑-Quake网络空间测绘系统通过对全网资产测绘,发现Dubbo在国内均有广泛使用,具体分布如下图所示。 0x07 时间线 2020-06-22 Apache Dubbo 官方发布通告2020-06-23 360CERT发布预警 【云栖号在线课堂】每天都有产品技术专家分享!课程地址:https://yqh.aliyun.com/zhibo 立即加入社群,与专家面对面,及时了解课程最新动态!【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK 原文发布时间:2020-07-05本文作者:安全客本文来自:“互联网架构师”,了解相关信息可以关注“互联网架构师”

优秀的个人博客,低调大师

PostgreSQL 数据库内外查询速度比较

数据库内使用 Rust 编写存储过程,使用pgxr程序库; 数据库外使用 Go 语言,使用pgx连接数据库进行查询; 逻辑都是查询某个表的字段列表,循环执行10000次; 测试结果如下: Rust 存储过程: test_sql_speed: 26.810285862s Go 连接数据库查询: 32.746561715s Go 语言只建立一次连接。 看来复用连接的话开销很小的嘛,一次只需要花费 0.5 毫秒左右。 然后,又测试了最简单的 SQL 查询:SELECT 1,同样也是 10000 次; 这次,Rust 存储过程: test_sql_speed: 67.651917ms Go 连接数据库查询: 1.261617769s 数据库内查询那是相当快的,这样算来每次处理连接的耗时大概在 0.1 毫秒左右。 源代码如下: Rust #[no_mangle] pub extern "C" fn test_sql_speed(_fcinfo: FunctionCallInfo) -> Datum { let sys_time = SystemTime::now(); for _ in 1..10000 { let _i = query_for_int("select 1"); } let difference = SystemTime::now().duration_since(sys_time) .expect("SystemTime::duration_since failed"); eprintln!("test_sql_speed: {:?}", difference); PG_RETURN_I32(1) } Go func main() { db := openDbConnection() start := time.Now() i := 0 for i = 1; i <= 10000; i++ { db.Query(`SELECT 1`) } t := time.Now() elapsed := t.Sub(start) fmt.Printf("%v\n", elapsed) } 后来发现用于查询表字段的方法效率不行,是从 information_schema 这个 ANSI 标准目录里去查的,后来看了一些资料,改成从 pg_catalog 这个原生目录去查,结果性能有了大幅提升。 Rust 里查询一万次只用了 1 秒,Go 里查询一万次用了 3 秒。

优秀的个人博客,低调大师

Nginx开启gzip压缩大幅提高页面加载速度

1、Vim打开Nginx配置文件 vim/usr/local/nginx/conf/nginx.conf 2、找到如下一段,进行修改 gzip on;gzip_min_length 1k;gzip_buffers 4 16k;#gzip_http_version 1.0;gzip_comp_level 2;gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/font-woff;gzip_vary off;gzip_disable "MSIE [1-6]\."; 3、解释一下 第1行:开启Gzip 第2行:不压缩临界值,大于1K的才压缩,一般不用改 第3行:buffer,就是,嗯,算了不解释了,不用改 第4行:用了反向代理的话,末端通信是HTTP/1.0,有需求的应该也不用看我这科普文了;有这句的话注释了就行了,默认是HTTP/1.1 第5行:压缩级别,1-10,数字越大压缩的越好,时间也越长,看心情随便改吧 第6行:进行压缩的文件类型,缺啥补啥就行了,JavaScript有两种写法,最好都写上吧,总有人抱怨js文件没有压缩,其实多写一种格式就行了 第7行:跟Squid等缓存服务有关,on的话会在Header里增加"Vary: Accept-Encoding",我不需要这玩意,自己对照情况看着办吧 第8行:IE6对Gzip不怎么友好,不给它Gzip了 4、:wq保存退出,重新加载Nginx /usr/local/nginx/sbin/nginx -s reload 5、用curl测试Gzip是否成功开启 curl -I -H "Accept-Encoding: gzip, deflate" "http://www.slyar.com/blog/" HTTP/1.1 200 OKServer: nginx/1.0.15Date: Sun, 26 Aug 2012 18:13:09 GMTContent-Type: text/html; charset=UTF-8Connection: keep-aliveX-Powered-By: PHP/5.2.17p1X-Pingback: http://www.slyar.com/blog/xmlrpc.phpContent-Encoding: gzip 谋胆并重

优秀的个人博客,低调大师

决定网站访问速度快慢的因素总结

第一,主机线路和地区的选择 现在比较普遍的说法是南电信北网通。众所周知,我们国家存在多个不同网络接入商,比如电信,网通,铁通,教育网,移动等。一些公司采用的是机房有单线也有bgp,这个要根据情况自己来选择。目前除阿里云8线BGP机房以外,其他国内idc提供商多数都是线路比较单一或几线的BGP机房,选择主机或托管的时候就要根据自己的需求睁大眼睛了 第二、主机的配置和访问量是否匹配 服务器的配置包括服务器硬件和网络带宽两部分,我司目前采用的都是配置很高的服务器,可以满足整体需要。因此决定您主机配置的因素主要在于主机参数,您选择价格相对较高的产品,所提供的配置和带宽就越大。访问量大的站点,所需要的主机配置和带宽资源也相应更大。 针对虚拟主机来讲,资源共享,适合于中小展示站点。 针对VPS,云主机或独立服务器,需要根据自己的需求考虑服务器的配置(比如CPU、内存和峰值带宽),一般云主机配置比VPS高,但低于独立的物理实体服务器 第三、访问者与机房间的路由网络状况 这也是多数客户抱怨网站访问慢的主要原因。如果电信、联通等某个路由节点不畅通或节点之间延迟比较高,就会出现短暂的访问慢或无法访问等现象,不过这是暂时现象,其解决根源在于整个互联网各线路商的共同合作完善。另外部分用户采用的是爱普、长宽、移动等网络,网络出口不固定且有些网络出口单一,于其他网络互通并不好 第四、访问者的计算机配置、本地网速等 配置越高,处理能力越强。带宽只要有效带宽比较多,都没有影响。避免开启迅雷等p2p下载工具或影音软件占用带宽过高,或局域网中其他电脑占用带宽过多导致有效带宽不高而访问慢 第五、网站程序是否优化 同样的网络环境和配置,一个优化程序(优化从程序脚本执行效率比较高)做出来的网站明显比繁琐劣质的程序网站快。个别站点因为首页比较大 或外链了其他比较慢的网站,会导致加载时间慢,会给人一种访问慢的感觉 本文转自 2012hjtwyf 51CTO博客,原文链接:http://blog.51cto.com/hujiangtao/1931878,如需转载请自行联系原作者

优秀的个人博客,低调大师

WSL 2 正在优化 Windows 文件系统访问速度

WSL 2 的文件访问性能优化经历了一个漫长的技术迭代过程。最初的 WSL 1(2016)使用 DrvFs,这是一种直接运行在 Windows NT 内核上的自定义文件系统驱动,使/mnt/c下的文件操作几乎直接到达 NTFS,延迟极低。WSL 2(2019)切换到完整 Linux 内核运行在轻量级 Hyper-V VM 中后,跨系统文件访问面临新的技术挑战——微软在 Windows 端 WSL 服务中构建了一个 Plan 9 文件服务器,Linux 会话在启动时通过 Hyper-V socket 连接,9P 协议成为了两者之间的桥梁。 问题在于 9P 协议有固有缺陷:每次操作的消息大小被限制在 64 KB 参数以内,对于文件 heavy 的工作负载——比如涉及大量小文件的操作——会产生显著的开销。2021 年左右,virtiofs 作为实验性功能登场,用户可以通过在 .wslconfig 文件的 [wsl2] 部分设置 virtiofs=true 来启用。virtiofs 使用 VirtIO 传输进行共享内存文件访问,相比 9P 减少了序列化开销。但它一直是 opt-in 的可选功能——默认传输仍然是 Plan 9 over Hyper-V socket。 2026 年 5 月,一个重要的变化通过 PR #40654 合并到 WSL 2 主线。这个由 Ben Hillis 编写的变更,为每个 virtio 设备提供了独立的 DMA 池,而不是共享一个全局 SWIOTLB 池。在此之前,WSL 2 会话中的所有 virtio 设备——包括不同驱动器的 virtiofs 挂载点和 virtio 网络适配器——都在同一个 bounce buffer 区域(下限 4GB DMA 边界)排队,在重 I/O 操作期间造成争用。对于同时使用多个驱动器挂载点的用户,这个争用尤为明显。 这个优化需要 Microsoft.WSL.Kernel 6.18.26.3-1 或更高版本,结合 WSL 2 DeviceHost 1.2.29-0 使用。运行旧内核的用户会看到一条消息:"The running kernel is missing a patch that significantly improves virtio device performance. Update to a more recent WSL kernel to enable this optimization." 这是一个非破坏性的变更——没有独立 DMA 池功能的系统仍然可以正常运行 virtiofs,只是无法享受性能优化。 受益最大的场景是跨系统文件 heavy 的工作流:项目存储在 Windows 驱动器上,但构建在 Linux 中运行。从 /mnt/c 执行 cargo build、npm install 或 mvn package 等命令,都会因这个优化而改善性能。VirtioProxy 网络也受益,因为它共享同一个 DMA 基础设施。用户应当确保 WSL 2 会话的 RAM 保持在 1GB 以上,以提供至少 64MB 的 SWIOTLB 池空间余量——这是让优化生效的前提条件。 virtiofs 仍然是 opt-in 的默认选项。对于大多数用户,这意味着如果当前 virtiofs 没有打开,这个优化暂时还与你无关。但它代表了 WSL 2 在文件访问性能这个议题上持续改进的路径——微软正在逐步解决过去几年社区反馈最多的痛点之一。 参考来源:https://www.boxofcables.dev/wsl2-per-device-swiotlb-pools-for-virtiofs-and-virtioproxy/

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册