高性能计算实践- 遥遥领先!看看 streaming store 在矩阵转置中有多少提升

news/2025/11/19 20:56:43/文章来源:https://www.cnblogs.com/coffeepro123/p/19242280

说明

本文作为上一篇 矩阵转置 transpose 复现的补充测试。来看看 streaming store 到底什么实力。

性能测试

FORCE_INLINE void transpose_8x8_store_contiguous(const uint8_t* src0, const uint8_t* src1, const uint8_t* src2, const uint8_t* src3,const uint8_t* src4, const uint8_t* src5, const uint8_t* src6, const uint8_t* src7,uint8_t* pDst) {__m128i r0 = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(src0));__m128i r1 = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(src1));__m128i r2 = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(src2));__m128i r3 = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(src3));__m128i r4 = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(src4));__m128i r5 = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(src5));__m128i r6 = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(src6));__m128i r7 = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(src7));__m128i t0 = _mm_unpacklo_epi8(r0, r1); __m128i t1 = _mm_unpacklo_epi8(r2, r3);__m128i t2 = _mm_unpacklo_epi8(r4, r5); __m128i t3 = _mm_unpacklo_epi8(r6, r7);__m128i t4 = _mm_unpacklo_epi16(t0, t1); __m128i t5 = _mm_unpacklo_epi16(t2, t3);__m128i t6 = _mm_unpackhi_epi16(t0, t1); __m128i t7 = _mm_unpackhi_epi16(t2, t3);__m128i c0 = _mm_unpacklo_epi32(t4, t5); __m128i c1 = _mm_unpackhi_epi32(t4, t5);__m128i c2 = _mm_unpacklo_epi32(t6, t7); __m128i c3 = _mm_unpackhi_epi32(t6, t7);_mm_store_si128(reinterpret_cast<__m128i*>(pDst + 0), c0);_mm_store_si128(reinterpret_cast<__m128i*>(pDst + 16), c1);_mm_store_si128(reinterpret_cast<__m128i*>(pDst + 32), c2);_mm_store_si128(reinterpret_cast<__m128i*>(pDst + 48), c3);
}template <bool UseStream>
FORCE_INLINE void
transpose_64x64_tile_impl(const uint8_t* pSrc, unsigned int srcStep, uint8_t* pDst, unsigned int dstStep) {alignas(64) uint8_t tmp[64 * 64];uint8_t* tmpPtr = tmp;size_t srcStep8 = (size_t)srcStep * 8;const uint8_t* s0 = pSrc;for (int y = 0; y < 64; y += 8) {transpose_8x8_store_contiguous(s0, s0+srcStep, s0+srcStep*2, s0+srcStep*3,s0+srcStep*4, s0+srcStep*5, s0+srcStep*6, s0+srcStep*7, tmpPtr);tmpPtr += 64;s0 += srcStep8;}for (int colBlock = 0; colBlock < 8; ++colBlock) {const uint8_t* bBase = tmp + colBlock * 64;for (int r = 0; r < 8; ++r) {int laneOffset = r * 8;__m128i b0 = _mm_loadl_epi64((const __m128i*)(bBase + 0 * 512 + laneOffset));__m128i b1 = _mm_loadl_epi64((const __m128i*)(bBase + 1 * 512 + laneOffset));__m128i b2 = _mm_loadl_epi64((const __m128i*)(bBase + 2 * 512 + laneOffset));__m128i b3 = _mm_loadl_epi64((const __m128i*)(bBase + 3 * 512 + laneOffset));__m128i b4 = _mm_loadl_epi64((const __m128i*)(bBase + 4 * 512 + laneOffset));__m128i b5 = _mm_loadl_epi64((const __m128i*)(bBase + 5 * 512 + laneOffset));__m128i b6 = _mm_loadl_epi64((const __m128i*)(bBase + 6 * 512 + laneOffset));__m128i b7 = _mm_loadl_epi64((const __m128i*)(bBase + 7 * 512 + laneOffset));__m128i v0 = _mm_unpacklo_epi64(b0, b1);__m128i v1 = _mm_unpacklo_epi64(b2, b3);__m128i v2 = _mm_unpacklo_epi64(b4, b5);__m128i v3 = _mm_unpacklo_epi64(b6, b7);uint8_t* dstRowPtr = pDst + (colBlock * 8 + r) * dstStep;if (UseStream) {_mm_stream_si128(reinterpret_cast<__m128i*>(dstRowPtr + 0), v0);_mm_stream_si128(reinterpret_cast<__m128i*>(dstRowPtr + 16), v1);_mm_stream_si128(reinterpret_cast<__m128i*>(dstRowPtr + 32), v2);_mm_stream_si128(reinterpret_cast<__m128i*>(dstRowPtr + 48), v3);} else {_mm_storeu_si128(reinterpret_cast<__m128i*>(dstRowPtr + 0), v0);_mm_storeu_si128(reinterpret_cast<__m128i*>(dstRowPtr + 16), v1);_mm_storeu_si128(reinterpret_cast<__m128i*>(dstRowPtr + 32), v2);_mm_storeu_si128(reinterpret_cast<__m128i*>(dstRowPtr + 48), v3);}}}
}// 8x8 直接转置
FORCE_INLINE void
transpose_8x8_u8_to_strided(const uint8_t* pSrc, unsigned int srcStep, uint8_t* pDst, unsigned int dstStep) {__m128i r0 = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(pSrc + 0 * srcStep));__m128i r1 = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(pSrc + 1 * srcStep));__m128i r2 = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(pSrc + 2 * srcStep));__m128i r3 = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(pSrc + 3 * srcStep));__m128i r4 = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(pSrc + 4 * srcStep));__m128i r5 = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(pSrc + 5 * srcStep));__m128i r6 = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(pSrc + 6 * srcStep));__m128i r7 = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(pSrc + 7 * srcStep));__m128i t0 = _mm_unpacklo_epi8(r0, r1); __m128i t1 = _mm_unpacklo_epi8(r2, r3);__m128i t2 = _mm_unpacklo_epi8(r4, r5); __m128i t3 = _mm_unpacklo_epi8(r6, r7);__m128i t4 = _mm_unpacklo_epi16(t0, t1); __m128i t5 = _mm_unpacklo_epi16(t2, t3);__m128i t6 = _mm_unpackhi_epi16(t0, t1); __m128i t7 = _mm_unpackhi_epi16(t2, t3);__m128i c0 = _mm_unpacklo_epi32(t4, t5); __m128i c1 = _mm_unpackhi_epi32(t4, t5);__m128i c2 = _mm_unpacklo_epi32(t6, t7); __m128i c3 = _mm_unpackhi_epi32(t6, t7);_mm_storel_epi64(reinterpret_cast<__m128i*>(pDst + 0 * dstStep), c0);_mm_storel_epi64(reinterpret_cast<__m128i*>(pDst + 1 * dstStep), _mm_srli_si128(c0, 8));_mm_storel_epi64(reinterpret_cast<__m128i*>(pDst + 2 * dstStep), c1);_mm_storel_epi64(reinterpret_cast<__m128i*>(pDst + 3 * dstStep), _mm_srli_si128(c1, 8));_mm_storel_epi64(reinterpret_cast<__m128i*>(pDst + 4 * dstStep), c2);_mm_storel_epi64(reinterpret_cast<__m128i*>(pDst + 5 * dstStep), _mm_srli_si128(c2, 8));_mm_storel_epi64(reinterpret_cast<__m128i*>(pDst + 6 * dstStep), c3);_mm_storel_epi64(reinterpret_cast<__m128i*>(pDst + 7 * dstStep), _mm_srli_si128(c3, 8));
}// 如果内存是64字节对齐速度会更快
// void aligned_free_wrapper(void* ptr) { _aligned_free(ptr); }
//
// using AlignedUniquePtr = std::unique_ptr<uint8_t[], void(*)(void*)>;
//
// AlignedUniquePtr make_aligned_buffer(size_t size, size_t alignment) {
//     size_t remainder = size % alignment;
//     size_t alloc_size = (remainder == 0) ? size : (size + alignment - remainder);
//
//     void* ptr = nullptr;
//
//     ptr = _aligned_malloc(alloc_size, alignment);
//
//     return AlignedUniquePtr(static_cast<uint8_t*>(ptr), aligned_free_wrapper);
// }
//
// class TransposeFixture : public benchmark::Fixture {
// public:
//     AlignedUniquePtr src_owner{nullptr, std::free};
//     AlignedUniquePtr dst_owner{nullptr, std::free};
//
//     uint8_t* src = nullptr;
//     uint8_t* dst = nullptr;
//
//     const int width = 4096;
//     const int height = 4096;
//     size_t step;
//
//     void SetUp(const benchmark::State& state) override {
//         step = width;
//         size_t total_bytes = step * height;
//         size_t alignment = 64;
//
//         src_owner = make_aligned_buffer(total_bytes, alignment);
//         dst_owner = make_aligned_buffer(total_bytes, alignment);
//
//         if (!src_owner || !dst_owner) {
//             const_cast<benchmark::State&>(state).SkipWithError("Memory allocation failed!");
//             return;
//         }
//
//         src = src_owner.get();
//         dst = dst_owner.get();
//
//         std::memset(src, 128, total_bytes);
//         std::memset(dst, 0, total_bytes);
//     }
//
//     void TearDown(const benchmark::State& state) override {
//     }
// };class TransposeFixture : public benchmark::Fixture {
public:// Changed to standard unique_ptr arraystd::unique_ptr<uint8_t[]> src_owner;std::unique_ptr<uint8_t[]> dst_owner;uint8_t* src = nullptr;uint8_t* dst = nullptr;const int width = 4096;const int height = 4096;size_t step;void SetUp(const benchmark::State& state) override {step = width;size_t total_bytes = step * height;// Removed alignment logic, using standard new[]try {src_owner = std::make_unique<uint8_t[]>(total_bytes);dst_owner = std::make_unique<uint8_t[]>(total_bytes);} catch (const std::bad_alloc&) {const_cast<benchmark::State&>(state).SkipWithError("Memory allocation failed!");return;}src = src_owner.get();dst = dst_owner.get();std::memset(src, 128, total_bytes);std::memset(dst, 0, total_bytes);}void TearDown(const benchmark::State& state) override {}
};// 空跑的基准测试,目的是在实际测试开始前唤醒 CPU 到高频状态
void CPU_WarmUp(benchmark::State& state) {for (auto _ : state) {// 进行一些简单的浮点运算以消耗 CPU 周期volatile double x = 1.0;for (int i = 0; i < 1000; ++i) {x = x * 1.0001 + 0.001;}benchmark::DoNotOptimize(x);}
}
// 强制 WarmUp 至少运行 1 秒,并排在最前面
BENCHMARK(CPU_WarmUp)->MinTime(1.0);BENCHMARK_F(TransposeFixture, Std_Memcpy)(benchmark::State& state) {size_t size = size_t(width) * height;for (auto _ : state) {std::memcpy(dst, src, size);benchmark::DoNotOptimize(dst);}state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(width) * int64_t(height) * 2);
}BENCHMARK_F(TransposeFixture, Buffered_64x64_Stream)(benchmark::State& state) {for (auto _ : state) {// 按 64x64 块遍历for (int y = 0; y < height; y += 64) {for (int x = 0; x < width; x += 64) {const uint8_t* sTile = src + y * step + x;uint8_t* dTile = dst + x * step + y;transpose_64x64_tile_impl<true>(sTile, step, dTile, step);}}_mm_sfence();}state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(width) * int64_t(height) * 2);
}BENCHMARK_F(TransposeFixture, Direct_8x8_StoreU)(benchmark::State& state) {for (auto _ : state) {for (int y = 0; y < height; y += 8) {const uint8_t* src_row_ptr = src + y * step;for (int x = 0; x < width; x += 8) {uint8_t* dst_block_ptr = dst + x * step + y;transpose_8x8_u8_to_strided(src_row_ptr + x, step, dst_block_ptr, step);}}}state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(width) * int64_t(height) * 2);
}BENCHMARK_MAIN();

测试结果

TransposeFixture/Std_Memcpy                602332 ns       593750 ns         1000 bytes_per_second=52.6316Gi/s
TransposeFixture/Buffered_64x64_Stream     922109 ns       920348 ns          747 bytes_per_second=33.9545Gi/s
TransposeFixture/Direct_8x8_StoreU       12036794 ns     12187500 ns           50 bytes_per_second=2.5641Gi/s

标准库的 memcpy 在 4Kx4K 这种完全顺序、完全对齐、完全不用做重排的场景里,是一个很好的上限参考。
显然,streaming store 在这个场景下确实已经遥遥领先。当然前提是用对。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux ftp 端口查看

在 Linux 系统中,查看 FTP 服务使用的端口通常可以通过以下几种方法实现:方法 1:使用 netstat 命令 sudo netstat -tuln | grep -E 20|21-t: 显示 TCP 端口 -u: 显示 UDP 端口 -l: 显示监听中的端口 -n: 不转换端口…

详细介绍:JavaEE初阶7.0

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

AT_arc195_e [ARC195E] Random Tree Distance gemini-3 Pro题解

优化距离计算 我目前专注于核心概念:距离求和。我正在探索高效的方法来计算所有可能的父序列中所有节点对之间的总距离。组合爆炸是一个真正的障碍,但我正在思考巧妙的策略来降低计算复杂度。目标是找到一个公式,而…

noip10

11.1711.17 这场又是神人模拟赛。 大分题面。 t1 题意比Fwb模糊不清+inf。 赛后统计小样例2大家一共读出了6种答案。 感觉就是描述不清的detect。 读懂后纯简单题。 但有一车细节。 挂细节上了(代码中感叹号处)。 code…

关于jupyterNotebook 的虚拟环境配置

因为默认的python设置是3.13版本,会经常出现版本不配套问题需要用比较旧的python版本 window 11 在anconda prompt中: 创建一个纯净的 Python 3.9 环境 conda create -n bio_final python=3.9 -y 激活它 conda activ…

2025云南冻品批发/冷冻食品批发厂家最新TOP5权威推荐

当前冻品批发领域需求稳步增长,餐饮企业、食品加工厂等采购方对产品品质、品类丰富度及冷链稳定性的要求不断提高,行业竞争也愈发激烈。部分商家存在品质管控缺失、冷链供应链薄弱、产品创新不足等问题,让采购方难以…

Windows11系统安装Docker

Windows11系统安装Docker1. Windows 系统安装 Docker 1.1 Windows 系统启用 Hyper-V 和“容器”功能 开始 -> 搜索并打开控制面板 -> 点击程序重启系统。 1.2 安装 Docker Desktop 在 Docker 官网下载 Docker De…

详细介绍:C++/Java如何与AI深度结合?开发者必看指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

什么?Viggle Ai Pro版会员免费送?

你没看错,目前全球第一梯队的 Ai 视频创作工具 Viggle Ai免费送了~ 不信,图什么? 汇聚全网 Ai 视频创作人,让大家尝尝 Viggle Ai 有多香,用好了你在继续使用~ 没用过 Ai 工具? 这都什么年代了,Ai 工具还用我再絮…

linux ftp 用户权限

在 Linux 系统中,FTP(File Transfer Protocol)服务通常由 vsftpd(Very Secure FTP Daemon)来提供。用户权限管理是 FTP 服务安全性和访问控制的核心部分。一、FTP 用户权限管理 1. FTP 用户的创建与管理 在 Linux…

linux ftp 用户名 密码

在 Linux 系统中,FTP(File Transfer Protocol) 是一种用于在计算机之间传输文件的协议。要进行 FTP 连接,通常需要使用 ftp 命令行工具,或者使用 vsftpd 等 FTP 服务器。 一、使用 ftp 命令行工具连接 FTP 服务器…

linux ftp 用户及目录

在 Linux 系统中,FTP(File Transfer Protocol)通常通过 vsftpd(Very Secure FTP Daemon)来实现。下面我将介绍如何在 Linux 中管理 FTP 用户和目录,包括创建用户、配置目录权限、以及使用 vsftpd 管理 FTP 服务。…

linux ftp 用户创建

在 Linux 系统中,如果你想要创建一个 FTP 用户,通常需要使用 useradd 命令来创建用户,并通过 passwd 命令设置密码。以下是创建 FTP 用户的详细步骤:1. 创建 FTP 用户 使用 useradd 命令创建用户: sudo useradd -…

[豪の算法奇妙冒险] 代码随想录算法训练营第一天 | 704-二分查找、27-移除元素、977-有序数组的平方

LeetCode704 二分查找、LeetCode27 移除元素、LeetCode977 有序数组的平方代码随想录算法训练营第一天 | 704-二分查找、27-移除元素、977-有序数组的平方LeetCode704 二分查找题目链接:https://leetcode.cn/problems…

完整教程:【C语言实战(44)】C语言打造全能简易计算器:突破运算极限

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

【第7章 I/O编程与异常】 `for line in f`及其需要的文件打开模式

for line in f? 以及文件打开模式 一、为什么 for line in f 可以逐行读取文件? 这是Python文件对象的一个「语法糖」特性,本质是利用了文件对象的可迭代性。 1. 什么是「可迭代对象」? 简单说,就是可以用 for 循…

Google 王炸!Gemini 3 Pro 上线:前端能力、代码理解全面进化。

昨天 Google 发布了新模型Gemini 3 Pro,这个模型可以说是异常强大。 目前已经可以在 Gemini CLI 、Gemini 网页、antigravity 中使用。【gzh:和平本记】 Gemini CLI 如何使用? 使用资格: 1)目前 Gemini 3 Pro 已面…

【Agent】MemOS 源码笔记---(1)--基本概念

【Agent】MemOS 源码笔记---(1)--基本概念 目录【Agent】MemOS 源码笔记---(1)--基本概念0x00 概要0x01 背景1.1 为什么需要MemOS1.2 MemOS 相关信息1.3 MemOS能做些什么0x02 原理2.1 记忆类型2.1.1 GeneralTextMemory…

完整教程:GPTBots 工作流:让AI从“会说“到“会做“的技术演进引言:企业AI化的瓶颈在哪里?

完整教程:GPTBots 工作流:让AI从“会说“到“会做“的技术演进引言:企业AI化的瓶颈在哪里?2025-11-19 20:20 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !impo…

html-webpack-plugin扩展创建:自定义钩子构建

html-webpack-plugin扩展创建:自定义钩子构建pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &q…