问题
我有一段java代码(相关的JDK 1.6.0._22),它实现了无状态,无副作用的自由函数,没有互斥体.然而它确实使用了大量内存(我不知道这是否相关).
在过去,我访问过Sun Laboratories并收集了标准的"性能与线程数"曲线.由于此函数没有互斥锁,因此它有一个很好的图形,尽管随着线程数量的增加垃圾收集也开始了.经过一些垃圾收集调整后,我能够使这条曲线几乎平坦.
我现在在英特尔硬件上做同样的实验.硬件有4个CPU,每个CPU有8个内核和超线程.这给出了64个availableProcessors().不幸的是,"性能与线程数"的曲线很好地适用于1,2,3线程和3线程的大写.在3个线程之后,我可以根据需要添加任意数量的线程,并且性能不会更好
尝试解决问题
我的第一个想法是我曾经愚蠢并在某处引入了一些同步代码.通常要解决此问题,我运行JConsole或JVisualVM,并查看线程堆栈跟踪.如果我有64个线程以3的速度运行,我预计其中有61个会等待进入互斥锁.我没有找到这个.相反,我发现所有线程都在运行:非常慢.
第二个想法是,时间框架可能引入了问题.我用一个虚拟函数替换了我的函数,使用AtomicLong计算到十亿.这与线程数量相当精确:使用64个线程比使用1个线程,我能够快速计算到10亿次,高出64倍.
我认为(绝望开始)也许垃圾收集花了很长时间,所以我调整了垃圾收集参数.虽然这改善了我的延迟变化,但它对吞吐量没有影响:我仍然有64个线程以我期望3运行的速度运行.
我已经下载了英特尔工具VTunes,但我的技巧很弱:它是一个复杂的工具,我还不明白.我订购了说明书:给自己一个有趣的圣诞礼物,但是现在有点太晚了,无法解决我当前的问题
题
我们有一个简单的内存吞吐量基准.对于大块内存,它所做的只是重复记忆.
在几台不同的机器上查看结果(针对64位编译),Skylake机器的性能明显优于Broadwell-E,保持OS(Win10-64),处理器速度和RAM速度(DDR4-2133)不变.我们不是说几个百分点,而是大约2个因素.Skylake配置为双通道,Broadwell-E的结果不会因双/三/四通道而异.
任何想法为什么会这样?随后的代码在VS2015的Release中编译,并报告完成每个memcpy的平均时间:
64位:Skylake为2.2ms,Broadwell-E为4.5ms
32位:Skylake为2.2ms,Broadwell-E为3.5ms.
通过利用多个线程,我们可以在四通道Broadwell-E构建上获得更大的内存吞吐量,这很不错,但是看到单线程内存访问的这种巨大差异令人沮丧.为什么差异如此显着的任何想法?
我们还使用了各种基准测试软件,他们验证了这个简单示例所展示的内容 - 单线程内存吞吐量在Skylake上更好.
#include <memory>
#include <Windows.h>
#include <iostream>
//Prevent the memcpy from being optimized out of the for loop
_declspec(noinline) void MemoryCopy(void *destinationMemoryBlock, void *sourceMemoryBlock, size_t size)
{
memcpy(destinationMemoryBlock, sourceMemoryBlock, size);
}
int main()
{
const int SIZE_OF_BLOCKS = 25000000;
const int NUMBER_ITERATIONS = 100;
void* sourceMemoryBlock = malloc(SIZE_OF_BLOCKS);
void* destinationMemoryBlock = malloc(SIZE_OF_BLOCKS);
LARGE_INTEGER Frequency;
QueryPerformanceFrequency(&Frequency);
while (true)
{
LONGLONG total = 0;
LONGLONG max = 0;
LARGE_INTEGER StartingTime, …Run Code Online (Sandbox Code Playgroud) 我在使用时遇到对齐问题 ymm寄存器,一些代码片段对我来说似乎很好.这是一个最小的工作示例:
#include <iostream>
#include <immintrin.h>
inline void ones(float *a)
{
__m256 out_aligned = _mm256_set1_ps(1.0f);
_mm256_store_ps(a,out_aligned);
}
int main()
{
size_t ss = 8;
float *a = new float[ss];
ones(a);
delete [] a;
std::cout << "All Good!" << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当然,sizeof(float)是4在我的架构(英特尔(R)至强(R)CPU E5-2650 V2 @ 2.60GHz),我与编译gcc使用-O3 -march=native标志.当然,错误会随着未对齐的内存访问而消失,即指定_mm256_storeu_ps.我在xmm寄存器上也没有这个问题,即
inline void ones_sse(float *a)
{
__m128 out_aligned = _mm_set1_ps(1.0f);
_mm_store_ps(a,out_aligned);
}
Run Code Online (Sandbox Code Playgroud)
我做了什么愚蠢的事吗?解决这个问题的方法是什么?
考虑以下简单测试:
import numpy as np
from timeit import timeit
a = np.random.randint(0,2,1000000,bool)
Run Code Online (Sandbox Code Playgroud)
让我们找到第一个的索引 True
timeit(lambda:a.argmax(), number=1000)
# 0.000451055821031332
Run Code Online (Sandbox Code Playgroud)
由于numpy短路,这相当快。
它也适用于连续切片
timeit(lambda:a[1:-1].argmax(), number=1000)
# 0.0006490410305559635
Run Code Online (Sandbox Code Playgroud)
但是,似乎不连续的情况并非如此。我主要对查找最后一个感兴趣True:
timeit(lambda:a[::-1].argmax(), number=1000)
# 0.3737605109345168
Run Code Online (Sandbox Code Playgroud)
更新:我的假设是观察到的减速是由于没有短路造成的,这是不准确的(感谢@Victor Ruiz)。实际上,在全
False阵列的最坏情况下
b=np.zeros_like(a)
timeit(lambda:b.argmax(), number=1000)
# 0.04321779008023441
Run Code Online (Sandbox Code Playgroud)
我们仍然比不连续的情况快一个数量级。我已经准备好接受维克多(Victor)的解释,即真正的罪魁祸首是复制品(强迫使用复制品的时机
.copy()是暗示性的)。之后,是否发生短路就不再重要了。
但是其他步长!= 1会产生类似的行为。
timeit(lambda:a[::2].argmax(), number=1000)
# 0.19192566303536296
Run Code Online (Sandbox Code Playgroud)
问题:为什么在最后两个示例中不进行复制就numpy不会短路UPDATE ?
而且,更重要的是:是否有一种解决方法,即某种方法可以强制numpy短更新, 而无需在非连续数组上也进行复制?
当从连续的内存位置执行一系列_mm_stream_load_si128()调用(MOVNTDQA)时,硬件预取器是否仍会启动,或者我应该使用显式软件预取(使用NTA提示)以获得预取的好处,同时仍然避免缓存污染?
我问这个的原因是因为他们的目标似乎与我相矛盾.流加载将获取绕过缓存的数据,而预取器尝试主动将数据提取到缓存中.
当顺序迭代一个大型数据结构(处理过的数据不会在很长一段时间内被修饰)时,我有必要避免污染chache层次结构,但我不想因频繁出现频繁的~100次循环处罚-fetcher闲置.
目标架构是Intel SandyBridge
我开始认真研究算法和数据结构,并有兴趣学习如何比较我可以实现A&DT的不同方式的性能.
对于简单的测试,我可以获得运行之前/之后的时间,运行该事物10 ^ 5次,并平均运行时间.我可以按大小参数化输入,或者对随机输入进行采样,并获得运行时间与输入大小的列表.我可以将其输出为csv文件,并将其输入到pandas中.
我不确定是否有任何警告.我也不确定如何测量空间复杂度.
我正在学习用C++编程.有没有人性化的工具来实现我的目标?
我已经了解了不同的缓存映射技术,如直接映射,关联映射和集合关联映射技术,还学习了权衡.但我很好奇现在在intel core i7或AMD处理器中使用了什么.以及这些技术是如何演变的.还有哪些事情需要改进?
我一直有这个想法,减少迭代次数是的方式来使程序更加高效.由于我从未真正确认过,我开始测试这个.
我制作了以下C++程序来测量两个不同函数的时间:
完整的测试代码:
#include <iostream>
#include <chrono>
using namespace std;
int* list1; int* list2;
int* list3; int* list4;
int* list5; int* list6;
int* list7; int* list8;
int* list9; int* list10;
const int n = 1e7;
// **************************************
void myFunc1()
{
for (int i = 0; i < n; i++)
{
list1[i] = 2;
list2[i] = 4;
list3[i] = 8;
list4[i] = 16;
list5[i] = 32;
list6[i] = 64;
list7[i] = 128;
list8[i] = 256;
list9[i] = …Run Code Online (Sandbox Code Playgroud) 高速缓存行通常为64 字节,也存在其他大小。
我非常简单的问题是:这个数字背后是否有任何理论,或者它只是背后的工程师无疑所做的大量测试和测量的结果?
不管怎样,我想知道这些是什么(理论,如果有的话,以及决定背后的各种测试)。
卷积层是卷积神经网络(CNN)中计算量最大的部分。目前实现卷积层的常见方法是将图像扩展为列矩阵(im2col)并使用现有的并行通用矩阵乘法(GEMM)库。然而im2col操作需要加载和存储图像数据,并且还需要另一个内存块来保存中间数据。
如果我需要优化卷积实现,我可能会选择用SIMD指令直接实现。这种方法不会产生任何内存操作开销。
非常规律的内存访问模式所带来的好处超过了浪费的存储成本。
来自以下链接,在链接末尾
https://petewarden.com/2015/04/20/why-gemm-is-at-the-heart-of-deep-learning/
所以我希望知道原因。浮点运算可能需要更多指令周期吗?或者输入图像不太大,因此可能会残留在缓存中,并且内存操作不需要访问DDR并且消耗更少的周期。
memory floating-point instruction-set cpu-architecture conv-neural-network