为啥 load_ps() 可以在一台 PC 上工作,而不能在另一台 PC 上工作?

Posted

技术标签:

【中文标题】为啥 load_ps() 可以在一台 PC 上工作,而不能在另一台 PC 上工作?【英文标题】:Why is load_ps() working on one PC but not on another?为什么 load_ps() 可以在一台 PC 上工作,而不能在另一台 PC 上工作? 【发布时间】:2018-05-22 13:48:25 【问题描述】:

我编写了以下代码来缩放一组数字:

 #include <stdio.h>
 #include <stdlib.h>
 #include <math.h>
 #include "immintrin.h"

 void scale(struct problem_param prob_param, float* features)
 
    int i,j,k;
    for (j = 0; j < prob_param.Nr_ft; j++)
    
        __m256      range_vec ,low_up_vec , low_vec,tmp_vec;
        __m256      feat_min_vec, feat_vec;
        unsigned    count           = prob_param.Size;
        unsigned    offset          = j * prob_param.Size;
        float       feature_max     = features[offset];
        float       feature_min     = features[offset];
        /*
         * Look for min and max of each feature.
         */
        for ( i = 1; i < prob_param.Size ; i++)
        
            if (features[i + offset] > feature_max )        feature_max = features[i + offset];

            if (features[i + offset] < feature_min )    feature_min = features[i + offset];
        
        printf("feature : %u \t min = %f \t max = %f \n",j,feature_min,feature_max);
        /*
         * Set the range.
         * Set constant vectors for the vector instructions.
         */
        float       range   = feature_max - feature_min;
        feat_min_vec        = _mm256_set1_ps (feature_min);
        range_vec           = _mm256_set1_ps (range);
        low_up_vec          = _mm256_set1_ps (prob_param.upper_limit - prob_param.lower_limit);
        low_vec             = _mm256_set1_ps (prob_param.lower_limit);
        /*
         * Normalising
         * -----------
         * Head
         */
        for ( i = 0; i < prob_param.Size && count >= 7 ; i+=8)
        
            feat_vec    = _mm256_load_ps(&features[i + offset]);
            tmp_vec     = _mm256_sub_ps(feat_vec,feat_min_vec);
            tmp_vec     = _mm256_mul_ps(tmp_vec,low_up_vec);
            tmp_vec     = _mm256_div_ps(tmp_vec,range_vec);
            feat_vec    = _mm256_add_ps(tmp_vec,low_vec);

            _mm256_store_ps (&features[i + offset], feat_vec);

            count -=8;
        
        /*
         * Normalising
         * -----------
         * Tail
         */
        for ( k = i; k < prob_param.Size ; k++)
        
            features[k + offset] = prob_param.lower_limit + (prob_param.upper_limit - prob_param.lower_limit) * (features[k + offset] - feature_min) / range;
        
    

这是负责缩放的函数,我这样称呼它:

#include <stdio.h>
#include <stdlib.h>

#include "data.h" 
#include "common.h"

#define     training_size       3089
#define     number_features     4
#define     low                 -1.0
#define     up                  1.0
float* feature_array;

int main()

  struct problem_param pp;

  pp.Size           =       training_size;
  pp.Nr_ft          =       number_features;
  pp.lower_limit    =       low;
  pp.upper_limit    =       up;

posix_memalign((void **) &feature_array, 32, (size_t) training_size * number_features *sizeof(float));

scale(pp,feature_array);

return EXIT_SUCCESS;

我用我的 MacBook Pro Core i5 Haswell 测试了这段代码,它可以工作,但是当我用 ASUS Core I7 Haswell 测试它时,它显示了加载的分段错误。我错过了什么吗?

【问题讨论】:

请注意,将float ** 转换为void ** 是不合适的。相反,你真的应该使用一个临时的void * 变量。 我猜你有一个与 load_ps 无关的 UB... 【参考方案1】:

offset(因此i + offset)的值并不总是 8 的倍数(在上面的示例中它等于 0、3089、6178、9267),因此您的加载和存储内在函数将一般会错位。

最简单的解决方案是使用_mm256_loadu_ps 代替_mm256_load_ps,并使用_mm256_storeu_ps 代替_mm256_store_ps

至于为什么这似乎可以在您的 MacBook Pro 上运行,我的猜测是,clang 会在您背后生成未对齐的加载/存储指令,从而隐藏问题,直到您尝试在具有不同编译器的系统上运行代码。

更新:我刚刚通过编译和反汇编生成的代码验证了上述假设(在带有 macOS 10.13.4 和 Xcode 9.3.1 的 Haswell MacBook Pro 上):

>>> vmovups (%r14,%r13,4), %ymm0
    vsubps  192(%rsp), %ymm0, %ymm0 ## 32-byte Folded Reload
    vmulps  448(%rsp), %ymm0, %ymm0 ## 32-byte Folded Reload
    vdivps  384(%rsp), %ymm0, %ymm0 ## 32-byte Folded Reload
    vaddps  416(%rsp), %ymm0, %ymm0 ## 32-byte Folded Reload
>>> vmovups %ymm0, (%r14,%r13,4)

注意使用vmovups 而不是vmovaps

【讨论】:

以上是关于为啥 load_ps() 可以在一台 PC 上工作,而不能在另一台 PC 上工作?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 SQL 开发人员从另一台计算机访问安装在一台计算机上的 oracle db(以便 2 人可以在同一个数据库上协同工作)

为啥 Spring 在一台机器上而不是另一台机器上出现循环依赖问题?

应用程序的单选按钮需要在一台 PC 上按*双*加速键

为啥我的 Perl 程序在一台机器上得到污染警告,而在另一台机器上却没有?

我如何在一台 Linux PC 中使用 Opencv 构建到另一台?

为啥谷歌播放商店稳定性报告说我的应用程序在一台设备上崩溃了[重复]