首页 文章 精选 留言 我的

精选列表

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

SpringBoot2整合Redis,开启缓存,提高访问速度

前言 什么是Redis Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持字符串、哈希表、列表、集合、有序集合,位图,hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。 参考文档:https://www.redis.net.cn 为什么选择Redis 这个问题一般人会拿Redis和Memcache来做比较,但是个人认为这两者对比并不合适,因为Memcache仅仅作为缓存,而Redis是一个NoSQL数据库,除了缓存还能做其他的很多事。所以拿来对比的同学应该就只是拿Redis来做缓存用了。但是Redis还有很多高级功能,包括持久化、复制、哨兵、集群等。因此Redis的用途更为广泛。 编码 1.添加Redis依赖 之前的文章中我们讲到数据库连接池的作用,有太多的有点了,所以今天在Redis这边我们也建立一个连接池来连接Redis。提高资源的利用率。在此采用的org.apache.commons.pool2来作为池,因此需要对添加一个池依赖。 打开pom.xml文件,添加 xml 复制代码 <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 2.配置SpringBoot的application.properties文件 因为SpringBoot2.x默认使用lettuce作为连接池,所以以下为lettuce的配置方式 sh 复制代码 # Redis配置 # spring.redis.database : Redis数据库索引(默认为0) # spring.redis.host : Redis服务器地址 # spring.redis.port : Redis服务器连接端口 # spring.redis.password : Redis服务器连接密码(默认为空) # spring.redis.timeout : 连接超时时间(毫秒) # spring.redis.lettuce.pool.max-active : 连接池最大连接数(使用负值表示没有限制) # spring.redis.lettuce.pool.max-idle : 连接池中的最大空闲连接 # spring.redis.lettuce.pool.max-wait : 连接池最大阻塞等待时间(使用负值表示没有限制) # spring.redis.lettuce.pool.min-idle : 连接池中的最小空闲连接 # spring.redis.lettuce.shutdown-timeout : 连接池中的关闭超时时间 spring.redis.database=1 spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.password= spring.redis.timeout=100000 spring.redis.lettuce.pool.max-active=50 spring.redis.lettuce.pool.max-idle=300 spring.redis.lettuce.pool.max-wait=-1 spring.redis.lettuce.pool.min-idle=10 spring.redis.lettuce.shutdown-timeout=100000 3.编写Controller测试Redis缓存数据 新增RedisController.java java 复制代码 package org.xujun.springboot.controller; import javax.annotation.Resource; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class RedisController { @Resource private StringRedisTemplate stringRedisTemplate; @GetMapping("redis") public String redis() { String key = "redis"; String data = "redis-data"; // 保存数据 stringRedisTemplate.opsForValue().set(key, data); // 获取数据 String getData = stringRedisTemplate.opsForValue().get(key); System.out.println(data.equals(getData)); // 删除数据 Boolean delete = stringRedisTemplate.delete(key); System.out.println(delete); return "suc"; } } 4.测试结果 运行项目,并且访问[http://127.0.0.1:8080/redis]。结果如下图所示 总结:本文章仅仅做了SpringBoot整合Redis,然后做了和Redis缓存测试。并未去探索Redis的高级功能。但是后期会陆续推出Redis的系列文章,在该系列中会详细讲解Redis

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

App启动速度优化

谷歌官方文档:https://developer.android.com/topic/performance/launch-time.html#common 一、app启动方式 启动方式分为两种:冷启动、热启动、温启动。 1. 冷启动 启动app时,后台没有app的进程,或者进程被killed,这叫冷启动。冷启动因为系统会重新创建一个新的进程分配给它,所以会先创建和初始化Application类,再创建和初始化MainActivity类(包括一系列的测量、布局、绘制),最后显示在界面上。 2. 热启动 启动app时,后台已有app的进程(例:按back键、home键,应用虽然会退出,但是该应用的进程是依然会保留在后台,可进入任务列表查看),所以在已有进程的情况下,这种启动会从已有的进程中来启动应用,这个方式叫热启动。 热启动因为会从已有的进程中来启动,所以热启动就不会走Application这步了,而是直接走MainActivity(包括一系列的测量、布局、绘制),所以热启动的过程只需要创建和初始化一个MainActivity就行了,而不必创建和初始化Application,因为一个应用从新进程的创建到进程的销毁,Application只会初始化一次。 3. 温启动 介于冷启动和热启动之间, 一般来说在以下两种情况下发生: 用户back退出了App, 然后又启动. App进程可能还在运行, 但是activity需要重建。用户退出App后, 系统可能由于内存原因将App杀死, 进程和activity都需要重启, 但是可以在onCreate中将被动杀死锁保存的状态(saved instance state)恢复。 通过三种启动状态的相关描述, 可以看出我们要做的启动优化其实就是针对冷启动. 热启动和温启动都相对较快. 二、app启动流程 这里是冷启动流程。在安卓系统上,应用在没有进程的情况下,应用的启动都是这样一个流程:当点击app的启动图标时,安卓系统会从Zygote进程中fork创建出一个新的进程分配给该应用,之后会依次创建和初始化Application类、创建MainActivity类、加载主题样式Theme中的windowBackground等属性设置给MainActivity以及配置Activity层级上的一些属性、再inflate布局、当onCreate/onStart/onResume方法都走完了后最后才进行contentView的measure/layout/draw显示在界面上,所以直到这里,应用的第一次启动才算完成,这时候我们看到的界面也就是所说的第一帧。 00=>start: Application的构造器方法 01=>operation: attachBaseContext() 02=>operation: onCreate方法 03=>operation: Activity 的构造方法 04=>operation: onCreate() 05=>operation: 配置主题背景等属性 06=>operation: onStart() 07=>operation: onResume() 08=>end: 测量布局绘制显示在界面上 00->01->02->03->04->05->06->07->08->09 三、测量app启动时间 应用的启动时间:点击应用icon开始创建出一个新的进程直到看到了界面上的第一帧,这段时间就是应用的启动时间。 1.方式一:通过adb shell命令的方式进行测量,这种方法测量的最为精准 $ adb shell am start -W [packageName]/[packageName.LaunchActivity] 执行成功后返回三个测量得到的时间: **ThisTime : **一般和TotalTime时间一样,除非在应用启动时开了一个透明的Activity预先处理一些事再显示出主Activity,这样将比TotalTime小。 **TotalTime : **应用的启动时间,包括创建进程+Application初始化+Activity初始化到界面显示。 **WaitTime : **一般比TotalTime大点,包括系统影响的耗时。 举个栗子,下面是对全视频的启动时间测量: 冷启动时间: C:\Users\luguoqiang>adb shell am start -W com.qsp.launcher/com.qsp.launcher.Spla shActivity Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.L AUNCHER] cmp=com.qsp.launcher/.SplashActivity } Status: ok Activity: com.qsp.launcher/.SplashActivity ThisTime: 957 TotalTime: 957 WaitTime: 977 Complete 热启动时间: C:\Users\luguoqiang>adb shell am start -W com.qsp.launcher/com.qsp.launcher.Spla shActivity Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.L AUNCHER] cmp=com.qsp.launcher/.SplashActivity } Status: ok Activity: com.qsp.launcher/.SplashActivity ThisTime: 284 TotalTime: 284 WaitTime: 312 Complete 2. 方式二:利用Traceview性能分析工具 生成trace文件 Traceview是一个性能分析工具, 主要是分析当前线程情况, 各个方法执行时间等,Android studio内置的工具。 在Application的onCreate开始和结尾打上trace Debug.startMethodTracing("App"); ... Debug.stopMethodTracing(); 运行程序,会在设备的sd卡上生成一个App.trace文件,注意不要忘记加权限。 DDMS中进行分析 1478350802035.png 在下方的方法区点击"Real Time/Call", 按照方法每次调用耗时降序排. 耗时超过500ms都是值得注意的。 3. 方式三:Time to initial display 从 4.4 (API 19) 开始,logcat会输出带有 Diaplay 的 log 。该值代表从app启动进程到完成Activity第一次绘制的时候完成了下一个刘流程: 举个栗子,全视频的启动时间的如下 1478489524776.png 4.方式四:NimbleDroidAndroid 应用性能分析服务 四、怎样优化 Application 的 onCreate()中加入了许多耗时的操作,把这些初始化的操作放在一个单独的线程中处理。 1. 利用InitializeService的IntentService来做初始化工作 //TODO IntentService不同于Service,具体不同需要搜索资料 [InitializeService.java] public class InitializeService extends IntentService { private static final String ACTION_INIT_WHEN_APP_CREATE = "com.demo.service.action.INIT"; public InitializeService() { super("InitializeService"); } public static void start(Context context) { Intent intent = new Intent(context, InitializeService.class); intent.setAction(ACTION_INIT_WHEN_APP_CREATE); context.startService(intent); } @Override protected void onHandleIntent(Intent intent) { if (intent != null) { final String action = intent.getAction(); if (ACTION_INIT_WHEN_APP_CREATE.equals(action)) { performInit(); } } } private void performInit() { AppLog.d("performInit begin:" + System.currentTimeMillis()); // 这里进行一些耗时性的初始化,需要注意某些初始化的操作不能在子线程中执行。 ... // init crash helper CrashHelper.init(this.getApplicationContext()); // init Push PushPlatform.init(this.getApplicationContext()); ... AppLog.d("performInit end:" + System.currentTimeMillis()); } } Application中的onCreate()处用法如下 public class MyApplication extends MultiDexApplication { @Override public void onCreate() { super.onCreate(); ... InitializeService.start(this); } } 五、启动页开启前的白屏or黑屏优化 初始化首屏的Activity过程中(或者说在等待第一帧显示的时间里)屏幕会显示一个空白/黑色的窗口(颜色基于主题),直至首屏Activity完全启动。 这个空白的窗口跟主题相关,所以可以从首屏的主题入手。 给Splash界面加上一个主题,这个主题会在现实第一帧前提前显示在界面上,设置想要展示的背景。 做一个splash背景 [logo_splash.xml] <?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 底层白色 --> <item android:drawable="@color/white" /> <!-- 顶层Logo居中 --> <item> <bitmap android:gravity="center" android:src="@drawable/ic_github" /> </item> </layer-list> 自定义splashTheme主题 [styte.xml] <style name="SplashTheme" parent="AppTheme"> <item name="android:windowBackground">@drawable/logo_splash</item> </style> 将一个什么不渲染的布局的Activity作为启动页 public class LogoSplashActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 注意, 这里并没有setContentView, 单纯只是用来跳转到相应的Activity. // 目的是减少首屏渲染 if (AppPref.isFirstRunning(this)) { IntroduceActivity.launch(this); } else { MainActivity.launch(this); } finish(); } } 在AndroidMainfest.xml设置为启动屏,并加上主题 <activity android:name=".ui.module.main.LogoSplashActivity" android:screenOrientation="portrait" android:theme="@style/SplashTheme"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> 参考博客: (TraceView 性能分析工具)[http://mouxuejie.com/blog/2016-02-25/android-tools-traceview/]

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

约束布局ConstraintLayout加快布局速度

Android Studio2.2更新布局设计器,同时,引人了约束布局ConstraintLayout。 简单来说,可以把它看做是相对布局的升级版本,但是区别与相对布局更加强调约束。何为约束,即控件之间的关系。 它能让你的布局更加扁平化,一般来说一个界面一层就够了;同时借助于AS我们能极其简单的完成界面布局。 ConstraintLayout简介​ ConstraintLayout的优点非常突出。 ConstraintLayout不需要使用嵌套布局就可以让我们去构建一个大而复杂的布局, 他与RelativeLayout很相似,所有在里面的View的布局方式取决于View与View之间的关系和父布局。但是他比RelativeLayout 更灵活,并且在Android Studio’s Layout Editor中可以很容易的去使用。 ConstraintLayout的所有工作都可以使用布局编辑器的可视化工具中完成,因为布局API和布局编辑器对此专门构建的。因此你可以完全通过拖拽的方式去构建一个使用了ConstraintLayout 的布局,而不用直接在XML中编辑。 参考下图: 这个界面主要分成下面几个部分: 左侧边栏,包括Palette组件库和Component Tree 中间是布局设计器,包括两部分,左边是视图预览,右边是布局约束 右侧边栏,上面是类似盒子模型的边界和大小布局设计器,下面是属性列表。 简单介绍完了约束布局的特色和开发界面,接下来来看看如何使用约束布局。 添加约束布局 使用约束布局,必须确保你拥有最新的约束布局的库。 点击Tools > Android > SDK Manager 点击SDK Tools Tab 展开Support Repository,然后勾选ConstraintLayout for Android 和Solver for ConstraintLayout。勾选Show Package Details,注意你下载的版本。 点击OK 添加ConstraintLayout库到你的build.gradle文件中,目前最新版本是beta4 dependencies { //... compile 'com.android.support.constraint:constraint-layout:1.0.0-beta4' } 你下载的这个库的版本可能会更高,确保与你之前下载的版本匹配即可。 使用约束布局 新建的项目,自动生成的布局默认不会使用ConstraintLayout,但是Android Studio提供了便捷的方式,可以直接转换成ConstraintLayout。 打开你的布局文件,切换到Design tab 在Component Tree窗体中,右击布局文件,然后点击Convert layout to ConstraintLayout(如下图)。 当然, 也可以新建一个新的约束布局。 新建一个布局文件 输入布局文件的名字,将布局的根元素改为 android.support.constraint.ConstraintLayout 最后点击完成 添加约束 拖一个View到布局编辑器中。当你添加了一个View到ConstraintLayout中,他的四个角对应着的四个小矩形框是控制大小的,每一条边有四个圆形的约束控制点。 这里主要包含几种类型的约束 尺寸大小 边界约束 基准线约束 约束到一个引导线(辅助线) 尺寸大小 尺寸约束使用的是『实心方块』,如图: 这个很好理解,就是调整组件的大小。 你可以使用View每个角的控制点去调整其大小,但是这样做只是把宽高写死,这样做不能适应不同的内容和不同的屏幕大小,我们应该避免这样去使用。为了选择一个动态的大小模式或者定义一个更具体的尺寸,请单击并打开编辑器右侧的Properties窗口,如下图。 灰色的矩形区域,代表选择的View,矩形的的符号代表宽和高。 (>>>)Wrap Content:View的大小与其内容适配 (有点像弹簧的图标)Any Size:View大小刚好匹配其对应的约束,他的实际值是0dp,表示这个View没有期望的尺寸,但是他渲染后的大小将会匹配其约束。 (直线)Fixed:View的大小的是固定的 点击符号即可在上面三种模式中互相切换。 注意:你不应该在ConstraintLayout中使用match_parent,而是使用0dp 边界约束 边界约束使用的是『空心圆圈』,如图: 边界约束,是使用最多的约束,它用于建立组件与组件之间、组件与Parent边界之间的约束关系,实际上,就是确定彼此的相对位置。 单击View选中,然后单击并按住一个约束控制点拖拽这条线到一个可用的锚点(其他View、Layout的边缘或者引导线),当你松开,这个约束将会被创建,两个View也将被默认的margin隔开。 为了确保左右的View都被均衡的隔开,点击工具栏的Margin 去为新添加进布局的View选择一个默认的margin值。 Button将会显示你当前选择的值,你做的更改将应用于之后你新添加的View。 你也可以通过点击Properties面板线上的数字去更改margin的值。 工具中提供的margin值全是8的倍数,帮助你的View与Material Design的推荐的8dp的方形网格保持一致 基准线约束 基准线约束,使用的是『空心圆角矩形』,如图: 基准线约束,是让两个带有文本属性的组件进行对齐的,可以让两个组件的文本按照基准线进行对齐。唯一要注意的是,你需要把鼠标放在控件上,等基准线约束的图形亮了,才可以进行拖动。 约束到一个引导线(辅助线) 你可以添加一个水平和垂直方向上的引导线,这可以当做你的附加约束。你在布局内可以定位这个引导线,dp和百分比作为单位均可。 想要创建这个引导线,在工具栏点击Guidelines(如下图),然后点击Add Vertical Guidelineo或者Add Horizontal Guideline即可。 拖动引导线中间的圆即可定位引导线的位置。 当你创建一个约束的时候,一定要记得下面几点规则: 每一个View必须有两个约束:一个水平的,一个垂直的。 只有约束控制点和另外一个锚点在同一平面才能创建约束(也就是说将要创建的约束的View和锚点View属于同一级)。因此一个View的垂直平面(左侧和右侧)只能被另一个的垂直平面约束,基线只能被其他基线约束。 一个约束控制点,只能被用来创建一次约束,但是可以在同一锚点创建多个约束(来自不同的View) 如果你想要删除一个约束,先选举中View,然后点击需要删除的约束控制点即可。 使用自动连接和约束推断 自动连接(Autoconnect) 为添加进布局的View自动创建两个或者多个约束,Autoconnect 默认被禁用,你可以通过点击编辑器工具栏中的Turn on Autoconnect(一个有点像磁铁的图片)开启他。 当你开启了Autoconnect,当你添加新的View到布局之后Autoconnect就会自动创建约束,他不会为已经存在的View创建约束。如果你拖动View一次,约束就值将会改变,但是之前的约束本身不会被改变。所以如果你想重新去定位View,那么你必须删除之前的约束。 或者,你可以点击Infer Constraints(一个有点像电灯的图标)去为布局中所有的View创建约束 Infer Constraints扫描整个布局为所有的View决定一套最有效的约束,因此他可以创建两个距离很远的View之间的约束。然而Autoconnect,只能为新添加进布局的View创建约束,并且他创建的约束仅仅只能是距离最近的元素。在这两种情况下,你可以随时通过点击约束控制点去删除约束然后创建新的约束去修改他。 快速对齐Align 工具栏中有个对齐图标,可以点击按钮直接让多个控件对齐。 最后,上面一些操作还可以通过右键点击控件找到。 更多精彩请关注微信公众账号likeDev

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

Elasticsearch:加快 HNSW 图的合并速度

作者:来自 ElasticThomas Veasey及Mayya Sharipova 过去,我们曾讨论过搜索多个 HNSW 图时所面临的一些挑战,以及我们是如何缓解这些问题的。当时,我们也提到了一些计划中的改进措施。本文正是这项工作的成果汇总。 你可能会问,为什么要使用多个图?这是 Lucene 中架构选择的一个副作用:不可变段(immutable segments)。正如大多数架构决策一样,它既有优点也有缺点。例如,我们最近正式发布了无服务器版本的 Elasticsearch。在这个背景下,我们从不可变段中获得了非常显著的优势,包括高效的索引复制、索引计算与查询计算的解耦,以及它们各自的自动扩展能力。对于向量量化,段合并让我们有机会更新参数,以更好地适应数据特性。在这个方向上,我们认为通过测量数据特性并重新评估索引选择,还可以带来其他优势。 本文将探讨我们为显著减少构建多个 HNSW 图的开销,特别是为了降低图合并成本所做的工作。 背景 为了保持可管理的段(segment)数量,Lucene 会定期检查是否需要合并段。这基本上是检查当前段数量是否超过了一个由基础段大小和合并策略决定的目标段数。如果超过了这个限制,Lucene 就会将若干段合并,直到约束条件不再被违反。这个过程在其他文档中已有详细描述。 Lucene 倾向于合并相似大小的段,因为这样可以实现写放大的对数增长。对于向量索引来说,写放大指的是同一个向量被插入图的次数。Lucene 通常尝试将大约 10 个段合并成一组。因此,向量大约会被插入图 1 + 9/10 × log₁₀(n/n₀) 次,其中 n 是索引中的总向量数量,n₀ 是每个基础段的预期向量数量。由于这种对数增长,即便在非常大的索引中,写放大仍能保持在个位数。然而,合并图所花费的总时间与写放大呈线性关系。 在合并 HNSW 图时,我们已经采用了一个小的优化:保留最大段的图,并将其他段的向量插入这个图。这就是上述公式中 9/10 系数的原因。接下来我们将展示,如何通过利用所有参与合并的图中的信息,显著提高这一过程的效率。 图合并 之前,我们保留了最大的图,并插入了其他图中的向量,忽略了包含它们的图。我们在下面利用的关键见解是,每个我们丢弃的图包含了它所包含的向量的相似性信息。我们希望利用这些信息来加速插入至少一部分向量。 我们专注于将一个较小的图 插入到一个较大的图 的问题,因为这是一个原子操作,我们可以用它来构建任何合并策略。 该策略是找到一个小图中顶点的子集 来插入到大图中。然后,我们利用这些顶点在小图中的连接性来加速插入剩余的顶点 。以下我们用 和 分别表示小图和大图中顶点 的邻居。该过程的示意如下。 我们使用下文将讨论的一个过程(第1行)来计算集合 。然后,使用标准的 HNSW 插入过程(第2行)将 中的每个顶点插入到大图中。对于尚未插入的每个顶点,我们会找到其已插入的邻居及这些邻居在大图中的邻居(第 4 和第 5 行)。接着,使用以这个集合为种子的 FAST-SEARCH-LAYER 过程(第 6 行),从 HNSW 论文中的 SELECT-NEIGHBORS-HEURISTIC 中找到候选集合(第 7 行)。实际上,我们是在替换 INSERT 方法(论文中的算法1)中的 SEARCH-LAYER 过程,其他部分保持不变。最后,将刚插入的顶点加入集合 J(第8行)。 显然,为了使这个过程有效,每个 中的顶点必须至少有一个邻居在 J 中。实际上,我们要求对于每个 ,都有 ​,其中 ,即最大层连接数。我们观察到,在实际的 HNSW 图中,顶点度数的分布差异很大。下图展示了 Lucene HNSW 图底层中顶点度数的典型累计密度函数。 我们尝试了使用固定值的 ​ 和将其设为顶点度数的函数这两种方法。第二种选择带来了更大的加速,并对召回率的影响较小,因此我们选择了以下公式: 请注意, 是顶点 在小图中的度数。设定下限为 2 意味着我们将插入所有度数小于 2 的顶点。 一个简单的计数论证表明,如果我们仔细选择 ,我们只需要直接将约 的顶点插入到大图 中。具体而言,我们对图的边进行着色,如果我们将它的一个端点插入到 中。然后我们知道,为了确保 中的每个顶点都有至少 ​ 个邻居在 J 中,我们需要着色至少 条边。此外,我们预期: 这里, 是小图中顶点的平均度数。对于每个顶点 ,我们最多会着色 条边。因此,我们预期着色的总边数最多为。我们希望通过仔细选择 ,能够着色接近这个数量的边。因此,为了覆盖所有的顶点, 需要满足: 这意味着: 提供 SEARCH-LAYER 支配了运行时间,这表明我们可以在合并时间上实现最多 5 倍的加速。考虑到写入放大效应的对数增长,这意味着即使对于非常大的索引,我们的构建时间通常也仅仅是构建一个图时的两倍。 这种策略的风险在于我们可能会损害图的质量。最初,我们尝试了一个无操作的 FAST-SEARCH-LAYER。我们发现这会导致图质量下降,特别是在合并到单个段时,召回率作为延迟的函数会降低。然后我们探索了使用有限图搜索的各种替代方案。最终,最有效的选择是最简单的。使用 SEARCH-LAYER,但设置一个较低的 ef_construction。通过这种参数设置,我们能够在保持优良图质量的同时,减少约一半的合并时间。 计算连接集 找到一个好的连接集可以表述为一个图覆盖问题。贪心启发式算法是一种简单有效的启发式方法,用于逼近最优的图覆盖。我们采用的方法是使用贪心启发式算法,一次选择一个顶点将其加入到 中,使用以下定义的每个候选顶点的增益: 在这里, 表示向量 在 中的邻居数量, 是指示函数。增益包括我们加入到 中的顶点的邻居数量变化,即 ,因为通过加入一个覆盖较少的顶点,我们可以获得更多的增益。增益计算在下图中对于中央橙色节点进行了说明。 我们为每个顶点 维护以下状态: 是否过时(stale) 它的增益 在 中相邻顶点的计数,记作 一个在 [0, 1] 范围内的随机数,用于打破平局。 计算加入集合的伪代码如下。 我们首先在第 1-5 行初始化状态。 在主循环的每次迭代中,我们首先提取最大增益的顶点(第 8 行),并在存在平局时随机打破平局。在做任何修改之前,我们需要检查该顶点的增益是否过时。特别地,每次我们将一个顶点添加到 中时,会影响其他顶点的增益: 由于它的所有邻居在 中有了额外的邻居,它们的增益可能会发生变化(第14行)。 如果它的任何邻居现在已经完全覆盖,那么它们所有邻居的增益都可能发生变化(第14-16行)。 我们以懒惰的方式重新计算增益,因此只有在我们要将一个顶点插入到 中时,才会重新计算该顶点的增益(第18-20行)。 请注意,我们只需要跟踪我们已添加到 中的顶点的总增益,以决定何时退出。此外,尽管 ,至少会有一个顶点具有非零增益,因此我们始终会取得进展。 结果 我们在4个数据集上进行了实验,涵盖了我们支持的三种距离度量(欧几里得、余弦和内积),并采用了两种类型的量化方法:1)int8 —— 每个维度使用1字节的整数;2)BBQ —— 每个维度使用一个比特。 实验 1:int8 量化 从基准到候选方案(提出的改进)的平均加速比为: 索引时间加速:1.28× 强制合并加速:1.72× 这对应于以下运行时间的划分: 为了完整性,以下是各项时间数据: quora-E5-small; 522931 文档;384 维度;余弦度量 基准:索引时间:112.41s,强制合并:113.81s 候选:索引时间:81.55s,强制合并:70.87s cohere-wikipedia-v2; 100万文档;768 维度;余弦度量 基准:索引时间:158.1s,强制合并:425.20s 候选:索引时间:122.95s,强制合并:239.28s gist; 960 维度,100万文档;欧几里得度量 基准:索引时间:141.82s,强制合并:536.07s 候选:索引时间:119.26s,强制合并:279.05s cohere-wikipedia-v3; 100万文档;1024 维度;内积度量 基准:索引时间:211.86s,强制合并:654.97s 候选:索引时间:168.22s,强制合并:414.12s 下面是与基准进行对比的召回率与延迟图,比较了候选方案(虚线)与基准在两种检索深度下的表现:recall@10 和 recall@100 ,分别针对具有多个分段的索引(即索引所有向量后默认合并策略的最终结果)以及强制合并为单个分段的索引。曲线越高且越靠左表示性能越好,这意味着在较低的延迟下获得更高的召回率。 从下面的图表可以看出,在多个分段索引上,候选方案在 Cohere v3 数据集上的表现更好,对于其他所有数据集,曲线稍差但几乎可比。当合并为单个分段后,所有数据集的召回曲线几乎相同。 实验 2:BBQ 量化 从基准到候选方案的平均加速比为: 索引时间加速:1.33× 强制合并加速:1.34× 以下是具体的细分: 为完整性考虑,以下是时间数据: quora-E5-small; 522931 个文档; 384 维度; 余弦度量 基准:索引时间:70.71秒,强制合并:59.38秒 候选方案:索引时间:58.25秒,强制合并:40.15秒 cohere-wikipedia-v2; 100 万个文档; 768 维度; 余弦度量 基准:索引时间:203.08秒,强制合并:107.27秒 候选方案:索引时间:142.27秒,强制合并:85.68秒 gist; 960 维度,100 万个文档; 欧氏度量 基准:索引时间:110.35秒,强制合并:323.66秒 候选方案:索引时间:105.52秒,强制合并:202.20秒 cohere-wikipedia-v3; 100 万个文档; 1024 维度; 内积度量 基准:索引时间:313.43秒,强制合并:165.98秒 候选方案:索引时间:190.63秒,强制合并:159.95秒 从多个段索引的图表可以看到,候选方案在几乎所有数据集上表现更好,除了 cohere v2 数据集,基准略优。对于单个段索引,所有数据集的召回曲线几乎相同。 总的来说,我们的实验涵盖了不同的数据集特征、索引设置和检索场景。实验结果表明,我们能够在保持强大的图质量和搜索性能的同时,实现显著的索引和合并加速,这些结果在各种测试场景中表现一致。 结论 本文讨论的算法将在即将发布的 Lucene 10.2 版本中提供,并将在基于该版本的 Elasticsearch 发布中可用。用户将在这些新版本中受益于改进的合并性能和缩短的索引构建时间。这个变化是我们不断努力使 Lucene 和 Elasticsearch 成为一个快速高效的向量搜索数据库的一部分。 自己亲自体验向量搜索,通过这款自定进度的搜索 AI 实践学习。你可以开始免费的云试用或立即在本地机器上尝试 Elastic。 原文:Speeding up merging of HNSW graphs - Elasticsearch Labs

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

Android Studio Chipmunk 发布,加快构建速度

Android Studio 是 Android 开发的官方 IDE,包括构建 Android 应用程序所需的一切。近日 Android 团队正式发布了 Android Studio Chipmunk 的稳定版本。这个版本是一个较小的功能版本,但包含了最新的 IntelliJ 更新,并在质量和稳定性方面做出了改善,仅在这个版本中就解决了超过 175 个质量问题。 Compose Animation 预览 这个之前的实验性功能现在可以让 Jetpack Compose 的开发者检查和调试他们用 Compose 构建的动画。如果一个动画在可合成的预览中被描述,你可以检查每个动画值在特定时间的准确值,暂停动画、循环、快进或放慢。这对于逐帧比较动画与它们的设计规格特别有用。 Compose Animation Preview 目前支持AnimatedVisibility 和 updateTransition。它将在未来支持更多的动画类型。 CPU Profiler Android Studio Chipmunk 现在显示更新的 jank 信息,包括 jank 类型以及预期和实际期限,帮助你发现 jank 的实际原因。当你使用 Android Emulator 或 API 级别为 31 或更高的设备时(Android 12 或以上),该 jank 信息可用。 在 CPU 分析器中显示 Jank 信息 Build Analyzer:检查 Jetifier 在 Chipmunk 中为 Build Analyzer 中引入了一个新的 Jetifier 检查,它将通知你是否可以删除 Jetifier 标志以提高构建过程中的性能。 Jetifier 标志是为了自动迁移第三方库以使用 AndroidX 而设计的,绝大多数 Android Studio 项目仍然启用了它。然而库的生态已经大部分转移到了对 AndroidX 的原生支持,现在有这个标志通常会增加不必要的构建开销,关闭它通常会节省 5-10% 的构建时间。 IntelliJ 平台更新 虽然 Android Studio Chipmunk 的 Android 特定功能数量不多,但它包括了 IntelliJ 2021.2 平台的主要版本,其中有许多新功能。 更多详情可查看:https://android-developers.googleblog.com/2022/05/android-studio-chipmunk.html

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

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等操作系统。