Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Caching 理解并行HashMap实现的缓存线失效和条带锁_Caching_Locking - Fatal编程技术网

Caching 理解并行HashMap实现的缓存线失效和条带锁

Caching 理解并行HashMap实现的缓存线失效和条带锁,caching,locking,Caching,Locking,如果我们在内存中为并发hashmap将条带锁彼此放得非常近,那么缓存线的大小可能会影响性能,因为我们将不得不不必要地使缓存无效。如果在条带锁数组中添加填充,将提高性能 有人能解释一下吗?从非并发hashmap开始,基本原则如下: 为键和值创建索引结构(通常是一个数组或一组数组) 获取密钥的哈希值 将其减小到阵列的大小之内。(模运算很简单,因此如果散列值是123439281,并且有31个可用插槽,那么我们使用123439281%31,它是9,并将其用作索引) 看看那里是否有一个键,如果有,它是否匹

如果我们在内存中为并发hashmap将条带锁彼此放得非常近,那么缓存线的大小可能会影响性能,因为我们将不得不不必要地使缓存无效。如果在条带锁数组中添加填充,将提高性能


有人能解释一下吗?

从非并发hashmap开始,基本原则如下:

  • 为键和值创建索引结构(通常是一个数组或一组数组)
  • 获取密钥的哈希值
  • 将其减小到阵列的大小之内。(模运算很简单,因此如果散列值是123439281,并且有31个可用插槽,那么我们使用
    123439281%31
    ,它是
    9
    ,并将其用作索引)
  • 看看那里是否有一个键,如果有,它是否匹配(等于)
  • 如果密钥是新的,则存储该密钥和值
  • 同样的方法也可用于查找给定键的值(或查找没有值)

    当然,如果同一个插槽中有一个密钥与您所关心的密钥不相等,则上述方法不起作用,并且有不同的处理方法,主要是继续在不同的插槽中查找,直到一个插槽空闲为止,或者让插槽实际充当相等索引对的链接列表。我不想详细讨论这件事

    如果您正在寻找其他插槽,则在填充数组后它将无法工作(并且在该点之前会很慢),如果您使用链表来处理冲突,那么如果在同一索引中有多个键,则速度将非常慢(随着情况变得更糟,您想要的O(1)将越来越接近O(n))。无论哪种方式,当存储量过大时,您都需要一种调整内部存储大小的机制

    好的。这是对hashmap的一个非常高级的描述。如果我们想让它线程安全呢

    默认情况下,它不会是线程安全的,例如,两个不同的线程写入不同的密钥,其哈希模降低到相同的值,那么一个线程可能会践踏另一个线程

    使hashmap线程安全的最简单方法是,只需在所有操作上使用一个锁。这意味着每个线程都会在其他线程上等待,所以它不会有很好的并发行为。有了一点巧妙的结构,我们就可以拥有多个阅读线程或一个写作线程(但不是两者都有),但这仍然不是很好

    可以创建完全不使用锁的安全并发哈希映射(请参阅我在C#中编写的一个哈希映射的描述,或Cliff Click在Java中使用的一个哈希映射,我在C#版本中使用了它的基本方法)

    但另一种方法是条带锁

    因为映射的基础是一个键值对数组(很可能是哈希代码的缓存副本)或一组数组,并且因为让两个线程一次写入和/或读取数组的不同部分通常是安全的(有一些警告,但我现在将忽略它们)唯一的问题是当两个线程需要相同的插槽时,或者当需要调整大小时

    因此,不同的插槽可能有不同的锁,只有在同一插槽上运行的线程才需要彼此等待

    调整尺寸的问题仍然存在,但这并不是不可克服的;如果需要调整大小,请获取每一个锁(以设置的顺序,以便防止死锁发生),然后执行调整大小,首先检查是否没有其他线程同时执行相同的调整大小

    但是,如果您有一个具有10000个插槽的hashmap,这将意味着10000个锁对象。这占用了大量内存,而且调整大小意味着获得10000个锁中的每一个

    条带锁介于单锁方法和按插槽锁方法之间。拥有一个包含一定数量锁的数组,比如说16是一个漂亮的(二进制)整数。当您需要对插槽执行操作时,请获取锁号
    插槽索引%16
    ,然后执行操作。现在,虽然线程可能仍然会阻塞在完全不同的插槽上执行操作的线程(插槽5和插槽21具有相同的锁),但它们仍然可以同时执行许多其他操作,因此这是两个极端之间的中间地带

    这就是条带锁在高级别上的工作原理

    现在,现代的内存访问是不统一的,因为它不需要相同的时间来访问任意的内存块,因为CPU中有一个级别的缓存(通常至少两个级别)。这种缓存既有好的影响,也有坏的影响

    显然,好的效果通常大于坏的,否则芯片制造商不会使用它。如果您访问一块内存,然后访问一块非常靠近它的内存,第二次访问可能会非常快,因为它在第一次读取时已加载到缓存中。写入也得到了改进

    一段给定的代码可能希望对彼此靠近的内存块执行多个操作(例如,读取同一对象中的两个字段,或一个方法中的两个局部变量),这已经足够自然了,这就是为什么这种缓存首先起作用的原因。程序员进一步努力在设计代码时尽可能利用这一事实,hashmaps等集合就是一个典型的例子。例如,我们可能将密钥和散列存储在同一个数组中,以便读取其中一个会将另一个带到缓存中以便快速读取,等等

    但有时这种缓存会产生负面影响。特别是当两个线程要处理几乎同时彼此接近的内存位时

    这不会经常出现,因为线程通常处理自己的堆栈或指向b的堆内存位