sgcGrid 基准测试
本页的每个数字都是在同一台机器上,使用 sgcGrid 的 QA 性能关卡和 QA 套件附带的两个基准测试程序测得的。没有任何估算或外推。请在您自己的硬件和数据库上运行同样的程序,得到属于您的数据。
本页的每个数字都是在同一台机器上,使用 sgcGrid 的 QA 性能关卡和 QA 套件附带的两个基准测试程序测得的。没有任何估算或外推。请在您自己的硬件和数据库上运行同样的程序,得到属于您的数据。
| 测量项 | Win32 | Win64 |
|---|---|---|
| 100,000 行网格的 1920 x 1080 帧,12 列(QA 关卡 C1) | 1.47 毫秒 | 1.29 毫秒 |
| 同一帧,带闪烁单元格和 3 条格式规则 | 1.67 毫秒 | 1.46 毫秒 |
| 工作线程向已排序的 1,000 行网格实时更新,以 60 Hz 渲染帧 | 每秒 1,000 次更新,60.0 fps,0 次丢帧 | 每秒 1,000 次更新,60.0 fps,0 次丢帧 |
| 同一网格保持 60 fps 时,单个工作线程的最高更新速率 | 1,552,858 次更新/秒 | 2,001,921 次更新/秒 |
| 服务器模式,10,000,000 行 SQLite 表:打开到绘制出第一页 | 未运行 | 117.3 毫秒(使用估算行数为 2.1 毫秒) |
测量日期:2026-11-04。下方各表给出了测试方法、测试机器和每一项数据。
一帧是 1920 x 1080 网格的可见窗口,由 TsgcGridRenderer 以无窗口方式渲染到光栅画布,所用代码与绘制 VCL 和 FireMonkey 网格的代码相同。整个过程没有窗口,也没有 GPU。网格每帧都会略微滚动,就像实时网格一样。C1 和其他帧关卡取预热后 5 次运行(每次 60 帧)的中位数。
分组、筛选、排序和汇总在网格数据模型上运行,不使用渲染器。每个数字是预热后 5 次运行的中位数,关卡有说明时则取 12 次交替运行中的最佳值。每个关卡都有预算,数字超出预算时 QA 运行即告失败。
工作线程使用线程安全的 UpdateRow 写入价格更新,与 LiveTicker 演示的做法相同。主线程每 16.67 毫秒(60 Hz)渲染一帧。每一帧都会像已绘制的网格那样对变化的视图重新排序。基准测试会报告每秒写入的更新数、每秒渲染的帧数、错过时隙的帧数以及每帧的渲染时间。
TsgcGridServerProvider_DataSet 通过 FireDAC 对生成的表进行分页。每一步在其行加载完毕且帧绘制完成时结束。每个数字是预热后 5 次运行的中位数,数据库文件位于操作系统缓存中。每次运行的跳转位置相差 2,000 行,因此每次运行都从数据库读取,而不是从网格的页缓存读取。
| CPU | Intel Core Ultra 5 245T,14 核,14 个逻辑处理器 |
| 内存 | 31.5 GB |
| 磁盘 | NVMe SSD,Phison 1 TB |
| 操作系统 | Windows 11 Pro 26H2,版本 26300.9711 |
| 编译器 | Delphi 13(编译器 37.0),Win32 和 Win64 发布版本 |
| 数据库 | 通过 FireDAC 使用 SQLite 3.42.0,嵌入并静态链接 |
| 源代码 | 2026-11-04 的 sgcGrid 开发树(svn 修订版 20169,含本地修改) |
这台机器是开发工作站,而不是独立的基准测试服务器。每次重负载运行都持有机器范围的锁,因此没有其他构建或测试同时运行。
sgcGrid QA 套件的 perf 组,在 Win32 和 Win64 上单独运行。预算是关卡强制执行的上限(Win32 / Win64)。
| 关卡 | 测量内容 | Win32 | Win64 | 预算 |
|---|---|---|---|---|
| C1 | 平面帧 1920 x 1080,12 列,100,000 行 | 1.47 毫秒 | 1.29 毫秒 | 4.09 / 4.32 毫秒 |
| 带样式 | 同一帧,闪烁单元格和 3 条格式规则(60 帧平均值) | 1.67 毫秒 | 1.46 毫秒 | 25 / 16 毫秒 |
| GW3 | 带查找高亮的帧,100,000 行 | 1.43 毫秒 | 1.75 毫秒 | 25 / 16 毫秒 |
| GW5 | 带 20 列、3 个带区层级、2 个冻结列的帧,100,000 行 | 1.62 毫秒 | 1.42 毫秒 | 25 / 16 毫秒 |
| GW7 | 带图像、进度和 30 点迷你图列的帧,100,000 行 | 2.20 毫秒 | 3.20 毫秒 | 25 / 16 毫秒 |
| F1 | 带 2 个冻结列和页脚、向两个方向滚动的帧,100,000 行 | 0.56 毫秒 | 0.50 毫秒 | 25 / 16 毫秒 |
| G4 | 按 2 列分组、全部展开的 1,000,000 行的帧 | 0.49 毫秒 | 0.48 毫秒 | 25 / 16 毫秒 |
| D1 | 带 3 个可见明细带(每个 50 行)的帧 | 2.47 毫秒 | 2.65 毫秒 | 25 / 16 毫秒 |
| G1 | 按 2 列对 100,000 行分组,2 个汇总,构建并展平 | 15.11 毫秒 | 11.98 毫秒 | 60 / 40 毫秒 |
| G2 | 按 2 列对 1,000,000 行分组,2 个汇总,构建并展平 | 155.37 毫秒 | 132.61 毫秒 | 700 / 450 毫秒 |
| G3 | 展开或折叠 1,000,000 行视图中的一个分组 | 4.16 毫秒 | 3.28 毫秒 | 25 / 15 毫秒 |
| G5 | 页脚汇总,100,000 行的 5 列 | 5.64 毫秒 | 2.34 毫秒 | 25 / 15 毫秒 |
| T1 | 100,000 行的树,深度最多 8 层,完全展开时构建并展平 | 20.42 毫秒 | 22.80 毫秒 | 90 / 60 毫秒 |
| GW0 | 重建已排序视图,1,000,000 行 | 253.45 毫秒 | 240.29 毫秒 | 268.24 / 259.62 毫秒 |
| GW1 | 含 1,000 个成员的文本列的不同值,带级联筛选,1,000,000 行 | 55.61 毫秒 | 48.42 毫秒 | 450 / 300 毫秒 |
| GW2 | 使用 1,000 个值中选 500 个的值列表加一个条件进行筛选,1,000,000 行 | 50.75 毫秒 | 48.02 毫秒 | 225 / 150 毫秒 |
| M1 | 数据之上的行视图内存,按 2 列分组的 1,000,000 行 | 20.07 MB | 20.09 MB | 32 MB |
| X1 | 导出 100,000 行 x 12 列的 XLSX,2 个分组层级,含汇总 | 1,693.87 毫秒 | 1,002.71 毫秒 | 4,500 / 3,000 毫秒 |
| L2 | 日志查看器跟随末尾,同时一个线程每秒追加 10,000 行:每秒绘制的帧数 | 38.87 fps | 38.67 fps | 至少 20 fps |
| L2 | 同一次运行:输入延迟的第 99 百分位 | 4.22 毫秒 | 2.83 毫秒 | 33 毫秒 |
sgcGridBench_Live:工作线程用 UpdateRow 写入随机一行的价格、涨跌、成交量和时间,同时主线程每 16.67 毫秒 渲染一个已排序、闪烁的 1920 x 1080 网格。每种情况 5 秒。错过的时隙是指在下一帧开始之后才完成的帧。
| 行数 | 目标速率 | 平台 | 更新数/秒 | 帧数/秒 | 错过的时隙 | 渲染中位数 | 渲染 p99 | 渲染最大值 |
|---|---|---|---|---|---|---|---|---|
| 1,000 | 1,000/s | Win32 | 1,000 | 60.0 | 0 | 1.28 毫秒 | 2.46 毫秒 | 2.57 毫秒 |
| 1,000 | 1,000/s | Win64 | 1,000 | 60.0 | 0 | 1.36 毫秒 | 2.41 毫秒 | 2.45 毫秒 |
| 1,000 | 10,000/s | Win32 | 9,998 | 60.0 | 0 | 1.43 毫秒 | 2.79 毫秒 | 2.98 毫秒 |
| 1,000 | 10,000/s | Win64 | 9,998 | 60.0 | 0 | 1.29 毫秒 | 2.46 毫秒 | 3.17 毫秒 |
| 1,000 | 尽可能快 | Win32 | 1,552,858 | 60.0 | 0 | 1.26 毫秒 | 3.09 毫秒 | 3.35 毫秒 |
| 1,000 | 尽可能快 | Win64 | 2,001,921 | 60.0 | 0 | 1.26 毫秒 | 2.54 毫秒 | 3.28 毫秒 |
| 100,000 | 1,000/s | Win32 | 1,000 | 60.0 | 0 | 10.52 毫秒 | 13.72 毫秒 | 15.92 毫秒 |
| 100,000 | 1,000/s | Win64 | 1,000 | 60.0 | 0 | 10.50 毫秒 | 13.99 毫秒 | 14.68 毫秒 |
| 100,000 | 10,000/s | Win32 | 9,966 | 47.6 | 62 | 14.70 毫秒 | 40.33 毫秒 | 42.03 毫秒 |
| 100,000 | 10,000/s | Win64 | 9,997 | 54.2 | 29 | 13.59 毫秒 | 35.10 毫秒 | 39.86 毫秒 |
100,000 行的已排序网格在排序列发生变化的每一帧都会对视图重新排序,因此其渲染时间随行数增长。对未排序列的更新会保留视图,而 MaxFPS 可以降低只需要看起来实时的网格的帧率。
SQLite 3.42.0 上的 sgcGridBench_Server,Win64。该表有 12 列混合类型(整数、文本、浮点数、日期时间、布尔值),一个带索引的整数键和一个带索引的客户列,数据为确定性生成。每一步都以行加载完毕且 1920 x 1080 帧绘制完成结束。预热后 5 次运行的中位数。
| 步骤 | 1,000,000 行 | 10,000,000 行 | 数据库上运行的内容 |
|---|---|---|---|
| 打开到绘制出第一页 | 2.8 毫秒 | 117.3 毫秒 | 来自架构的列,精确的 COUNT(*),第一页 |
| 使用估算行数打开 | 2.1 毫秒 | 2.1 毫秒 | CountMode = gscEstimated:只取第一页,页面返回满页时行数继续增长 |
| 精确行数 | 0.2 毫秒 | 115.6 毫秒 | SELECT COUNT(*) |
| 跳转到中间 | 40.1 毫秒 | 378.8 毫秒 | 中间行处的一页,LIMIT 和 OFFSET |
| 跳转到末尾 | 75.0 毫秒 | 735.4 毫秒 | 最后一页,LIMIT 和 OFFSET |
| 按带索引的列排序 | 2.5 毫秒 | 119.3 毫秒 | 通过其索引执行 ORDER BY customer,外加新查询的精确行数 |
| 按无索引的列排序 | 106.9 毫秒 | 1,169.3 毫秒 | ORDER BY amount DESC,全表扫描 |
| 筛选生成器条件 | 97.5 毫秒 | 975.7 毫秒 | WHERE region = 'North' AND qty > 50,行数和第一页(分别匹配 40,000 行和 400,000 行) |
| 服务器分组,绘制分组行 | 266.5 毫秒 | 3,917.8 毫秒 | GROUP BY region(12 个分组)并给出每组的行数,全表扫描 |
| 运行后的进程内存 | 76.5 MB | 75.7 MB | 基准测试进程的工作集,包含 SQLite 页缓存 |
网格本身只读取屏幕上的页,因此它自身的开销保持不变。随表增长的是数据库的工作:精确行数、深度跳转的 OFFSET,以及对无索引的列进行排序或分组。在排序或分组的列上建立索引,或使用估算行数,可以让这些步骤保持很短。生成 10,000,000 行的表用了 42.5 s,对同一文件的第二次运行得到的数据与上述数字相差在 25% 以内。
SQL Server、PostgreSQL、Firebird 以及 100,000,000 行的表将使用同样的测试工具测量。运行后,它们的数据会添加到这里。在此之前,不会公布它们的任何数字。
基准测试程序位于 sgcUI 源代码的 QA 文件夹,QA\Grid\Bench。它们是控制台程序,没有 IDE 工程。
build_bench.bat 使用 Delphi 13 为 Win32 和 Win64 构建 sgcGridBench_Server.exe 和 sgcGridBench_Live.exe,输出到 bin\win32 和 bin\win64,并把 ini 文件复制到它们旁边。
sgcGridBench_Server.exe --rows=10000000 在表不存在时创建该表,然后测量每一步,并写出一份 Markdown 报告和每个数据一行的 CSV。选项:--ini、--rows、--runs、--db、--generate、--out。
编辑 sgcGridBench_Server.ini:DriverName 是 FireDAC 驱动(MSSQL、PG、FB 或 SQLite),[Params] 中的每个键都会传给连接,Dialect 选择 SQL 方言。ini 中为每种引擎提供了一个示例。
sgcGridBench_Live.exe --seconds=5 运行上面的实时用例。客户端关卡是 QA 套件的 perf 组,即 sgcGridQA.exe --group=perf,它会把每个数据追加到 sgcGridQA_perf.txt。请在空闲的机器上单独运行。
[Connection]
DriverName=PG
[Params]
Server=localhost
Port=5432
Database=bench
User_Name=postgres
Password=secret
[Bench]
Dialect=postgresql
Table=bench_orders
Rows=10000000
Runs=5