- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我一直在使用 altivec 实现基本的数学运算,作为为即将进行的项目学习 simd 的一种方式。另外,作为查看其性能优势的一种方式,我跟踪了执行操作所需的时间,但我遇到了一些奇怪的情况。
我做的第一件事是将两个 vector 相加并减去两个 vector 。这很好用。我做的下一件事是将两个 vector 相乘。然而,乘法比加法更快,尽管根据我的特定 CPU 数据表中有关所使用指令的说明,用于加法和乘法的时钟周期更少。
我有两个数组,每个数组大小为 10MB,并通过这两个例程运行它们:
void av_AddValues(int32_t* intArrayA, int32_t* intArrayB, int32_t* outputBuffer, int size)
{
int iterations = size / (sizeof(__vector int32_t) / sizeof(int32_t));
__vector int32_t* tempA = (__vector int32_t *) intArrayA;
__vector int32_t* tempB = (__vector int32_t *) intArrayB;
__vector int32_t* tempOut = (__vector int32_t *) outputBuffer;
for(int i = 0; i < iterations; i++)
{
__vector int32_t sum = vec_add(*tempA, *tempB);
vec_st(sum, 0, tempOut);
tempA++;
tempB++;
tempOut++;
}
}
void av_MultiplyValues(int16_t* intArrayA, int16_t* intArrayB, int32_t* outputBuffer, int size)
{
int iterations = size / (sizeof(__vector int16_t) / sizeof(int16_t));
__vector int16_t* tempA = (__vector int16_t *) intArrayA;
__vector int16_t* tempB = (__vector int16_t *) intArrayB;
__vector int32_t* tempOut = (__vector int32_t *) outputBuffer;
for(int i = 0; i < iterations; i++)
{
__vector int32_t productEven = vec_mule(*tempA, *tempB);
__vector int32_t productOdd = vec_mulo(*tempA, *tempB);
__vector int32_t mergedProductHigh = vec_mergeh(productEven, productOdd);
__vector int32_t mergedProductLow = vec_mergel(productEven, productOdd);
vec_st(mergedProductHigh, 0, tempOut);
tempOut++;
vec_st(mergedProductLow, 0, tempOut);
tempA++;
tempB++;
tempOut++;
}
}
在我的特定平台上,av_AddValues 需要 81ms 来处理,av_MultiplyValues 需要 48ms 来处理。 (使用 std::chrono::high_resolution_clock 记录的时间)
为什么乘法的处理时间比加法要少?
考虑到 __vector 类型总是处理 16 字节的数据,我认为 32 位值相加与 16 位值相乘并没有什么区别。
我的第一个想法是,由于将数字相加是一项微不足道的任务,因此 CPU 完成操作的速度比从内存中获取数据的速度要快。而在乘法中,这种获取延迟被 CPU 忙于工作而无需等待那么长时间的事实所隐藏。
这是一个正确的假设吗?
完整代码:
#include <chrono>
#include <random>
#include <limits>
#include <iostream>
#include <cassert>
#include <cstring>
#include <cstdint>
#include <malloc.h>
#include <altivec.h>
#undef vector
void GenerateRandom16bitValues(int16_t* inputABuffer, int16_t* inputBBuffer, int32_t* outputBuffer, int size);
void GenerateRandom32bitValues(int32_t* inputABuffer, int32_t* inputBBuffer, int32_t* outputBuffer, int size);
void TestAdd();
void TestMultiply();
void av_AddValues(int32_t* intArrayA, int32_t* intArrayB, int32_t* outputBuffer, int size);
void av_MultiplyValues(int16_t* intArrayA, int16_t* intArrayB, int32_t* outputBuffer, int size);
int main()
{
TestAdd();
TestMultiply();
}
void GenerateRandom16bitValues(int16_t* inputABuffer, int16_t* inputBBuffer, int32_t* outputBuffer, int size)
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(std::numeric_limits<int16_t>::min(), std::numeric_limits<int16_t>::max());
for(int i = 0; i < size; i++)
{
inputABuffer[i] = dis(gen);
inputBBuffer[i] = dis(gen);
outputBuffer[i] = 0;
}
}
void GenerateRandom32bitValues(int32_t* inputABuffer, int32_t* inputBBuffer, int32_t* outputBuffer, int size)
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::max());
for(int i = 0; i < size; i++)
{
inputABuffer[i] = dis(gen);
inputBBuffer[i] = dis(gen);
outputBuffer[i] = 0;
}
}
void TestAdd()
{
int size = 10'485'760;
int bytes = size * sizeof(int32_t);
int32_t* inputABuffer = (int32_t*) memalign(64, bytes);
int32_t* inputBBuffer = (int32_t*) memalign(64, bytes);
int32_t* outputBuffer = (int32_t*) memalign(64, bytes);
assert(inputABuffer != nullptr);
assert(inputBBuffer != nullptr);
assert(outputBuffer != nullptr);
GenerateRandom32bitValues(inputABuffer, inputBBuffer, outputBuffer, size);
for(int i = 0; i < 20; i++)
{
auto start = std::chrono::high_resolution_clock::now();
av_AddValues(inputABuffer, inputBBuffer, outputBuffer, size);
auto end = std::chrono::high_resolution_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
for(int k = 0; k < size; k++)
{
assert(outputBuffer[k] == (inputABuffer[k] + inputBBuffer[k]));
}
std::cout << "Vector Sum - " << diff.count() << "ms\n";
memset(outputBuffer, 0, size);
}
}
void TestMultiply()
{
int size = 10'485'760;
int16_t* inputABuffer = (int16_t*) memalign(64, size * sizeof(int16_t));
int16_t* inputBBuffer = (int16_t*) memalign(64, size * sizeof(int16_t));
int32_t* outputBuffer = (int32_t*) memalign(64, size * sizeof(int32_t));
assert(inputABuffer != nullptr);
assert(inputBBuffer != nullptr);
assert(outputBuffer != nullptr);
GenerateRandom16bitValues(inputABuffer, inputBBuffer, outputBuffer, size);
for(int i = 0; i < 20; i++)
{
auto start = std::chrono::high_resolution_clock::now();
av_MultiplyValues(inputABuffer, inputBBuffer, outputBuffer, size);
auto end = std::chrono::high_resolution_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
for(int k = 0; k < size; k++)
{
assert(outputBuffer[k] == (inputABuffer[k] * inputBBuffer[k]));
}
std::cout << "Vector product - " << diff.count() << "ms\n";
memset(outputBuffer, 0, size);
}
}
void av_AddValues(int32_t* intArrayA, int32_t* intArrayB, int32_t* outputBuffer, int size)
{
int iterations = size / (sizeof(__vector int32_t) / sizeof(int32_t));
__vector int32_t* tempA = (__vector int32_t *) intArrayA;
__vector int32_t* tempB = (__vector int32_t *) intArrayB;
__vector int32_t* tempOut = (__vector int32_t *) outputBuffer;
for(int i = 0; i < iterations; i++)
{
__vector int32_t sum = vec_add(*tempA, *tempB);
vec_st(sum, 0, tempOut);
tempA++;
tempB++;
tempOut++;
}
}
void av_MultiplyValues(int16_t* intArrayA, int16_t* intArrayB, int32_t* outputBuffer, int size)
{
int iterations = size / (sizeof(__vector int16_t) / sizeof(int16_t));
__vector int16_t* tempA = (__vector int16_t *) intArrayA;
__vector int16_t* tempB = (__vector int16_t *) intArrayB;
__vector int32_t* tempOut = (__vector int32_t *) outputBuffer;
for(int i = 0; i < iterations; i++)
{
__vector int32_t productEven = vec_mule(*tempA, *tempB);
__vector int32_t productOdd = vec_mulo(*tempA, *tempB);
__vector int32_t mergedProductHigh = vec_mergeh(productEven, productOdd);
__vector int32_t mergedProductLow = vec_mergel(productEven, productOdd);
vec_st(mergedProductHigh, 0, tempOut);
tempOut++;
vec_st(mergedProductLow, 0, tempOut);
tempA++;
tempB++;
tempOut++;
}
}
性能统计和性能记录的输出:
Adding
Performance counter stats for './alti':
2151.146080 task-clock (msec) # 0.999 CPUs utilized
9 context-switches # 0.004 K/sec
0 cpu-migrations # 0.000 K/sec
30957 page-faults # 0.014 M/sec
3871497132 cycles # 1.800 GHz
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
1504538891 instructions # 0.39 insns per cycle
234038234 branches # 108.797 M/sec
687912 branch-misses # 0.29% of all branches
270305159 L1-dcache-loads # 125.656 M/sec
79819113 L1-dcache-load-misses # 29.53% of all L1-dcache hits
<not supported> LLC-loads
<not supported> LLC-load-misses
2.152697186 seconds time elapsed
CPU Utilization
76.04% alti alti [.] av_AddValues
Multiply
Performance counter stats for './alti':
1583.016640 task-clock (msec) # 0.999 CPUs utilized
4 context-switches # 0.003 K/sec
0 cpu-migrations # 0.000 K/sec
20717 page-faults # 0.013 M/sec
2849050875 cycles # 1.800 GHz
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
1520409634 instructions # 0.53 insns per cycle
179185029 branches # 113.192 M/sec
535437 branch-misses # 0.30% of all branches
205341530 L1-dcache-loads # 129.715 M/sec
27124936 L1-dcache-load-misses # 13.21% of all L1-dcache hits
<not supported> LLC-loads
<not supported> LLC-load-misses
1.584145737 seconds time elapsed
CPU Utilization
60.35% alti alti [.] av_MultiplyValues
最佳答案
这与输入缓冲区的大小有关。
在一种情况下(TestAdd):
int size = 10'485'760;
int bytes = size * sizeof(int32_t);
int32_t* inputABuffer = (int32_t*) memalign(64, bytes);
int32_t* inputBBuffer = (int32_t*) memalign(64, bytes);
int32_t* outputBuffer = (int32_t*) memalign(64, bytes);
您分配 3 * size * 4 字节 (sizeof(int32_t) = 4)
在另一个(test_mul)中:
int size = 10'485'760;
int16_t* inputABuffer = (int16_t*) memalign(64, size * sizeof(int16_t));
int16_t* inputBBuffer = (int16_t*) memalign(64, size * sizeof(int16_t));
int32_t* outputBuffer = (int32_t*) memalign(64, size * sizeof(int32_t));
您分配 size*4 + 2*size*2 (sizeof(int16_t) = 2)
由于此代码完全受内存限制,因此第二个代码的速度为 (3*4)/(4 + 2*2) = 快 1.5 倍。
这与您的测量结果一致,因为 2.15/1.5 = 1.43,接近 1.58。
关于c - SIMD 与 Altivec : why is multiplying two vectors faster than adding two vectors?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44273494/
我有一个场景,我们必须通过 Azure AD 对企业用户进行身份验证,但通过 Azure AD B2C 对外部用户进行身份验证——所有这些都来自同一个登录屏幕。 有一些 Web 应用程序将共享此功能。
在使用 Azure AD B2C 和 Azure AD B2B 之前,我通常会将应用程序添加到我们租户的 Azure AD 中,并且 Office 365 用户可以使用其帐户 (SSO) 访问应用程序
当 Azure Active Directory 信任访问本地 Active Directory 用户时,我们是否可以使用本地 AD 用户名(域限定的 sam 帐户名称,例如:cosmos\brahm
什么是在网站上展示广告的好托管广告管理器? 我听说过OpenX ,但从未使用过。 最佳答案 我们使用名为 Ad Serving Solutions http://www.adservingsoluti
是否可以将用户从云 Azure Active Directory 同步到本地 AD? On Premises 这里有点错误,因为它实际上是 Azure 中的虚拟网络,带有 Windows Server
我正在关注这里的答案:Multi-Tenant Azure AD Auth in Azure AD B2C with Custom Policies 以及这里的演练:https://github.co
我正在尝试使用/common Azure AD 端点在 Azure AD B2C 中使用 Azure AD Auth。根据How to sign in any Azure Active Directo
来自 Mercurial 文档: The manifest is the file that describes the contents of the repository at a particu
我正在尝试将 firebase admob 与 React Native 集成到我的应用程序中,一切都适用于 testID横幅 ('ca-app-pub-3940256099942544/293473
我有一个应用程序需要根据其本地 AD 通用名称来过滤权限。几点注意事项: Azure AD Connect 正在 OnPrem AD 和 Azure 之间同步数据 我已成功将登录用户的组信息从 Azu
我正在使用 blogspot 平台并在我的网站上使用了 Google Adsense。我想对齐一个自动 Adsense 广告,它根本不居中,而带有代码的广告则完全没有问题。它只是自动广告,有人可以帮助
为什么redirect URL必须完全匹配?在域级别进行匹配是否不足以提供适当的安全性? 如果我有数百条路径怎么办? 示例网址: https://myawesomesite.com https://m
我即将创建一个新的 Azure AKS 群集,并且希望将 AKS 与 Azure Key Vault 集成。几个月前,在学习阶段,我看到需要使用Azure AD pod管理的身份来做到这一点,但现在我
我正在尝试配置我的 Azure AD 以同步我的本地 AD DS,如果在 Microsoft Azure AD 中添加任何用户,它应该自动在我的本地 AD 中注册。 我已创建 Azure AD 并配置
我有大约 50 个用户的 Azure AD。这些用户是我们购买Office365时创建的。假设 Azure AD 上的域是 example.com。 ([email protected])在本地,我们
我正在尝试获取组 Azure AD 的名称,Azure 登录 (openId) 后的内部 token 我收到 json 格式的组 ID,但我需要组名称。 登录后的Json: Claims 尝试使用Gr
我们希望将 Azure AD B2C 用于我们的 Web 应用程序,以允许用户使用其公司 ADFS 帐户登录。 根据Azure Active Directory B2C: Add ADFS as a
首先,我无法了解为什么需要这些数据,也无法了解有关网络的细节。您必须相信我,除了运行 LDAP 查询的 PowerShell 脚本之外,没有其他方法可以获取这些数据。 我正在使用具有多个林和多个域的网
我是一个相当新的 PS 用户...正在寻求有关 powershell 脚本的帮助来获取用户所属的安全组列表。 描述我需要什么: 我有包含许多用户(samaccountnames)的输入列表(txt 文
我有两个要存储在目录中以供我的应用程序使用的声明。这些内容不可供用户编辑,但可用于应用程序从 token 中读取。 内置策略可以检索声明,但是,使用自定义策略检索这些声明没有取得任何成功。 通读文章“
我是一名优秀的程序员,十分优秀!