This Indicator Shows CPU, GPU, Memory Usage on Ubuntu 22.04 Panel

There are several Gnome Shell extensions to display system resource usage in Ubuntu, but in this tutorial I’m going to introduce an indicator that works in not only GNOME, but also UnityMATE, and Budgie desktop environments.

It’s Indicator-SysMonitor, a free and open-source applet developed by the leader of Ubuntu Budgie team.

With it, user can display the usage and/or temperature of the following system resource in top-panel:

  • average CPU usage.
  • NVIDIA GPU utilization.
  • Memory usage.
  • network upload/download speed.
  • CPU, NVIDIA GPU temperature.
  • Swap usage.
  • Public IP address.

Most important is that user can customize the output, by setting which one or ones to display, in which order with which text. User just need to click the indicator on panel to open ‘Preferences’ dialog from pop-down menu, and format the output code in ‘Advanced’ tab.

How to Install Indicator-Sysmonitor

UPDATE: This app is NOT updated for almost one year! It does NOT work anymore in my case in Ubuntu 24.04!

The developer has an Ubuntu PPA contains the packages for Ubuntu 18.04, Ubuntu 20.04, Ubuntu 22.04, Ubuntu 22.10, and even the next Ubuntu 23.04.

1. First, press Ctrl+Alt+T on keyboard to open a terminal window. When it opens, run command to add the PPA:

Type user password when it asks and hit Enter to continue.

2. For the old Ubuntu 18.04, you need to manually refresh package index after adding PPA:

3. And, install the indicator applet via command:

Finally, search for and open the applet like a normal application (it has same icon to System Monitor).

And click on the applet to open Preferences, and turn on start at login, configure output layout, refresh interval, etc.

Uninstall Indicator-Sysmonitor

You can close the applet by clicking on it in panel and select “Quit”. And remove the package at any time by running a single command in terminal window:

Also remove the PPA repository, either by running the command below or open “Software & Updates”and remove source line under “Other Software” tab.

参考链接


This Indicator Shows CPU, GPU, Memory Usage on Ubuntu 22.04 Panel

Disable Nagle’s Algorithm with OkHttp or Scarlet

Android 客户端使用 OKHTTP 的时候,需要通过修改底层的 Socket 来解决问题,比如设置 TCP 的心跳、修改TCP 的滑动窗口算法等。

相关的代码参考如下:

TCP includes a feature called Nagle’s Algorithm that will postpone sending small messages, in hopes of combining them with future messages. This can save bandwidth, because TCP has to include additional information along with each segment. Combining multiple small messages into a single segment reduces the overall bandwidth used. However, this increases the time it will take for some small messages to be received.

If you want all of your messages to be sent with the lowest latency possible, and you are willing to use additional bandwidth, you should disable Nagle’s Algorithm. WebSocket connections made with OkHttp have Nagle’s Algorithm enabled by default. There was an issue about this back in 2013, and the fix was to add the socketFactory and sslSocketFactory options.

To disable Nagle’s Algorithm using these options, you have to create classes that extend SocketFactory and SSLSocketFactory and call setTcpNoDelay(true) on their underlying sockets. You can use the following classes I created, which use composition:

SocketFactory

SSLSocketFactory

Putting It Together

You can create an OkHttpClient that uses these classes as follows:

WebSocket connections that your OkHttpClient makes will have Nagle’s Algorithm disabled.

Scarlet

Scarlet is a library from Tinder that uses OkHttp for WebSocket connections. If you pass the OkHttpClient that you created above to OkHttpClientUtils#newWebSocketFactory, all WebSocket connections made with Scarlet will have Nagle’s Algorithm disabled.

Note that all Scarlet WebSocket connections use OkHttp. As of writing this post, Scarlet is still using OkHttp version 3.11. OkHttp 4 has been out for over a year. If you are starting a new project, consider using OkHttp directly, rather than using Scarlet.

参考链接


Disable Nagle’s Algorithm with OkHttp or Scarlet

矩阵分解的算法实现:C++的Armadillo库与Eigen库

1.背景介绍

矩阵分解是一种常见的矩阵分析方法,主要用于处理高维数据的降维和特征提取。在现代数据挖掘和机器学习领域,矩阵分解技术被广泛应用于推荐系统、图像处理、文本摘要等方面。本文将介绍如何使用 C++Armadillo 库和 Eigen 库实现矩阵分解算法,并详细解释其核心原理、数学模型以及具体操作步骤。

1.1 矩阵分解的基本概念

矩阵分解是指将一个矩阵分解为多个较小的矩阵的过程。这些较小的矩阵通常具有一定的结构或特点,可以帮助我们更好地理解和处理原始矩阵。矩阵分解的主要目的是将复杂的高维数据降维,以便更容易地进行分析和处理。

常见的矩阵分解方法有非负矩阵分解(NMF)、奇异值分解(SVD)、高斯混合模型(GMM)等。这些方法各自具有不同的优势和局限性,适用于不同类型的数据和问题。

1.2 Armadillo库和Eigen库的简介

Armadillo 是一个 C++ 的数值计算库,提供了丰富的数据结构和算法实现,可以方便地处理向量、矩阵和高维数据。Armadillo 库支持各种线性代数计算、优化问题解决、随机数生成等功能,是一个强大的 C++ 数值计算工具。

Eigen 库是一个 C++ 的线性代数库,专注于高效的矩阵计算和求解线性方程组。Eigen 库提供了丰富的矩阵类和操作函数,支持各种基本线性代数操作、高级线性代数结构和求解线性方程组等功能。

在本文中,我们将使用 Armadillo 库和 Eigen 库实现矩阵分解算法,并详细解释其核心原理、数学模型以及具体操作步骤。

2.核心概念与联系

2.1 矩阵分解的核心概念

矩阵分解的核心概念包括:

  1. 矩阵:矩阵是由行向量组成的有序列。矩阵可以用来表示高维数据、系数、权重等信息。

  2. 矩阵分解:将一个矩阵分解为多个较小矩阵的过程。这些较小矩阵通常具有一定的结构或特点,可以帮助我们更好地理解和处理原始矩阵。

  3. 降维:矩阵分解的一个重要应用是降维,即将高维数据降至低维数据,以便更容易地进行分析和处理。

  4. 特征提取:矩阵分解还可以用于特征提取,即从原始矩阵中提取出具有代表性的特征,以便进行更精确的分类、聚类等分析。

2.2 Armadillo 库和 Eigen 库与矩阵分解的联系

Armadillo 库和 Eigen 库都是 C++ 的数值计算库,提供了丰富的数据结构和算法实现,可以方便地处理向量、矩阵和高维数据。这两个库在矩阵分解算法实现中发挥着重要作用,主要体现在以下几个方面:

  1. 数据结构:Armadillo 库和 Eigen 库提供了丰富的矩阵类和操作函数,可以方便地创建、操作和处理矩阵数据。

  2. 线性代数计算:这两个库提供了各种线性代数计算函数,如矩阵乘法、逆矩阵、求解线性方程组等,可以方便地实现矩阵分解算法中的核心计算。

  3. 高级线性代数结构:Armadillo 库和 Eigen 库支持各种高级线性代数结构,如对称矩阵、正交矩阵、特征分解等,可以帮助我们更好地理解和处理矩阵分解算法。

3.核心算法原理和具体操作步骤以及数学模型公式详细讲解

3.1 非负矩阵分解(NMF)算法原理

非负矩阵分解(NMF)是一种常见的矩阵分解方法,目标是将一个非负矩阵分解为两个非负矩阵的乘积。NMF 的核心思想是将一个矩阵分解为低维空间中的线性组合,从而实现数据的降维和特征提取。

NMF 的主要优势在于它可以处理非负数据,并且可以找到非负的基元素,这有助于解释和解释数据的特征。NMF还具有稀疏表示的优势,可以用于处理稀疏数据。

3.2 奇异值分解(SVD)算法原理

奇异值分解(SVD)是一种常见的矩阵分解方法,目标是将一个矩阵分解为三个矩阵的乘积。SVD 的核心思想是将一个矩阵分解为低维空间中的线性组合,从而实现数据的降维和特征提取。

SVD 的主要优势在于它可以处理正定矩阵,并且可以找到正定的基元素,这有助于解释和解释数据的特征。SVD 还具有稀疏表示的优势,可以用于处理稀疏数据。

3.3 矩阵分解算法的数学模型公式
3.3.1 非负矩阵分解(NMF)

假设给定一个非负矩阵 ARm×n,目标是将其分解为两个非负矩阵 WRm×rHRr×n 的乘积,即:

AWH

其中 r 是隐含因素的数量,W 表示特征矩阵,H 表示权重矩阵。

NMF的目标是最小化以下目标函数:

minW,Hi=1mj=1n(aijk=1rwikhjk)2

3.3.2 奇异值分解(SVD)

假设给定一个矩阵 ARm×n,目标是将其分解为三个矩阵 URm×rΣRr×rVTRn×r 的乘积,即:

AUΣVT

其中 U 表示左特征向量矩阵,Σ 表示对角矩阵的奇异值,VT 表示右特征向量矩阵。

SVD的目标是最小化以下目标函数:

minU,V||AUΣVT||F2

其中 ||||F 表示矩阵的弱F范数。

3.4 矩阵分解算法的具体操作步骤
3.4.1 非负矩阵分解(NMF)
  1. 初始化 WH 为非负随机矩阵。

  2. 使用梯度下降法或其他优化算法最小化目标函数。

  3. 更新 WH,直到收敛或达到最大迭代次数。

  4. 返回 WH

3.4.2 奇异值分解(SVD)
  1. 对矩阵 A 进行奇异值分解,得到 UΣV

  2. Σ 的非零奇异值存储在一个向量中。

  3. 返回 UΣV

4.具体代码实例和详细解释说明

4.1 非负矩阵分解(NMF)代码实例

4.2 奇异值分解(SVD)代码实例

5.未来发展趋势与挑战

矩阵分解技术在现代数据挖掘和机器学习领域具有广泛的应用前景,未来的发展趋势和挑战主要包括:

  1. 高效算法:随着数据规模的增加,矩阵分解算法的计算复杂度和运行时间将成为主要挑战。未来的研究需要关注如何提高矩阵分解算法的效率和并行性,以应对大规模数据处理的需求。

  2. 多模态数据处理:未来的矩阵分解技术需要能够处理多模态数据,如文本、图像、音频等。这将需要结合多种数据处理技术,并开发新的矩阵分解算法来处理不同类型的数据。

  3. 深度学习与矩阵分解的融合:深度学习技术在近年来取得了显著的进展,但与矩阵分解技术的结合仍然存在挑战。未来的研究需要关注如何将矩阵分解技术与深度学习技术相结合,以提高深度学习模型的性能和解释性。

  4. 解释性和可视化:矩阵分解技术的一个主要优势是它可以提供数据的解释性和可视化。未来的研究需要关注如何提高矩阵分解技术的解释性,以帮助用户更好地理解和利用分解结果。

6.附录常见问题与解答

Q: 矩阵分解与主成分分析(PCA)有什么区别?

A: 矩阵分解是将一个矩阵分解为多个较小矩阵的过程,目标是实现数据的降维和特征提取。主成分分析(PCA)是一种线性变换技术,目标是将原始数据变换为新的特征空间,使得新的特征具有最大的方差。矩阵分解和 PCA 都是用于数据降维和特征提取的方法,但它们的具体算法和实现方法有所不同。

Q: 矩阵分解与奇异值分解(SVD)有什么区别?

A: 矩阵分解是一种更一般的方法,可以将一个矩阵分解为多个较小矩阵的乘积,如非负矩阵分解(NMF)。奇异值分解(SVD)是矩阵分解的一种特殊实现,将一个矩阵分解为三个矩阵的乘积,即左特征向量矩阵、奇异值矩阵和右特征向量矩阵。奇异值分解是矩阵分解的一个具体实现,但矩阵分解可以包括其他实现。

Q: 如何选择矩阵分解算法?

A: 选择矩阵分解算法时,需要考虑数据类型、数据规模、计算资源等因素。如果数据是非负的,可以选择非负矩阵分解(NMF)算法。如果数据是正定矩阵,可以选择奇异值分解(SVD)算法。此外,还可以根据算法的计算复杂度、并行性和实现难度等因素进行选择。在实际应用中,可以尝试不同算法,并通过验证结果和性能来选择最佳算法。

参考链接


矩阵分解的算法实现:C++的Armadillo库与Eigen库

2024年11月论文-论DevOps开发

1、题目要求

论Devops及其应用。Devops是一组过程、方法与系统的统称,用于促进开发、技术运营和质量保障部门之间的沟通,协作与整合。它是一种重视软体开发人员和工厂运维技术人员之间沟通合作的模式。透过自动化“软件交付”和“架构变更”的流程,使得构建、测试,发布软件能够更加快捷、频繁和可靠。请围绕“Devops及其应用"论题,依次从以下三个方面进行论述。

概要叙述你参与管理和开发的软件项目,以及你在其中担任的主要工作.

结合你具体参与管理和开发的的实际项目,详细给述是哪些因素促使你决定引入Devops

结合你具体参与管理和开发的实际项目中如何引入DevOps

注:实际的论文题目内容与上述描述有较大出入,但本质上都是要求在项目中引入DevOps开发及自动化运维的过程,侧重于开发、管理。

2、考点整理

DevOps主要阶段及工作内容
1. 计划阶段

在项目启动时,将安全目标融入需求分析与规划中。团队通过威胁建模评估系统可能存在的风险,并制定应对策略。此阶段工作包括:

  • 确定安全需求,设计整体安全框架;
  • 识别潜在威胁,建立威胁模型;
  • 将安全目标与开发目标统一规划。
2. 开发阶段

开发阶段将安全审查融入编码实践中,通过自动化工具进行代码扫描和漏洞检测,确保每一行代码的安全性。具体任务包括:

  • 在代码仓库集成静态代码分析工具;
  • 开展安全编码培训,提升开发人员的安全意识;
  • 定期执行代码审查,发现并修复潜在漏洞。
3. 构建与测试阶段

在持续集成/持续交付(CI/CD)流水线中增加安全测试环节,确保构建的代码包满足安全要求。主要工作有:

  • 集成动态应用安全测试(DAST)工具;
  • 编写安全测试用例,对功能模块进行渗透测试;
  • 设置质量门禁,阻止存在高危漏洞的代码部署。
4. 部署阶段

通过基础设施即代码(IaC)技术构建安全的部署环境,确保基础设施配置符合安全标准。任务包括:

  • 使用IaC工具(如Terraform、Ansible)部署安全环境;
  • 对容器化环境(如Docker、Kubernetes)实施安全基线检测;
  • 配置网络隔离和访问控制策略,保护系统资源。
5. 运维阶段

在运维阶段,实时监控系统运行状态,并通过日志分析与安全警报工具快速响应安全事件。工作内容包括:

  • 部署安全信息与事件管理(SIEM)系统;
  • 定期更新和修复已知漏洞;
  • 模拟安全事件,完善应急响应计划。

3、论文部分

以下仅提供论文写作思路,参考。

3.1. 决定引入DevOps的主要因素

结合具体项目的特点和挑战,引入DevOps的决定因素如下:

3.1.1 频繁的需求变更

客户因业务调整频繁提出新需求,传统开发模式中需求变更需等待当前阶段结束后处理,响应时间长,且易导致累积风险。DevOps中的持续交付(Continuous Delivery)可以实现频繁部署,快速响应业务变化。

3.1.2 低效的部署流程

每次上线需耗费大量时间进行手动操作,包括代码打包、环境配置和部署测试,效率低且错误率高。自动化部署工具(如 Jenkins 和 Ansible)可以将这些环节流程化,大幅提升效率。

3.1.3 开发与运维的沟通壁垒

开发团队关注功能实现,而运维团队关注系统稳定性,两者缺乏有效协作。在项目中曾因日志配置问题导致系统上线后频繁宕机,问题排查耗时数日,影响了客户的使用体验。DevOps强调开发与运维的一体化协作,可以从根本上解决此类问题。

3.1.4 测试覆盖不足

传统开发模式中,测试通常集中在上线前进行,测试时间短且覆盖面不足,导致上线后问题频发。DevOps中的持续集成(Continuous Integration)将测试融入开发过程,确保每次代码变更都经过充分测试,从而提高代码质量。

3.1.5 系统复杂性增加

随着项目扩展,系统组件数量增加,传统模式下环境配置复杂且易出错。基础设施即代码(Infrastructure as Code, IaC)技术通过代码化管理环境配置,解决了环境不一致的问题。

3.2. 引入DevOps的实施过程
3.2.1 初步规划与目标设定

在项目的开发中期,笔者组织各团队成员分析当前开发与运维的痛点,结合项目需求明确了引入DevOps的目标:

  • 提升部署效率:实现自动化构建与部署,缩短交付周期;
  • 保障代码质量:通过自动化测试发现潜在问题;
  • 优化协作流程:增强开发、测试与运维的协作性。
3.2.2 工具链搭建与环境准备

结合项目需求,选择并配置了以下工具:

  • 版本控制:Git 用于代码管理,分支策略采用 GitFlow;
  • 持续集成/持续交付(CI/CD):Jenkins 用于搭建构建、测试与部署流水线;
  • 容器化与编排:使用 Docker 容器化各服务,Kubernetes 负责容器编排;
  • 自动化配置管理:通过 Ansible 自动化配置测试和生产环境;
  • 监控与日志:使用 ELK(Elasticsearch, Logstash, Kibana)实现日志分析与监控。
3.2.3 持续集成(CI)的实施

持续集成的主要工作包括:

  • 代码质量检查:每次代码提交后,Jenkins 自动运行 SonarQube 进行静态代码分析;
  • 自动化测试:构建流水线中集成了单元测试和集成测试,确保代码变更不会破坏现有功能;
  • 构建与制品管理:构建通过后,将制品存储到 Nexus 仓库,便于后续部署。
3.2.4 持续交付(CD)的实现

流水线的持续交付部分负责将构建后的制品部署到测试环境或生产环境:

  • 测试环境部署:每次提交代码后,Jenkins 自动将服务部署到测试环境并运行回归测试;
  • 生产环境部署:采用蓝绿部署模式,确保新版本上线过程中不会影响用户使用。
3.2.5 文化转型与团队协作

DevOps的成功实施离不开团队文化的转变。推动以下举措以优化团队协作:

  • 跨部门培训:组织开发与运维团队共同学习 DevOps 工具与实践;
  • 定期沟通会议:建立每日站会机制,分享进展与问题;
  • 职责重新分配:开发人员负责容器化配置,运维人员参与代码评审,促进角色融合。
3.3. DevOps实施的效果

引入DevOps后,项目的开发与交付流程得到了显著优化:

3.3.1 部署效率提升

自动化部署减少了人为操作,部署时间从1天缩短至1小时,每周可实现多次上线。

3.3.2 代码质量提高

通过自动化测试,代码缺陷发现率提高了40%,上线后问题数量减少了60%。

3.3.3 协作效率优化

开发与运维之间的沟通更加顺畅,问题解决时间减少了50%。

3.3.4 系统稳定性增强

借助监控与日志工具,系统异常能够被实时发现并快速处理,平均故障恢复时间从4小时减少到30分钟。

参考链接


树莓派5使用PCIE接口连接NVME M.2固态硬盘SSD

固态硬盘选型

目前测试可用的是雷克沙(Lexar) PLAY 1TB SSD 固态硬盘 2230 M.2接口

固态硬盘安装

New NVMe SSDs are not partitioned and will need to be both partitioned and formatted when first connected to the Raspberry Pi before they will be accessed in the Explorer.

树莓派系统配置

Enable the external PCIe port on the Raspberry Pi 5. Edit /boot/firmware/config.txt and add the following at the bottom:

修改后的完整内容:

开机自动挂载

查看磁盘分区:

增加挂载项:

参考链接


搭建Docker Registry私有仓库

一、概述

本文将详细介绍如何在本地搭建一个 Docker 私有 Registry 仓库,并实现镜像的上传和管理。通过私有仓库,可以方便地在本地网络中存储和分发 Docker 镜像,提高开发和部署效率。

二、搭建步骤

(一)下载Docker Registry镜像

1. 命令:

2. 解析:

  • docker pull 是 Docker 的命令,用于从 Docker Hub 下载指定的镜像。
  • registry 是 Docker 官方提供的 Registry 镜像,用于搭建私有仓库。
  • 默认情况下,docker pull 会下载最新版本的镜像(latest 标签)。
(二)启动Docker Registry容器

1. 命令:

2. 解析:

  • -d:表示以守护进程模式运行容器(后台运行)。
  • -p 5000:6000:将容器的 5000 端口映射到宿主机的 6000 端口,用于访问私有仓库。
  • --restart=always:设置容器在退出后自动重启,确保仓库服务始终可用。
  • --name registry:为容器指定名称 registry,便于后续管理。
  • registry:指定使用的镜像名称(即之前下载的 registry 镜像)。

3. 验证容器是否启动成功:

如果看到名为 registry 的容器正在运行,并且端口映射正确,说明启动成功。

(三)配置Docker客户端以信任私有仓库

由于私有仓库默认使用 HTTPS 协议,Docker 客户端需要配置为信任该仓库。

编辑 /etc/docker/daemon.json 文件:

添加以下内容:

insecure-registries:指定不安全的仓库地址,允许 Docker 客户端通过 HTTP 协议访问该地址。

重启 Docker 服务:

重启 Docker 服务后,配置生效。

如果使用的是 Docker Desktop 则参考下图修改:

(四)上传镜像到私有仓库

此部分功能暂时不能使用 Podman 替代

1. 标记镜像:

  • docker image tag:用于为镜像重新标记一个新的名称和标签。
  • nginx:latest:本地已有的镜像名称和标签。
  • 10.10.10.189:6000/docker.io/library/nginx:latest:目标仓库地址和镜像名称。

2. 推送镜像到私有仓库:

  • docker push:将标记后的镜像推送到指定的仓库地址。
  • 如果推送成功,会显示镜像层的上传进度和最终的摘要信息。
(五)验证镜像是否上传成功

通过 API 查看仓库中的镜像:

三、skopeo 简化多架构同步

四、常见问题及解决方法

(一)无法连接到私有仓库

1. 问题描述:

在推送或拉取镜像时,可能会遇到以下错误:

2. 解决方法:

  • 确保 /etc/docker/daemon.json 文件中正确配置了 insecure-registries。
  • 确保 Docker 服务已重启。
  • 确保仓库地址正确,且防火墙允许访问 6000 端口。
(二)网络问题导致无法解析地址

1. 问题描述: 如果尝试访问 http://10.10.10.189:6000 或 https://10.10.10.189:6000/v2/ 时,可能会遇到解析失败的问题。

2. 解决方法:

  • 检查仓库地址是否正确,确保 IP 地址和端口无误。
  • 确保网络连接正常,可以尝试 ping 或 curl 测试连通性。
  • 如果问题仍然存在,可能是网络配置或防火墙限制,建议检查网络设置或联系网络管理员。
(三)是否可以使用 Podman 替代

目前测试 push 镜像功能是有问题的,在推送的时候会报错,其他功能都是正常的。

如下:

使用 docker 执行上述命令不会报错。

参考链接


通过共享内存优化Flutter外接纹理的渲染性能,实时渲染不是梦

前言

看了咸鱼这篇《万万没想到——flutter这样外接纹理》的文章,我们了解到 Flutter 提供一种机制,可以将 Native 的纹理共享给 Flutter 来进行渲染。但是,由于 Flutter 获取 Native 纹理的数据类型是 CVPixelBuffer,导致 Native 纹理需要经过 GPU->CPU->GPU 的转换过程消耗额外性能,这对于需要实时渲染的音视频类需求,是不可接受的。

闲鱼这边的解决方案是修改了 Flutter Engine 的代码,将 FlutterGL 环境和 nativeGL 环境通过 ShareGroup 来联通,避免2个环境的纹理传递还要去 CPU 内存绕一圈。此方案能够解决内存拷贝的性能问题,但暴露 FlutterGL 环境,毕竟是一个存在风险的操作,给以后的 Flutter 渲染问题定位也增加了复杂度。所以,有没有一个完美、简便的方案呢?答案就是利用 CVPixelBuffer 的共享内存机制。

Flutter外接纹理的原理

先回顾下前置知识,看看官方提供的外接纹理机制究竟是怎样运行的。

图中红色块,是我们自己要编写的 Native 代码,黄色是 Flutter Engine 的内部代码逻辑。整体流程分为注册纹理,和整体的纹理渲染逻辑。

注册纹理
  1. 创建一个对象,实现FlutterTexture协议,该对象用来管理具体的纹理数据
  2. 通过FlutterTextureRegistry来注册第一步的FlutterTexture对象,获取一个flutter纹理id
  3. 将该id通过channel机制传递给dart侧,dart侧就能够通过Texture这个widget来使用纹理了,参数就是id
纹理渲染
  1. dart侧声明一个Texture widget,表明该widget实际渲染的是native提供的纹理
  2. engine侧拿到layerTree,layerTree的TextureLayer节点负责外接纹理的渲染
  3. 首先通过dart侧传递的id,找到先注册的FlutterTexture,该flutterTexture是我们自己用native代码实现的,其核心是实现了copyPixelBuffer方法
  4. flutter engine调用copyPixelBuffer拿到具体的纹理数据,然后交由底层进行gpu渲染

CVPixelBuffer格式分析

一切问题的根源就在这里了:CVPixelBuffer。从上面flutter外接纹理的渲染流程来看,native纹理到flutter纹理的数据交互,是通过 copyPixelBuffer 传递的,其参数就是 CVPixelBuffer。而前面咸鱼文章里面说的性能问题,就来自于纹理与 CVPixelBuffer 之间的转换。

那么,如果 CVPixelBuffer 能够和OpenGL的纹理同享同一份内存拷贝,GPU -> CPU -> GPU的性能瓶颈,是否就能够迎刃而解了呢?其实我们看一下flutter engine里面利用CVPixelBuffer来创建纹理的方法,就能够得到答案:

Flutter Engine是使用 CVOpenGLESTextureCacheCreateTextureFromImage 这个接口来从 CVPixelBuffer 对象创建OpenGL纹理的,那么这个接口实际上做了什么呢?我们来看一下官方文档

This function either creates a new or returns a cached CVOpenGLESTextureRef texture object mapped to the CVImageBufferRef and associated parameters. This operation creates a live binding between the image buffer and the underlying texture object. The EAGLContext associated with the cache may be modified to create, delete, or bind textures. When used as a source texture or GL_COLOR_ATTACHMENT, the image buffer must be unlocked before rendering. The source or render buffer texture should not be re-used until the rendering has completed. This can be guaranteed by calling glFlush().

从文档里面,我们了解到几个关键点:

  1. 返回的纹理对象,是直接映射到了CVPixelBufferRef对象的内存的
  2. 这块buffer内存,其实是可以同时被CPU和GPU访问的,我们只需要遵循如下的规则:
    • GPU访问的时候,该 CVPixelBuffer ,不能够处于lock状态。
      使用过pixelbuffer的同学应该都知道,通常CPU操作pixelbuffer对象的时候,要先进行lock操作,操作完毕再unlock。所以这里也容易理解,GPU使用纹理的时候,其必然不能够同时被CPU操作。
    • CPU访问的时候,要保证GPU已经渲染完成,通常是指在 glFlush() 调用之后。
      这里也容易理解,CPU要读写这个buffer的时候,要保证关联的纹理不能正在被OpenGL渲染。

我们用instrument的allocation来验证一下:

instrument的结果,也能够印证文档中的结论。 只有在创建pixelBuffer的时候,才分配了内存,而映射到纹理的时候,并没有新的内存分配。

这里也能印证我们的结论,创建pixelBuffer的时候,才分配了内存,映射到纹理的时候,并没有新的内存分配。

共享内存方案

既然了解到CVPixelBuffer对象,实际上是可以桥接一个OpenGL的纹理的,那我们的整体解决方案就水到渠成了,可以看看下面这个图

关键点在于,首先需要创建pixelBuffer对象,并分配内存。然后在native gl环境和flutter gl环境里面分别映射一个纹理对象。这样,在2个独立的gl环境里面,我们都有各自的纹理对象,但实际上其内存都被映射到同一个CVPixelBuffer上。在实际的每一帧渲染流程里面,native环境做渲染到纹理,而flutter环境里面则是从纹理读取数据。

Demo演示

这里我写了个小demo来验证下实际效果,demo的主要逻辑是以60FPS的帧率,渲染一个旋转的三角形到一个pixelBuffer映射的纹理上。然后每帧绘制完成之后,通知 Flutter 侧来读取这个pixelBuffer对象去做渲染。

核心代码展示如下:

关键代码都添加了注释,这里就不分析了

我们从上面的gif图上可以看到整个渲染过程是十分流畅的,最后看displayLink的帧率也能够达到60FPS。该demo是可以套用到其他的需要CPU与GPU共享内存的场景的。

完整的demo代码在这里flutter_texture

参考链接


通过共享内存优化flutter外接纹理的渲染性能,实时渲染不是梦

ubuntu 24.04编译CEF(Chromium Embedded Framework)

前置条件

1. 已经通过 GitLab的替代者-轻量级Gitea安装与配置-Windows 11 配置过镜像服务

基础知识

CIPD 全称 Chrome Infrastructure Package Deployment (https://chromium.googlesource.com/infra/luci/luci-go/+/main/cipd/README.md),主要用于管理 Google 项目构建中用到的二进制文件(例如编译器之类的),你可以简单的认为是针对大文件的git系统。

CIPD管理的所有文件可以在(https://chrome-infra-packages.appspot.com)上查看。

手动下载( version 参数从 depot_tools/cipd_client_version 读取)

linux-x86-64:
https://chrome-infra-packages.appspot.com/client?platform=linux-amd64&version=git_revision:b1f414539ac10cc67a0250890a38712cc06cf102

windows-x86-64:
https://chrome-infra-packages.appspot.com/client?platform=windows-amd64&version=git_revision:b1f414539ac10cc67a0250890a38712cc06cf102

动手实践

外网构建

国内镜像构建
1. 使用镜像地址替换 Git 仓库

命令执行后的 .gitconfig 文件:

2. 配置HTTP代理服务器

由于 Chromium 项目不仅依赖了一系列的第三方 Git 项目,还依赖了一系列已经编译后的二进制文件,这些文件被托管在 CIPD (Chrome Infrastructure Package Deployment) / GCS(Google Cloud Storage) 服务器上,这部分服务器也是没办法正常访问下载文件的,因此我们把这些二进制文件依旧托管在刚刚搭建的 Gitea 服务器上面,作为一个独立的 Git 项目来维护,减少非必要的服务器。

但是如何更改这部分的下载地址呢?我们可以通过设置 http_proxy 的方式,使用 Python 脚本,在脚本中完成下载数据的重定向。

代理脚本如下:

参考配置文件如下:

3. 执行编译

参考链接


Windows 11系统怎么清理Windows.old文件夹?

Windows.old 就是指电脑上在重装系统的历程中,备份数据旧系统中关键文件的文件夹,Windows.old 一般而言用途并不大,可是它坐落于新系统的C盘中很占用室内空间,因而就需要将其删除, Windows.old有着一些独特的管理权限,促使它无法同时在資源管理器里被删除,可能是为了避免用户的误操作。该怎么删除呢?下面大家就一起来看看 Windows 11 删除Windows.old 文件夹的操作。

清理Windows.old文件夹的方法:进到开始菜单,开启设置,进到系统,进入存储,进到清理建议,勾选之前的 Windows 安装文件后,点击清理就可以。

进到开始菜单,点击任务栏四宫图标。

进入设置

进到系统

进入存储

进到清理建议。待扫描进行后,进到清理建议。

清理Windows.old文件夹

勾选之前的Windows安装文件后,点击清理就可以。

参考链接


win11系统,怎么清理Windows.old文件夹?win11清理文件夹操作步骤

GitLab的替代者-轻量级Gitea安装与配置-Windows 11

前置条件
  • Windows 11 专业版 24H2
  • podman desktop 1.18.1 x64
  • Gitea 1.23.8
动手实践

Windows 的本质是是安装一个 Linux 虚拟机(当前是 Fedora Linux ),然后在虚拟机中安装 Gitea ,所以会出现后面需要进行端口数据转发。

后续如果想修改配置文件,那么需要通过 podman machine ssh 进入容器进行修改。

另外虚拟机整个被默认WSL2 挂载到  \\wsl.localhost\ 目录下,可以直接在 Windows 系统下访问,但是修改的时候可能会没有权限,还是需要通过命令行修改。

如果 Podman Desktop 没有开机自启动,确认如下如下配置是否已经勾选:

注意:如果对大型项目进行镜像部署,比如 chromium 。登录之前可以正常使用,登录特别慢,注意观察 PodmanLogs 项,如果观察到如下内容,特别是 [Slow SQL Query] 相关的日志输出:

上述的输出非常的不合理,简单的 SQL 语句执行耗时高达 26分钟,这个结果是非常不合理的。SQLite 的性能不可能这么差劲。

导出数据库文件,通过 SQLiteStudio 执行相同的 SQL 语句,在  0.1 毫秒执行结束。

并且 Docker 多次重启之后,高概率发生文件损坏。另一个常见现象是处理器占用长时间处于高位,某几个核心长时间满负荷,但是 DUMP 堆栈调用又一切正常,看不到用户态的高占用函数。

这个原因是由于 Windows 和 Linux 的跨系统文件兼容性问题无法得到很好的解决,WSL 2 下访问 Windows 文件性能会非常差。

具体解释参考  [wsl2] filesystem performance is much slower than wsl1 in /mnt #4197 

解决这个问题的方法就是不要进行映射,直接在存储在虚拟机的磁盘里

同时,建议内存不低于 32GB ,大型项目,如果内存不足的话,也可能诱发此类问题。

另外,默认情况下 WSL 2 最高分配 1TB 磁盘空间给虚拟机。如果要突破这个限制,需要进行磁盘手工扩容。

需要执行如下操作:

  1. 使用 wsl.exe --shutdown 命令终止所有 WSL 实例
  2. 获取磁盘路径 (PowerShell):
  3. 使用管理员权限打开 Windows 命令提示符,然后输入以下命令来打开 diskpart 命令解释器:
  4. 现在会出现一个 DISKPART> 提示。 输入以下命令:
  5. 使用 Windows 命令提示符 DISKPART> 提示输入要分配给此 Linux 发行版的新的大小上限值(单位是 MB ),比如 2TB
  6. 退出 DISKPART> 提示:
  7. 安装 resize2fs 如下:
  8. 无损扩大分区大小,默认整个虚拟磁盘,命令如下:

注意,如果执行 expand vdisk maximum=2048000 命令的时候报错 “虚拟磁盘服务错误”。可能是磁盘当前被挂载,导致无法进行磁盘扩容。此时需要命令行执行 diskmgmt 然后找到这个虚拟磁盘点击卸载之后再进行尝试。

参考链接