高精度矢量內積計算方法 (單精度浮點, 超長矢量)

高精度矢量內積計算方法 (單精度浮點, 超長矢量)

對于單精度浮點類型的超長矢量(超過1億元素)內積計算,累加誤差確實是一個重要問題。以下是幾種減少誤差的方法:

1. Kahan求和算法

這是最常用的補償求和算法,可以有效減少累加誤差:

float kahan_dot_product(const float* a, const float* b, size_t n) {float sum = 0.0f;float c = 0.0f; // 補償變量for (size_t i = 0; i < n; i++) {float product = a[i] * b[i];float y = product - c;float t = sum + y;c = (t - sum) - y;sum = t;}return sum;
}

2. 成對求和 (Pairwise Summation)

將求和過程組織為二叉樹形式,減少大數吃小數的機會:

float pairwise_dot(const float* a, const float* b, size_t start, size_t end) {if (end - start <= 128) { // 基礎塊大小可調整float sum = 0.0f;for (size_t i = start; i < end; i++) {sum += a[i] * b[i];}return sum;}size_t mid = start + (end - start) / 2;float left = pairwise_dot(a, b, start, mid);float right = pairwise_dot(a, b, mid, end);return left + right;
}

3. 多精度累加器

使用雙精度或更高精度累加器:

float high_precision_dot(const float* a, const float* b, size_t n) {double sum = 0.0;for (size_t i = 0; i < n; i++) {sum += (double)a[i] * (double)b[i];}return (float)sum;
}

4. 分塊并行計算

對于超長矢量,可以結合并行計算和誤差控制:

float parallel_dot(const float* a, const float* b, size_t n, int num_threads) {std::vector<double> partial_sums(num_threads, 0.0);#pragma omp parallel for num_threads(num_threads)for (int t = 0; t < num_threads; t++) {size_t start = t * n / num_threads;size_t end = (t + 1) * n / num_threads;double local_sum = 0.0;for (size_t i = start; i < end; i++) {local_sum += (double)a[i] * (double)b[i];}partial_sums[t] = local_sum;}double total = 0.0;for (double s : partial_sums) {total += s;}return (float)total;
}

實際應用建議

  1. 對于GPU計算:使用Kahan求和或成對求和,因為雙精度運算在GPU上可能性能較差
  2. 對于CPU計算:優先考慮雙精度累加器方法,簡單且有效
  3. 混合精度:可以考慮使用融合乘加(FMA)指令結合補償算法
  4. 內存訪問優化:確保數據連續訪問,減少緩存未命中

誤差分析

對于1億個單精度數的內積:

  • 普通累加:相對誤差可能達到1e-4量級
  • Kahan求和:誤差可降低到1e-8量級
  • 雙精度累加:誤差可降低到1e-16量級

選擇哪種方法取決于您的精度要求和計算環境限制。


測試代碼

#include <cmath>
#include <iostream>
#include <iomanip>
#include <omp.h>
#include <cstdlib>
#include <immintrin.h>using namespace std;//編譯: g++ -DAVX2 -DAVX512F -std=c++17 -Ofast -march=native -fopenmp test_dot.cpp/********************************************************<x,y>內積計算用double雙精度做累加類型,保證數值穩定
**********************************************************/template<typename F,int P=0>
F dot(int n, const F *x, const F *y)
{if constexpr (P==0){double s_time=omp_get_wtime();//累加用單精度F s=0;for(int i=0; i<n; i++){s+=x[i]*y[i];}double e_time=omp_get_wtime();cout<<"P="<<P<<": time used "<<e_time-s_time<<endl;return s;}else if constexpr (P==1){double s_time=omp_get_wtime();//累加用雙精度,乘法用單精度double s=0;for(int i=0; i<n; i++){s+=x[i]*y[i];}double e_time=omp_get_wtime();cout<<"P="<<P<<": time used "<<e_time-s_time<<endl;return s;}else if constexpr (P==2){double s_time=omp_get_wtime();//累加用雙精度,乘法用雙精度double s=0;for(int i=0; i<n; i++){double a=x[i];double b=y[i];s+=a*b;}double e_time=omp_get_wtime();cout<<"P="<<P<<": time used "<<e_time-s_time<<endl;return s;}
#ifdef AVX2 else if constexpr(P==3){static_assert(is_same_v<F,float>);/********************************************************************OpenMP多線程,AVX2計算<x,x>,<x,y>累加和乘法都用雙精度	*********************************************************************/double s_time=omp_get_wtime();__m256d xx_sum,xy_sum;xx_sum=xy_sum=_mm256_setzero_pd();#pragma omp parallel{__m256d sum_xx=_mm256_setzero_pd();__m256d sum_xy=_mm256_setzero_pd();#pragma omp for nowaitfor(int i=0; i<n; i+=8){__m256 x8=_mm256_loadu_ps(x+i);__m256 y8=_mm256_loadu_ps(y+i);__m128 lo,hi;__m256d t1,t2,t3,t4;lo=_mm256_extractf128_ps(x8,0);hi=_mm256_extractf128_ps(x8,1);t1=_mm256_cvtps_pd(lo);t2=_mm256_cvtps_pd(hi);lo=_mm256_extractf128_ps(y8,0);hi=_mm256_extractf128_ps(y8,1);t3=_mm256_cvtps_pd(lo);t4=_mm256_cvtps_pd(hi);
#if 0				sum_xx=_mm256_add_pd(sum_xx,_mm256_mul_pd(t1,t1));sum_xx=_mm256_add_pd(sum_xx,_mm256_mul_pd(t2,t2));sum_xy=_mm256_add_pd(sum_xy,_mm256_mul_pd(t1,t3));sum_xy=_mm256_add_pd(sum_xy,_mm256_mul_pd(t2,t4));
#else/**********************************FMA***********************************/sum_xx=_mm256_fmadd_pd(t1,t1,sum_xx);sum_xx=_mm256_fmadd_pd(t2,t2,sum_xx);sum_xy=_mm256_fmadd_pd(t1,t3,sum_xy);sum_xy=_mm256_fmadd_pd(t2,t4,sum_xy);
#endif}#pragma omp critical{xx_sum=_mm256_add_pd(xx_sum,sum_xx);xy_sum=_mm256_add_pd(xy_sum,sum_xy);}}double tmp[4];_mm256_storeu_pd(tmp,xy_sum);double xy=tmp[0]+tmp[1]+tmp[2]+tmp[3];_mm256_storeu_pd(tmp,xx_sum);double xx=tmp[0]+tmp[1]+tmp[2]+tmp[3];for(int i=n&~7; i<n; i++){double a=x[i];double b=y[i];xx+=a*a;xy+=a*b;}double e_time=omp_get_wtime();cout<<"P="<<P<<": time used "<<e_time-s_time<<endl;cout<<"xx="<<xx<<endl;return xy;}//P==3
#endif 	
#ifdef AVX512F 	else if constexpr(P==4){static_assert(is_same_v<F,float>);/********************************************************************OpenMP多線程,AVX512F計算<x,x>,<x,y>累加和乘法都用雙精度	*********************************************************************/double s_time=omp_get_wtime();__m512d xx_sum=_mm512_setzero_pd();__m512d xy_sum=_mm512_setzero_pd();#pragma omp parallel{__m512d sum_xx=_mm512_setzero_pd();__m512d sum_xy=_mm512_setzero_pd();#pragma omp for nowaitfor(int i=0; i<n; i+=16){__m512 x16=_mm512_loadu_ps(x+i);__m512 y16=_mm512_loadu_ps(y+i);__m256 lo,hi;__m512d t1,t2,t3,t4;lo=_mm512_extractf32x8_ps(x16,0);hi=_mm512_extractf32x8_ps(x16,1);t1=_mm512_cvtps_pd(lo);t2=_mm512_cvtps_pd(hi);lo=_mm512_extractf32x8_ps(y16,0);hi=_mm512_extractf32x8_ps(y16,1);t3=_mm512_cvtps_pd(lo);t4=_mm512_cvtps_pd(hi);
#if 0				sum_xx=_mm512_add_pd(sum_xx,_mm512_mul_pd(t1,t1));sum_xx=_mm512_add_pd(sum_xx,_mm512_mul_pd(t2,t2));sum_xy=_mm512_add_pd(sum_xy,_mm512_mul_pd(t1,t3));sum_xy=_mm512_add_pd(sum_xy,_mm512_mul_pd(t2,t4));
#else/***********************************FMA************************************/sum_xx=_mm512_fmadd_pd(t1,t1,sum_xx);sum_xx=_mm512_fmadd_pd(t2,t2,sum_xx);sum_xy=_mm512_fmadd_pd(t1,t3,sum_xy);sum_xy=_mm512_fmadd_pd(t2,t4,sum_xy);
#endif}#pragma omp critical{xx_sum=_mm512_add_pd(xx_sum,sum_xx);xy_sum=_mm512_add_pd(xy_sum,sum_xy);}}double tmp[8];_mm512_storeu_pd(tmp,xy_sum);double xy=tmp[0]+tmp[1]+tmp[2]+tmp[3]+tmp[4]+tmp[5]+tmp[6]+tmp[7];_mm512_storeu_pd(tmp,xx_sum);double xx=tmp[0]+tmp[1]+tmp[2]+tmp[3]+tmp[4]+tmp[5]+tmp[6]+tmp[7];for(int i=n&~15; i<n; i++){double a=x[i];double b=y[i];xx+=a*a;xy+=a*b;}double e_time=omp_get_wtime();cout<<"P="<<P<<": time used "<<e_time-s_time<<endl;cout<<"xx="<<xx<<endl;return xy;}
#endif return(0);}const int N=50000000;void test()
{using FLOAT=float;FLOAT *x=new FLOAT[N];FLOAT *y=new FLOAT[N];for(int i=0; i<N; i++){FLOAT t=0.001*sqrtf(FLOAT(i));FLOAT s=sqrt(sqrt(FLOAT(i)));x[i]=(rand()<RAND_MAX/2)?t:-t;y[i]=(rand()<RAND_MAX/2)?s:-s;}cout<<setprecision(15)<<endl;cout<<(double)dot<FLOAT,0>(N,x,y)<<endl;cout<<(double)dot<FLOAT,1>(N,x,y)<<endl;cout<<(double)dot<FLOAT,2>(N,x,y)<<endl;#ifdef AVX2	cout<<(double)dot<FLOAT,3>(N,x,y)<<endl;
#endif #ifdef AVX512F	cout<<(double)dot<FLOAT,4>(N,x,y)<<endl;
#endif }int main(int argc, char **argv)
{test();return(0);
}

資料

Intel /intrinsics-guide

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/74731.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/74731.shtml
英文地址,請注明出處:http://en.pswp.cn/web/74731.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java基礎:Logback日志框架

什么是日志 日志技術 可以將系統執行信息&#xff0c;方便的記錄到指定位置&#xff08;控制臺&#xff0c;文件中&#xff0c;數據庫中&#xff09; 可以隨時可以開關的形式控制日志的啟停&#xff0c;無需侵入到源代碼中去進行修改 LogBack日志框架 LogBack快速入門 logb…

MessageQueue --- RabbitMQ WorkQueue and Prefetch

MessageQueue --- RabbitMQ WorkQueue and Prefetch 什么是WorkQueue分發機制 --- RoundRobin分發機制 --- PrefetchSpring example use prefetch --- Fair Dispatch 什么是WorkQueue Work queues&#xff0c;任務模型。簡單來說就是讓多個消費者綁定到一個隊列&#xff0c;共同…

RNN模型與NLP應用——(9/9)Self-Attention(自注意力機制)

聲明&#xff1a; 本文基于嗶站博主【Shusenwang】的視頻課程【RNN模型及NLP應用】&#xff0c;結合自身的理解所作&#xff0c;旨在幫助大家了解學習NLP自然語言處理基礎知識。配合著視頻課程學習效果更佳。 材料來源&#xff1a;【Shusenwang】的視頻課程【RNN模型及NLP應用…

詳解AI采集框架Crawl4AI,打造智能網絡爬蟲

大家好&#xff0c;Crawl4AI作為開源Python庫&#xff0c;專門用來簡化網頁爬取和數據提取的工作。它不僅功能強大、靈活&#xff0c;而且全異步的設計讓處理速度更快&#xff0c;穩定性更好。無論是構建AI項目還是提升語言模型的性能&#xff0c;Crawl4AI都能幫您簡化工作流程…

從零開始玩python--python版植物大戰僵尸來襲

大家好呀&#xff0c;小伙伴們&#xff01;今天要給大家介紹一個超有趣的Python項目 - 用pygame制作植物大戰僵尸游戲的進階版本。相信不少小伙伴都玩過這款經典游戲&#xff0c;今天我們就用Python來實現它&#xff0c;讓編程學習變得更加有趣&#xff01;&#x1f31f; 一、…

圖解AUTOSAR_SWS_FlashTest

AUTOSAR Flash Test模塊詳解 基于AUTOSAR 4.4.0規范的Flash測試模塊分析與圖解 目錄 概述 1.1 Flash Test模塊的作用 1.2 工作原理架構設計 2.1 整體架構 2.2 依賴關系狀態管理 3.1 狀態轉換圖 3.2 前臺與后臺測試模式配置結構 4.1 配置類圖 4.2 關鍵配置參數交互流程 5.1 序列…

【mongodb】mongodb的字段類型

目錄 1. 基本數據類型1.1 String1.2 Number1.3 Boolean1.4 Date1.5 Null1.6 ObjectId1.7 Array1.8 Binary Data1.9 Object 2. 特殊數據類型2.1 Regular Expression2.2 JavaScript2.3 Symbol2.4 Decimal1282.5 Timestamp2.6 MinKey/MaxKey2.7 DBPointer 3. 常用字段類型示例4. 注…

MySQL篇(五)MySQL主從同步原理深度剖析

MySQL篇&#xff08;五&#xff09;MySQL主從同步原理深度剖析 MySQL篇&#xff08;五&#xff09;MySQL主從同步原理深度剖析一、引言二、MySQL主從同步基礎概念主庫&#xff08;Master&#xff09;從庫&#xff08;Slave&#xff09;二進制日志&#xff08;Binary Log&#x…

論文學習16:Learning Transferable Visual Models From Natural Language Supervision

代碼來源 Learning Transferable Visual Models From Natural Language Supervisionhttps://arxiv.org/pdf/2103.00020 模塊作用 當前最先進的計算機視覺系統被訓練用于預測一組固定的、預先定義的目標類別。這種受限的監督方式限制了它們的通用性和可用性&#xff0c;因為要…

[MySQL初階]MySQL(9)事務機制

標題&#xff1a;[MySQL初階]MySQL&#xff08;9&#xff09;事物機制 水墨不寫bug 文章目錄 一、認識事務1、多線程訪問數據庫出現的問題2、對CURD的限制是通過事務機制實現的3、事務的四個屬性4、哪些引擎支持事務 二、事務的提交與autocommit設置三、事務的隔離性和隔離級別…

spring-cloud-alibaba-nacos-config使用說明

一、核心功能與定位 Spring Cloud Alibaba Nacos Config 是 Spring Cloud Alibaba 生態中的核心組件之一&#xff0c;專為微服務架構提供動態配置管理能力。它通過整合 Nacos 的配置中心功能&#xff0c;替代傳統的 Spring Cloud Config&#xff0c;提供更高效的配置集中化管理…

SonarQube數據庫配置

SonarQube部署完成后&#xff0c;在瀏覽器地址欄輸入http://IP:9000可以進入登錄頁面&#xff0c;以本機運行為例&#xff0c;地址為http://127.0.0.1:9000/&#xff0c;默認登錄名&#xff1a;admin&#xff0c;登錄密碼也是admin。登錄后會要求設置密碼&#xff1a; 按要求設…

醫藥檔案區塊鏈系統

1. 醫生用戶模塊?? ??目標用戶??&#xff1a;醫護人員 ??核心功能??&#xff1a; ??檢索檔案??&#xff1a;通過關鍵詞或篩選條件快速定位患者健康檔案。??請求授權??&#xff1a;向個人用戶發起檔案訪問權限申請&#xff0c;需經對方確認。??查看檔案?…

CSS3學習教程,從入門到精通, 化妝品網站 HTML5 + CSS3 完整項目(26)

化妝品網站 HTML5 CSS3 完整項目 下面是一個完整的化妝品網站項目&#xff0c;包含主頁、登錄頁面和注冊頁面。我將按照您的要求提供詳細的代碼和注釋。 1. 網站規劃與需求分析 需求分析 展示化妝品產品信息提供用戶注冊和登錄功能響應式設計&#xff0c;適配不同設備美觀…

ROS2 多機時間同步(Chrony配置簡明指南)

適用場景&#xff1a; 主機運行 ROS2 Humble&#xff08;發布 /scan 等&#xff09;&#xff0c;板子運行 ROS2 Foxy&#xff08;發布 /tf 等&#xff09;&#xff0c;兩邊通過 ROS_DOMAIN_ID 跨平臺通訊。需要保證系統時間對齊&#xff0c;避免 TF 插值失敗、建圖抖動等問題。…

Nginx配置偽靜態,URL重寫

Nginx配置偽靜態&#xff0c;URL重寫 [ Nginx ] 在Nginx低版本中&#xff0c;是不支持PATHINFO的&#xff0c;但是可以通過在Nginx.conf中配置轉發規則實現&#xff1a; location / { // …..省略部分代碼if (!-e $request_filename) {rewrite ^(.*)$ /index.php?s/$1 l…

電路筆記(元器件):ADC LTC系列模數轉換器的輸出范圍+滿量程和偏移調整

LTC1740(LTC1740官方文檔)是Analog Devices&#xff08;原Linear Technology&#xff09;公司生產的一款高性能、低功耗的14位模數轉換器(ADC)。它通常用于需要高精度和快速采樣率的應用中&#xff0c;如通信系統、數據采集設備等。同類產品 LTC1746&#xff1a;一款14位、40Ms…

續-算法-數學知識

3、歐拉函數 1、定義&#xff1a; 1~n 中與 n 互質的數的個數 例如&#xff1a;6 的有 1 2 3 4 5 6 其中&#xff0c;與 n 互質 的 數的個數為 2個分別是&#xff1a;1、5 2、計算&#xff1a; $ N p_1^{a1} p_2^{a2} p_3^{a3} … p_k^{ak} $&#xff08;例如&#x…

C/C++測試框架googletest使用示例

文章目錄 文檔編譯安裝示例參考文章 文檔 https://github.com/google/googletest https://google.github.io/googletest/ 編譯安裝 googletest是cmake項目&#xff0c;可以用cmake指令編譯 cmake -B build && cmake --build build將編譯產物lib和include 兩個文件夾…

LintCode第974題-求矩陣各節點的最短路徑(以0為標準)

描述 給定一個由0和1組成的矩陣&#xff0c;求每個單元格最近的0的距離。 兩個相鄰細胞之間的距離是1。 給定矩陣的元素數不超過10,000。 在給定的矩陣中至少有一個0。 單元格在四個方向上相鄰:上&#xff0c;下&#xff0c;左和右。 樣例 例1: 輸入: [[0,0,0],[0,0,0],[0…