Chart 10 OpenCL 优化教学

文章目录

  • 前言
  • 10.1 博客
  • 10.2 Sample Code
    • 10.2.1 算法优化
    • 10.2.2 Vectorized load/store
    • 10.2.3 image 代替 buffer
  • 10.3 Epsilon Filter
    • 10.3.1 初始化实现
    • 10.3.2 Data pack optimization
    • 10.3.3 Vectorized load/store optimization
    • 10.3.4 Further increase workload per work item
    • 10.3.5 Use local memory optimization
    • 10.3.6 Branch operations optimization
    • 10.3.7 Summary
  • 10.4 Sobel filter
    • 10.4.1 Algorithm optimization
    • 10.4.2 Data pack optimization
    • 10.4.3 Vectorized load/store optimization
    • 10.4.4 Performance and summary
  • 10.5 总结


前言

这一章提供了一些示例,以演示使用前几章讨论的优化技术。除了一些简单的代码片段演示外,我们还通过使用前几章中讨论的多种实践方法,逐步优化了两个知名的图像处理 filter,即 Epsilon filter 和 Sobel filter。


10.1 博客

一些博客讨论了使用案例的优化,这些资源可以在高通开发者网络上公开获取。以下是开发人员可以参考的其中一些博客:

Table 10-1 Blogs on OpenCL optimizations and other resources

  1. OpenCl 优化总结
  2. Epsilon Filter 案例教学
  3. Sobel Filter 案例教学
  4. 矩阵乘-1
    矩阵乘-2
  5. OpenCL ML SDK

在本节中讨论的使用案例包括Epsilon滤镜和Sobel滤镜,这些案例在这些博客中有部分涉及。

10.2 Sample Code

10.2.1 算法优化

这个示例演示了如何简化一个算法以优化其性能。给定一张图像,在其上应用一个简单的 8x8 模糊滤波 (Box Filter)。

__kernel void ImageBoxFilter(__read_only image2d_t source, __write_only image2d_t dest, sampler_t sampler)
{
	... // variable declaration
	for( int i = 0; i < 8; i++ )
	{
		for( int j = 0; j < 8; j++ )
		{
			coor = inCoord + (int2) (i - 4, j - 4 );
			// !! read_imagef is called 64 times per work item
			sum += read_imagef( source, sampler, coor);
		}
	}
	// Compute the average
	float4 avgColor = sum / 64.0f;
	... // write out result
}
	上述代码中,两层 for 循环读取了 64 个元素,之前求平均值

为了减少纹理访问,上述内核被分成两个阶段。第一阶段计算每个工作项的2x2平均值,并将结果保存到一个中间图像中。第二阶段使用中间图像进行最终的计算。

// First pass: 2x2 pixel average
__kernel void ImageBoxFilter(__read_only image2d_t source, __write_only image2d_t dest, sampler_t sampler)
{ 
	... // variable declaration
	 // Sample an 2x2 region and average the results
	for( int i = 0; i < 2; i++ )
	{
		for( int j = 0; j < 2; j++ )
		{
			coor = inCoord - (int2)(i, j);
			// 4 read_imagef per work item
			sum += read_imagef( source, sampler, coor );
		}
	}
	// equivalent of divided by 4, in case compiler does not optimize
	float4 avgColor = sum * 0.25f;
	... // write out result
}

// Second Pass: final average
__kernel void ImageBoxFilter16NSampling( __read_only image2d_t source, __write_only image2d_t dest, sampler_t sampler)
{
	... // variable declaration
	int2 offset = outCoord - (int2)(3,3);
	// Sampling 16 of the 2x2 neighbors
	for( int i = 0; i < 4; i++ )
	{
		for( int j = 0; j < 4; j++ )
		{
			coord = mad24((int2)(i,j), (int2)2, offset);
			// 16 read_imagef per work item
			sum += read_imagef( source, sampler, coord );
		}
	}
	// equivalent of divided by 16, in case compiler does not optimize
	float4 avgColor = sum * 0.0625;
	... // write out result
}

修改后的算法每个工作项对图像缓冲进行20次访问(4次直接访问 + 16次其他访问),明显少于原始算法的64次read_imagef访问。

10.2.2 Vectorized load/store

该示例演示了如何在Adreno GPU上进行矢量化的加载/存储,以更好地利用带宽。

Original kernel before optimization

__kernel void MatrixMatrixAddSimple( const int matrixRows, const int matrixCols, __global float* matrixA,
										__global float* matrixB, __global float* MatrixSum)
{
	int i = get_global_id(0);
	int j = get_global_id(1);
	// Only retrieve 4 bytes from matrixA and matrixB.
	// Then save 4 bytes to MatrixSum.
	MatrixSum[i*matrixCols+j] = matrixA[i*matrixCols+j] + matrixB[i*matrixCols+j];
}

Modified kernel

__kernel void MatrixMatrixAddOptimized2(const int rows, const int cols, __global float* matrixA, __global float* matrixB,
                                           __global float* MatrixSum)
{
	int i = get_global_id(0);
	int j = get_global_id(1);
	// Utilize built-in function to calculate index offset
	int offset = mul24(j, cols);
	int index = mad24(i, 4, offset);
	// Vectorize to utilization of memory bandwidth for performance gain.
	// Now it retrieves 16 bytes from matrixA and matrixB.
	// Then save 16 bytes to MatrixSum
	float4 tmpA = (*((__global float4*)&matrixA[index])); 
	 // Alternatively vload and vstore can be used in here
	float4 tmpB = (*((__global float4*)&matrixB[index])); 
	(*((__global float4*)&MatrixSum[index])) = (tmpA+tmpB);
	// Since ALU is scalar based, no impact on ALU operation.
}
		int offset = mul24(j, cols);
		int index = mad24(i, 4, offset);
		
		这两行代码我觉得存在问题,至少行列的读取风格应该和 Origin 算法保持一致,应该修改为:
		
		int offset = mul24(i, cols);
		int index = mad24(j, 4, offset);

新的内核正在使用 float4 进行矢量化的加载和存储。由于这种矢量化,内核的全局工作大小应该是原始内核的 1/4。

10.2.3 image 代替 buffer

该示例为五百万对向量计算点积。原始代码使用缓冲对象,并进行了修改以使用纹理对象(read_imagef)来改善频繁的数据访问。这是一个简单的例子,但这种技术可以应用于许多情况,其中 buffer 对象的访问不如 纹理对象 的访问效率高。

Original kernel before optimization

__kernel void DotProduct(__global const float4 *a, __global const float4 *b, __global float *result)
{
	// a and b contain 5 million vectors each
	// Arrays are stored as linear buffer in global memory
	result[gid] = dot(a[gid], b[gid]);
}

Modified kernel for optimization

__kernel void DotProduct(__read_only image2d_t c, __read_only image2d_t d, __global float *result)
{
	// Image c and d are used to hold the data instead of linear buffer
	// read_imagef goes through the texture engine
	int2 gid = (get_global_id(0), get_global_id(1));
	result[gid.y * w + gid.x] = dot(read_imagef(c, sampler, gid), read_imagef(d, sampler, gid));
}

10.3 Epsilon Filter

Epsilon滤波在图像处理中被广泛用于减少蚊子噪声(Mosquito noise),这是一种在图像的高频区域,如边缘发生的一种扰动。该滤波器本质上是一个非线性的逐点低通滤波器,具有空间变化的支持,只有像素值超过特定阈值的像素才会被滤波。

在这个实现中,Epsilon滤波仅应用于YUV图像的亮度(Y)分量,因为噪声主要在亮度分量中可见。此外,它假设Y分量是连续存储的(NV12格式),与UV分量分开。该实现分为两个基本步骤,如图10-1所示。

  • 对于待滤波的像素,计算其相邻的9x9区域中每个像素与中心像素的绝对差值。
  • 如果绝对差值低于阈值,则使用相邻像素的值进行平均。阈值通常是应用程序中预先定义的常数。

在这里插入图片描述

10.3.1 初始化实现

该应用的目标是具有3264x2448分辨率(宽度=3264,高度=2448)的YUV图像,每个像素为8位。此处报告的性能数据来自Snapdragon 810(MSM8994,Adreno 430)处于性能模式下。

以下是初始实施参数和策略:

  • 使用 OpenCL 图像对象而不是 Buffer
    • 使用图像而不是缓冲区可以避免一些边界检查,并充分利用 Adreno GPU 中的 L1 缓存。
  • 使用 CL_R | CL_UNORM_INT8 图像格式/数据类型。
    • 由于这仅用于 Y 分量,因此使用单通道,而 Adreno GPU 中的内置纹理管道将读取的像素归一化为 [0, 1]。
  • 每个工作项生成一个输出像素。
  • 使用 2D 内核,全局工作大小设置为 [3264, 2448]。

在实现中,每个工作项必须访问 81 个浮点像素。Adreno A430 GPU 的性能被用作进一步优化的基准。

10.3.2 Data pack optimization

通过比较计算量和数据负载的量,很容易得出结论,这是一个受内存限制的用例。因此,主要的优化应该集中在如何提高数据加载效率上。

首先要注意的是,使用32位浮点(fp32)来表示像素值是一种浪费内存的做法。对于许多图像处理算法,8位或16位的数据类型可能已经足够。由于 Adreno GPU 具有本机硬件支持16位浮点数据类型,即半精度或fp16,因此可以应用以下优化选项:

  • 使用16位半精度数据类型,而不是32位浮点。
    • 现在,每个工作项访问81个半精度数据。
  • 使用CL_RGBA | CL_UNORM_INT8图像格式/数据类型。
    • 使用CL_RGBA加载四个通道以更好地利用TP带宽。
    • 用read_imageh替换read_imagef。TP会自动将数据转换为16位半精度。
  • 每个工作项:
    • 每行读取三个half4向量。
    • 输出一个处理过的像素。
    • 每输出像素的内存访问次数:3x9=27(half4)。
  • 性能提升:1.4倍。
    在这里插入图片描述

10.3.3 Vectorized load/store optimization

在前一步骤中,只输出一个像素,并加载了许多相邻像素。通过加载一些额外的像素,可以按以下方式过滤更多像素:

  • 每个工作项。
  • 每行读取三个half4向量。
    • 输出四个像素。
    • 每输出像素的内存访问次数:3x9/4 = 6.75(half4)。
  • 全局工作大小:(宽度/4)x 高度。
  • 对每行进行循环展开。
  • 在每行内部,使用滑动窗口方法。
    在这里插入图片描述

图10-3说明了如何使用额外加载的多个像素进行处理的基本图表。以下是步骤:

Read center pixel c;
For row = 1 to 9, do:
read data p1;
Perform 1 computation with pixel c;
read data p2;
Perform 4 computations with pixel c;
read data p3;
Perform 4 computations with pixel c;
end for
write results back to pixel c。

经过这一步骤,性能相比基准提高了3.4倍

10.3.4 Further increase workload per work item

可以通过增加每个工作项的工作量来预期更多的性能提升。以下是选项:

  • 读取一个额外的half4向量,并将输出像素数量增加到8。
  • 全局工作大小:width/8 x height。
  • 每个工作项。
    • 每行读取四个half4向量。
    • 输出八个像素。
    • 每输出像素的内存访问次数:4x9/8 = 4.5(half4)。

在这里插入图片描述
这些更改导致了轻微的性能提升,增加了0.1倍。以下是为什么效果不佳的原因:

  • 缓存命中率并没有太大变化,因为在先前的步骤中已经非常优秀。
  • 需要更多寄存器,导致波数减少,这会影响并行性和延迟的隐藏。

出于实验目的,可以按以下方式加载更多像素:

  • 读取更多的half4向量,并将输出像素数量增加到16。
  • 全局工作大小:width/16 x height。

图10-5显示每个工作项执行以下操作:

  • 每行读取6个half4向量。
  • 输出16个像素。
  • 每输出像素的内存访问次数是6x9/16 = 3.375(half4)。

经过这些更改,性能从基准的3.4倍下降到了0.5倍。将更多像素加载到一个内核中导致寄存器溢出,严重影响了性能。
在这里插入图片描述

10.3.5 Use local memory optimization

本地内存(Local Memory)的延迟比全局内存(Global Memory)短得多,因为它是片上内存。一种选择是将像素加载到本地内存中,避免重复从全局内存中加载。除了中心像素,还加载了9x9滤波的周围像素到本地内存,如图10-6所示。

在这里插入图片描述
表10-2列出了两种情况的设置及其性能。整体性能比原始性能要好得多。然而,它们并没有超过第10.4.4节中的最佳性能。
在这里插入图片描述
正如在第7.1.1节中讨论的那样,本地内存通常需要在工作组内部进行屏障同步,而且不一定比全局内存提供更好的性能。相反,如果开销太大,性能可能更差。在这种情况下,如果全局内存具有较高的缓存命中率,那么全局内存可能更好

10.3.6 Branch operations optimization

Epsilon滤波器需要在像素之间进行如下比较:

Cond = fabs(c -p) <= (half4)(T);
sum += cond ? p : consth0;
cnt += cond ? consth1 : consth0;

三元运算符 ?: 在硬件中会导致一些分歧,因为波中并不是所有的线程都进入相同的执行分支。分支操作可以通过ALU操作来替代,如下所示:

Cond = convert_half4(-(fabs(c -p) <= (half4)(T)));
sum += cond * p;
cnt += cond;

这个优化是建立在第10.3.2节描述的优化基础之上的,性能从基准的3.4倍提高到了5.4倍。

关键的区别在于新代码在高度并行化的ALU中执行,波中的所有线程基本上执行相同的代码片段。相对而言,变量Cond可能具有不同的值,而旧的代码则使用一些昂贵的硬件逻辑来处理分歧。

10.3.7 Summary

优化步骤及其性能指标总结在表10-3中。最初,该算法受到内存的限制。通过进行数据打包和矢量化加载,它变得更多地依赖于ALU。总体而言,这个用例的关键优化是最优地加载数据。许多受内存限制的用例可以通过使用类似的技术来加速。
在这里插入图片描述
在这里插入图片描述

Epsilon滤波器在三种不同分辨率下的OpenCL性能显示在表10-4中。对于较大的图像,收益更为明显。对于3264x2448的图像,与初始的OpenCL代码相比,观察到了5.4倍的性能提升,而对于512x512的图像,性能提升为4.3倍。与工作负载无关的内核执行与固定成本相关,随着工作负载的增加,其在整体性能中的权重变得较低。
在这里插入图片描述

10.4 Sobel filter

Sobel滤波器,也称为Sobel算子,用于许多图像处理和计算机视觉算法中进行边缘检测。它使用两个3x3的核与原始图像相结合,以近似求导。有两个核:一个用于水平方向,另一个用于垂直方向,如图10-7所示。
在这里插入图片描述

10.4.1 Algorithm optimization

Sobel滤波器是一个可分解的滤波器,可以分解如下:
在这里插入图片描述
与不可分解的2D滤波器相比,2D可分离滤波器可以将复杂度从O(n^2)降低到O(n)。由于2D的高复杂性和计算成本,使用可分离的滤波器而不是不可分离的滤波器是非常可取的。

10.4.2 Data pack optimization

尽管可分离滤波器显著减少了计算量,但对于每个点的滤波所需的像素数量是相同的,即对于这个3x3的核,是八个相邻像素加上中心像素。很容易看出这是一个受内存限制的问题。因此,如何有效地将像素加载到GPU是性能的关键。下面的图中说明了三种选项:
在这里插入图片描述
以下表格总结了每种情况下所需的总字节数和平均字节数。在图10-9中的第一种情况中,每个工作项只对一个像素进行Sobel滤波。随着每个工作项的像素数量增加,图10-10和图10-11中所示的情况下要加载的数据量减少。这通常减少了从全局内存到GPU的数据流量,从而获得更好的性能。
在这里插入图片描述

10.4.3 Vectorized load/store optimization

对于16x1和16x2的情况,可以通过使用OpenCL中的矢量化加载存储函数(如float4、int4、char4等)进一步减少加载/存储的数量。表10-6显示了矢量化情况下的加载/存储请求数量(假设像素数据类型为8位char)。
在这里插入图片描述
进行矢量化加载的代码片段如下:

short16 line_a = convert_short16(as_uchar16(*((__global uint4 *)(inputImage+offset))));

在边界有两个像素需要加载,如下所示:

short2 line_b = convert_short2(*((__global uchar2 *)(inputImage + offset + 16)));

注意:每个工作项处理的像素数量的增加可能会导致寄存器占用的压力加大,导致寄存器溢出到私有内存并导致性能下降。

10.4.4 Performance and summary

在应用了这两个优化步骤之后,观察到了显著的性能提升,如图10-12所示,其中在MSM8992(Adreno 418)上的原始性能(每个工作项一个像素)被归一化为1。

在这里插入图片描述

总结一下,以下是这个用例优化的关键点。

  • 数据打包提高了内存访问效率。
  • 使用矢量化加载/存储来减少内存流量。
  • 在这种情况下,短类型优于整数或字符类型。

在这种情况下,没有使用本地内存。数据打包和矢量化加载/存储已经最小化了可重复使用的数据重叠。因此,使用本地内存并不一定会提高性能。可能还有其他选项来提升性能,例如使用纹理而不是全局缓冲区。

10.5 总结

本章提供了一些示例和代码片段,演示了前几章介绍的优化规则以及性能的变化。开发人员应该尝试在真实设备上跟随这些步骤。由于编译器和驱动程序的升级,不是所有的结果都能够完全重现。但总体而言,通过这些优化步骤应该能够实现类似的性能提升。


本节根据案例讲解优化技巧,可以进行参考,没有给出具体的代码。后续研究我会写一些完整的案例供参考。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/246421.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

windows禁用系统更新

1.在winr运行框中输入services.msc&#xff0c;打开windows服务窗口。 services.msc 2.在服务窗口中&#xff0c;我们找到Windows update选项&#xff0c;如下图所示&#xff1a; 3.双击windows update服务&#xff0c;我们把启动类型改为禁用&#xff0c;如下图所示&#xff…

【网络安全】网络防护之旅 - 对称密码加密算法的实现

&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《网络安全之道 | 数字征程》⏰墨香寄清辞&#xff1a;千里传信如电光&#xff0c;密码奥妙似仙方。 挑战黑暗剑拔弩张&#xff0c;网络战场誓守长。 目录 &#x1f608;1. 初识网络安…

服务器系统启动卡logo问题解决分析

系统版本&#xff1a;Kylin-4.0.2-server-sp4-20200808.J1-57s-xxc_01-20210112-x86_64 问题&#xff1a;系统启动一直卡在麒麟logo那里&#xff0c;进不去系统。在gurb里去掉系统启动参数quiet和splash&#xff0c;发现启动卡在如下界面。 排查&#xff1a;开始怀疑是掉电导致…

【带头学C++】----- 九、类和对象 ---- 9.12 C++之友元函数(9.12.5---9.12.7)

❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️创做不易&#xff0c;麻烦点个关注❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️ ❤️❤️❤️❤️❤️❤️❤️❤️❤️文末有惊喜&#xff01;献舞一支&#xff01;❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️ 目录 补充上…

Kubernetes 容器编排 -- 1

前言 知识扩展 早在 2015 年 5 月&#xff0c;Kubernetes 在 Google 上的搜索热度就已经超过了 Mesos 和 Docker Swarm&#xff0c;从那儿之后更是一路飙升&#xff0c;将对手甩开了十几条街,容器编排引擎领域的三足鼎立时代结束。 目前&#xff0c;AWS、Azure、Google、阿里…

Vulnhub-DC-3 靶机复现完整过程

啰嗦两句&#xff1a; 提权之前完成是一个月前做的&#xff0c;当时在提权处出了点问题就搁置了&#xff0c;今天才完成&#xff0c;所以IP地址可能会会有变化 注意&#xff1a;后续出现的IP地址为192.168.200.55同样是靶机IP地址&#xff0c;若本文能有帮助到你的地方&#xf…

k8s中的Pod

目录 1.1 创建一个pod 1.2 pod是如何被创建的 1.3 创建一个单容器pod 1.4 创建一个多容器pod 1.4.1 配置节点标签 1.5 Pod容器的交互 1.5.1 创建pod&#xff0c;并做本地解析 1.5.2 pod共享进程 1.5.2 pod共用宿主机namespace Pod 是可以在 Kubernetes 中创建和管理的、…

Redis设计与实现之字典

目录 一、字典 1、 字典的应用 实现数据库键空间 用作Hash类型键的其中一种底层实现 2、字典的实现 哈希表实现 哈希算法 3、创建新字典 4、添加键值对到字典 5、添加新元素到空白字典 6、添加新键值对时发生碰撞处理 7、添加新键值对时触发了 rehash操作 Note:什么…

Java 基础学习(九)API概述、Object、String、正则表达式

1 API概述 1.1 API概述 1.1.1 什么是API API(Application Programming Interface)&#xff0c;意为&#xff1a;应用程序接口。API就是已经写好的的程序或功能&#xff0c;程序要需要时可以直接调用&#xff0c;无需再次编写。 API可以大致分为如下几类&#xff1a; 编程语…

【笔试强化】Day 2

文章目录 一、选择1.2.&#xff08;写错&#xff09;3.4.5.6.&#xff08;不会&#xff09;7.&#xff08;不清晰&#xff09;8. &#xff08;不会&#xff09;9.10.&#xff08;写错&#xff09; 二、编程1. 排序子序列解法&#xff1a;代码&#xff1a; 2. 倒置字符串解法&am…

STM32--中断使用(超详细!)

写在前面&#xff1a;前面的学习中&#xff0c;我们接触了STM32的第一个外设GPIO&#xff0c;这也是最常用的一个外设&#xff1b;而除了GPIO外&#xff0c;中断也是一个十分重要且常用的外设&#xff1b;只有掌握了中断&#xff0c;再处理程序时才能掌握好解决实际问题的逻辑思…

MyBatis之逆向工程

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

IT圈茶余饭后的“鄙视链”

哈哈&#xff0c;IT圈的鄙视链&#xff0c;简直就是一出情感大戏&#xff01;这个圈子里的人们总是忍不住要互相比较&#xff0c;互相鄙视&#xff0c;仿佛这是一场刺激的游戏&#xff0c;每个人都想要站在鄙视链的最顶端&#xff0c;成为那个最牛逼的存在。 首先&#xff0c;…

uniapp开发项目注意事项

uniapp创建项目用HBuilderX创建或者用脚手架命令创建都可以vue文件渲染h5&#xff0c;小程序很好nvue文件渲染原生app更好&#xff0c;注意nvue文件css的一些局限性&#xff0c;简称坑死人nvue所支持的通用样式已在本文档中全部列出&#xff0c;一些组件可能有自定义样式&#…

Docker网络模式:深度理解与容器网络配置

Docker 的网络模式是容器化应用中一个关键而复杂的方面。本文将深入讨论 Docker 的网络模式&#xff0c;包括基本概念、常用网络模式以及高级网络配置&#xff0c;并通过更为丰富和实际的示例代码&#xff0c;帮助读者全面掌握如何理解和配置容器网络。 Docker网络基础 1 Doc…

Pr自动从视频脚本剪辑视频FirstCut插件免费下载

FirstCut 插件将自动从视频脚本中剪辑视频&#xff0c;在例如新闻、采访、自媒体视频等带有配音或字幕内容的视频制作中提高了粗剪效率。 使用 FirstCut&#xff0c;大大缩短了粗剪的时间&#xff0c;而不是转到每个视频文件并找到 IN 点和 OUT 点&#xff0c;然后将其插入到序…

ubuntu install sqlmap

refer: https://github.com/sqlmapproject/sqlmap 安装sqlmap&#xff0c;可以直接使用git 克隆整个sqlmap项目&#xff1a; git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev 2.然后进入sqlmap-dev&#xff0c;使用命令&#xff1a; python s…

改进了编排控制并增强了推理的可视性,Agents for Amazon Bedrock 现已上市

七月份的时候&#xff0c;我们推出了 Agents for Amazon Bedrock 预览版。如今&#xff0c;Agents for Amazon Bedrock 全面上市。 Agents for Amazon Bedrock 通过编排多步任务&#xff0c;有助于您加速生成人工智能 &#xff08;AI&#xff09; 应用程序的开发。代理使用基础…

@德人合科技——天锐绿盾|电脑文件防止泄密|文件、文档、图纸、源代码等透明加密保护,防泄密软件系统

德人合科技——天锐绿盾提供了一种企业办公电脑文件防止泄密的解决方案&#xff0c;该方案对文件、文档、设计图纸、源代码等进行了透明加密保护。 pc访问地址&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee 透明加密是一种保护文…

Qt 表格相关API

1.文本框 限制输入数据类型&#xff08;如仅英文&#xff09; QValidator* validator new QRegExpValidator(QRegExp("[a-zA-Z]"), lineText); // 创建正则表达式验证器lineText->setValidator(validator); // 将验证器设置给 QLineEdit QLineEdit&#xff1a;单…