SIMD and Avx2

SIMD and Avx2SIMD一条指令可以执行多个数据group的计算和输出。对于SIMD相对应的SISD.intel SSE2 ,AVX2,AVX-512假设有一个任务是统计字符串中每一个字符出现的次数,我们可以用128bit的SISD指令进行统计。每8个bit代表一个字符,所以只需要两个SIMD指令(movemask、popcount)。详细测试:#include<stdio.h>#include<thread>#defineINC_TO1000000//o

大家好,又见面了,我是你们的朋友全栈君。

SIMD 一条指令可以执行多个数据group的计算和输出。对于SIMD相对应的SISD.
intel SSE2 , AVX2, AVX-512

假设有一个任务是统计字符串中每一个字符出现的次数,我们可以用128bit 的SISD指令进行统计。每8个bit代表一个字符,所以只需要两个SIMD指令(move mask、pop count)。
在这里插入图片描述

详细测试:

#include <stdio.h>
#include <thread>
#define INC_TO 1000000 // one million...
#include <mutex>
#include <functional>
#include <atomic>
#include <vector>
#include <sstream>
#include <iostream>
#include <emmintrin.h>
#include <immintrin.h>
#include <assert.h> 
#include <x86intrin.h>

struct StringView {
  char* buffer;
  size_t len;
};

void RandomGeneratorFile(const char* filename) {
  FILE* fp =  fopen(filename, "w");
  const size_t numbers = 16*8*1000;
  size_t count =0;
  do {
    for (char i = 'A'; i < 'Z'; i++) {
      fputc(i, fp);
    }
    count++;
  } while (count < numbers);
  fclose(fp);
}

StringView* GetFileContent(const char* filename) {
  FILE* fp = fopen(filename, "r");
  /*Move file point at the end of file.*/
  fseek(fp,0,SEEK_END);
  /*Get the current position of the file pointer.*/
  size_t size=ftell(fp);
  printf("file size:%d\n", size);
  char * buffer = new char[size];
  fseek(fp, 0, SEEK_SET);
  fread(buffer, size, size, fp);
  //  printf("content of buffer:%s\n", buffer);
  fclose(fp);
  StringView* str = new StringView();
  str->buffer = buffer;
  str->len = size;
  return str;
}

// 正常统计字符串
size_t count_chars_8(const char* data, size_t size, const char ch)
{
  size_t total = 0;
  while (size) {
    if (*data == ch)
      total += 1;
    data += 1;
    size -= 1;
  }
  return total;
}

// SIMD
size_t count_chars_128(const char* data, size_t size, const char ch)
{
  size_t total = 0;
  assert(size % 16 == 0);
  // 将ch广播16次
  __m128i tocmp = _mm_set1_epi8(ch);
  while (size) {
    int mask = 0;
    // 从memory 取出128bit数据
    __m128i chunk = _mm_load_si128 ((__m128i const*)data);
    // 对128bit数据进行比较, 返回16bit
    __m128i results = _mm_cmpeq_epi8(chunk, tocmp);
    // 
    mask = _mm_movemask_epi8(results);
    //统计int32的bit位是1的值
    // _mm_ prefix, because it does not operate on 128-bit registers, it just operates on standard 64-bit registers.
    total += _popcnt32(mask);
    data += 16;
    size -= 16;
  }
  return total;
}

// AVX
size_t count_chars_avx(const char* data, size_t size, const char ch)
{
  size_t total = 0;
  assert(size % 16 == 0);
  __m256i tocmp = _mm256_set1_epi8(ch);
  while(size) {
    __m256i chunk = _mm256_loadu_si256((__m256i*)data);
    __m256i results = _mm256_cmpeq_epi8(tocmp, chunk);
    unsigned mask = _mm256_movemask_epi8(results);
    total += __builtin_popcount(mask);
    data += 32;
    size -= 32;
  }
  // printf("count:%d\n", total);
  return total;
}

static void print_time_us(const char* name, 
      size_t(*fn)(const char*, size_t, const char), const char* a,
      size_t size, const char ch) {
    struct timespec start, end;
    clock_gettime(CLOCK_MONOTONIC_RAW, &start);
    fn(a, size, ch);
    clock_gettime(CLOCK_MONOTONIC_RAW, &end);
    uint64_t delta_us = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000;
    printf("Running: '%s' took %llu u/s\n", name, delta_us);
}

int main()
{
  RandomGeneratorFile("test.file");
  StringView* str = GetFileContent("test.file");
  print_time_us("NORMAL", count_chars_8, str->buffer, str->len, 'A');
  print_time_us("SIMD", count_chars_128, str->buffer, str->len, 'A');
  print_time_us("AVX", count_chars_avx, str->buffer, str->len, 'A');
  return 0;
}

编译命令:g++ -std=c++14 main.cc -o main -mavx -mavx2 -O2

输出:

file size:3200000
Running: 'NORMAL' took 2505 u/s
Running: 'SIMD' took 192 u/s
Running: 'AVX' took 97 u/s
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/139752.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • python读取txt中的一列称为_python读取txt文件并取其某一列数据的示例

    python读取txt中的一列称为_python读取txt文件并取其某一列数据的示例python读取txt文件并取其某一列数据的示例菜鸟笔记首先读取的txt文件如下:AAAAF1100003E8180003E1FC0003E7700003FFFC90AAAAF1100003E8240003E2080003E76C0003FFFCA5AAAAF1100003E8140003E2040003E7600003FFFC85AAAAF1100003E7F0…

    2022年5月27日
    45
  • Android中常用的加密方式[通俗易懂]

    Android中常用的加密方式[通俗易懂]Android中常用的加密方式HmacSHA1publicstaticStringgetSignUtil(Stringkey,Stringbase){Log.i(TAG,”getSignUtil:GETSIGN”);Stringtype=”HmacSHA1″;SecretKeySpecsecret=newSecretKeySpec(key.getBytes(),type);Macmac=null;try{

    2022年5月13日
    27
  • 如何用python刷屏_利用python实现在微信群刷屏的方法[通俗易懂]

    hello,我是小小炽,这是我写的第一篇博客,写博客一直都想在写,但是苦于能力尚浅,在各位大牛面前那既然是关公面前耍大刀了,但是其实想来每一个大牛不也是从一个小白慢慢进步学习从而达到一定的高度的吗,而且写博客的意义但不在于炫耀你的成果,而在于分享,听取他人的建议,互相学习,因此我下定决心,每天写一篇博客,不管是小项目还是学习笔记,至少坚持下来,我想一定会有所收获的。好,废话不多说,今天我写的是如何…

    2022年4月15日
    288
  • dirsearch使用方法_in search of的用法

    dirsearch使用方法_in search of的用法文章目录dirsearch扫描的目标扫描的字典类型字典格式设置响应结果的过滤请求相关设置连接相关设置通用设置输出模式常见的用法TIPSdirsearch扫描的目标-u,–url目标url-l,–url-list=FILE目标url文件路径–stdin从标准输入中指定url–cidr目标网段–raw=File从文件

    2022年10月6日
    0
  • 硬盘恢复分区_怎么把efi分区删掉

    硬盘恢复分区_怎么把efi分区删掉Windows系统在安装的时候,会自动为我们的磁盘划分一个恢复分区和一个EFI分区。如果后面不打算再用这些分区的时候,却发现无法删除。本文将提供解决方法。因为误操作会导致数据丢失,所以我将两种不同的解决方法分开成两篇文章以避免干扰:EFI分区/恢复分区不可删除?你需要使用命令行了(配合鼠标操作)EFI分区/恢复分区不可删除?你需要使用命令行了(全命令行操作)本文内容无法删…

    2022年8月11日
    8
  • 自动生成签名在线_签名生成

    自动生成签名在线_签名生成签名设计地址:www.mgs2s.com(复制到浏览器打开)工具集成签名设计免费版下载,签名设计免费版在线立即生成,简单简体签名设计免费版。最新方便设计公文签名设计颜色保存分享免费版 1、个别字体样式文字不完整,请换一种字体继续转换!增加个性签名图片尺寸,更改文件格式的同时缩小了文件大小,生成速度更快,下载更方便。兼顾使用手机上网的朋友们!    2、朋友们大家早上好,今天有空来给大家分享一下关于…

    2022年9月6日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号