我正尝试编写处理器密集型任务的代码,因此我想使用多线程并在可用处理器内核之间共享计算。
假设我有数千次迭代,所有迭代都有两个阶段:
阶段是按顺序进行的,因此没有重叠(不会同时写入和读取数据)。我的问题是:在下一个阶段(阶段1)开始之前,如何确定工作线程的数据(缓存)已更新。
我假设当人们在这种情况下谈论缓存或缓存时,它们的意思是处理器缓存(如果我错了,请修复我)。
据我了解,volatile只能用于非引用类型,而没有使用同步的意义,因为工作线程在读取时会相互阻塞(在处理一个选项时可能有数千次读取)。
在这种情况下我还能使用什么?
现在我有一些想法,但是我不知道它们有多昂贵(很可能是):
为所有迭代创建新的工作线程
在一个新的迭代中,在每个新线程开始之前,为每个线程制作一个数组副本(大小最大为195kB)
我很喜欢ReentrantReadWriteLock,但我不明白它与缓存有何关系。读取锁是否可以强制读取器的缓存进行更新?
答案 0 :(得分:0)
我正在寻找的东西在“ Java并发教程”中提到,我只需要更深入地了解。在这种情况下,它是AtomicIntegerArray类。不幸的是,它的效率不足以满足我的需求。我进行了一些测试,也许值得分享。
我估算了不同内存访问方法的成本,方法是多次运行它们并平均经过的时间,将所有内容分解为一次平均读取或写入。
我使用大小为50000的整数数组,并在每种测试方法中重复了100次,然后取平均结果。读取测试正在执行50000次随机(ish)读取。结果显示一次读/写访问的大概时间。尽管如此,这不能说是精确的度量,但我相信它可以很好地了解不同访问方法的时间成本。但是,对于不同的缓存大小和时钟速度,在不同的处理器上或使用不同的数字时,这些结果可能完全不同。
结果是:
第1点和第2点显示了在AtomicIntegerArray上进行顺序写入的结果。在某些文章中,我谈到了lazySet()方法的良好效率,因此我想对其进行测试。通常会将set()方法执行大约4次,但是不同的数组大小会显示不同的结果。
第3点和第4点显示了通过四个不同线程同时进行的随机读取对数组的一项进行“原子”访问和同步访问(同步吸气剂)之间的区别。这清楚地表明了“原子”访问的好处。
由于前四个值令人震惊,我真的很想在没有多线程的情况下测量访问时间,所以我得到了第5点和第6点的建议。我试图复制和修改先前测试中的方法,以编写代码尽可能接近。当然,可以有我不能影响的优化。
然后出于好奇,我提出了第7点和第8点,它们模仿了不变的访问方式。这里一个线程(通过顺序写入)创建数组,并将其引用传递给另一个线程,该线程对该数组进行随机(ish)读取访问。
如果更改了参数(例如数组的大小或正在运行的方法的数量),结果将有很大的不同。
结论: 如果算法占用大量内存(从同一个小数组中读取大量数据,但由于简短的计算而中断-这是我的情况),那么多线程处理可能会减慢计算速度,而不是加快计算速度。但是,如果读取次数很多,与数组的大小相比,使用数组的不变副本并使用多个线程可能会有所帮助。