http://www.7klian.com

Filecoin SDR机能优化道理阐明

          let cur_consumer = &cur_consumer;

上图给一个示例。整个芯片由两个die构成(上面是die0,下面是die1)。每个Die(CCD)包罗两个CCX。留意的是,每个core都有本身的L1/L2缓存,可是L3缓存是几个Core共享的。也就是说,在CCX的四个核可以共享L3缓存,假如四个核需要会见同一段数据,可以加快数据的会见时间。
              )
      prepare_block(replica_id, cur_layer, &mut buf);
          let ready_count = producer_val – i + 1;
                          }

      }
                  // Six rounds of all base parents
明晰了优化思路,SDR算法优化的代码照旧较量容易领略的。焦点逻辑在storage-proofs/porep/src/stacked/vanilla/create_label/mult.rs代码的create_layer_labels函数中。
  let mut base_parent_missing = vec![BitMask::default(); lookahead];
需要表明一下base_parent_missing。因为base parent的节点需要当前层前面节点的计较功效,在预读取的时候,大概这些节点还没有计较出来。这些节点的信息通过base_parent_missing标示出来。留意,ring_buf中的每个节点的数据除了依赖的节点信息外,尚有一些帮助信息(replica_id, node_id等等)。这些帮助信息的长度是64,所以BYTES_PER_NODE还会加上64。prepare_block函数就是预先填充帮助信息。
  // Next node to be filled
                  buf[352] = 0x80; // Padding
                  buf[96] = 0x80; // Padding
  num_nodes: u64,
                  for _j in 0..6 {
                          .copy_from_slice(source.as_byte_slice());
              let buf = unsafe { ring_buf.slot_mut(cur_slot) };
      let mut buf = [0u8; (NODE_SIZE * DEGREE) + 64];

优化的思路,就是将exp/base的parent节点信息的获取和sha256的计较并行。在举办节点计较之前,预先读取exp/base的节点信息并生存在sdr_parent_cache环缓冲中。留意,sdr_parent_cache和parent_cache纷歧样。sdr_parent_cache是某个节点依赖的多个节点的信息,parent_cache是节点依赖信息的缓存。Filecoin官方称这个优化是Multi-core优化,有几个原因:1/ 节点依赖数据的预读取和节点的计较由多核实现。2/ 预读取的逻辑自己也是由多核实现。
                      *GenericArray::<u8, U64>::from_slice(&buf[320..384]),
  } else {
  cur_layer: u32,
                  let blocks = [
                  layer_labels,

                  if bpm.get(k) {
commit 0313c663c1b4c7ea891dcaf7245e2cc5eb4b1f81
              for k in 0..BASE_DEGREE {
                  }
                      *GenericArray::<u8, U64>::from_slice(&buf[448..512]),
Date:   Thu Aug 27 23:08:12 2020 +0200
  const BYTES_PER_NODE: usize = (NODE_SIZE * DEGREE) + 64;
                      };
          runners.push(s.spawn(move |_| {
                  base_parent_missing,
                      *GenericArray::<u8, U64>::from_slice(&buf[128..192]),
详细的逻辑较量清晰易懂,感乐趣的小同伴直接看源代码。
                      *GenericArray::<u8, U64>::from_slice(&buf[384..448]),
                  memset(&mut buf[96..128], 0); // Zero out upper half of last block
                          if cur_parent_ptr.is_empty() {
                                  parents_cache.consumer_slice_at(cur_parent_ptr_offset);
                  cur_awaiting,
cur_consumer和cur_producer就是用于出产者和消费者之间的同步。cur_awaiting用于多个出产者之间的同步。
出产者和消费者之间需要同步。多个出产者之间也需要同步。
                  let bpm = unsafe { base_parent_missing.get(cur_slot) };
                  cur_parent_ptr_offset += 1;
) -> Result<()> {
3. SDR算法优化逻辑阐明
          let cur_producer = &cur_producer;
  // Highest node that is ready from the producer
  for buf in ring_buf.iter_slot_mut() {
                  ];
在Layer为1时,回收1个出产者,每个出产者一次出产16个节点的依赖数据。其他Layer时,回收2个出产者,每个出产者一次出产128个节点的依赖数据。Layer为1和其他时候区分隔,是因为Layer1的依赖干系和其他层纷歧样。
                          let end = start + NODE_WORDS;
1. Core Complex

parents_cache是节点依赖干系的cache数据,replica_id是replia的编号信息,layer_labels和exp_labels别离是当前层和上一层的数据,num_nodes节点个数,cur_layer是当前层的编号。
                  lookahead as u64,
                  num_nodes,
                  cur_parent_ptr = &cur_parent_ptr[1..];
                      let source = unsafe {
出产者预生成的节点依赖数据是通过Ring缓存(ring_buf)存储。这个存储在出产者和消费者之间共享。
  let (lookahead, num_producers, producer_stride) = if cur_layer == 1 {
                  // round 7 is only first parent
              // Grab the current slot of the ring_buf
                  compress256!(cur_node_ptr, &buf[64..], 1);
消费者的逻辑相对多一些。每一层的第一个节点因为没有base parent节点,计较较量非凡,单独处理惩罚:
                      *GenericArray::<u8, U64>::from_slice(&buf[256..320]),
      (400, 1, 16)
                  // Final round is only nine parents
  let mut ring_buf = RingBuf::new(BYTES_PER_NODE, lookahead);
                  memset(&mut buf[352..384], 0); // Zero out upper half of last block
          let ring_buf = &ring_buf;
  let cur_consumer = AtomicU64::new(0);
              } else {
      compress256!(cur_node_ptr, buf, 2);
  }
                      buf[64 + (NODE_SIZE * k)..64 + (NODE_SIZE * (k + 1))]
3.4 启动出产者
  // Fill in the fixed portion of all buffers
这个Patch在8月27号就做出来了。优化思路较量清晰:通过预读取base/exp parent的数据,让数据的筹备和sha256的计较并行。在先容详细的优化逻辑之前,先容一下AMD CPU架构的小常识:
优化之前计较一层的某个节点是这样的:

Filecoin官方本日正式公布了SDR的优化版本。在AMD3970x上,P1的机能2小时10分钟。看了看官方的优化思路,分享一下。P1机能优化的焦点Patch如下:
3.2 申请Ring缓存
查察出产者提供了几多节点数据,并开始举办处理惩罚:
                  compress256!(cur_node_ptr, &buf[64..], 5);
                          &layer_labels.as_slice()[start..end]
          for _count in 0..ready_count {
          let cur_awaiting = &cur_awaiting;
      cur_node_ptr[7] &= 0x3FFF_FFFF; // Strip last two bits to ensure in Fr
              if cur_layer == 1 {
                  sha2::compress256((&mut cur_node_ptr[..8]).try_into().unwrap(), &blocks);
                      // info!(“getting missing parent, k={}”, k);
          let base_parent_missing = &base_parent_missing;

                  parents_cache,
3.1 确定出产者设置
                  // Two rounds of all parents
优化后的计较方法如下:

在计较某个节点时,姑且获取exp/base的parent节点信息,再举办sha256的计较。
              }
      cur_node_ptr[..8].iter_mut().for_each(|x| *x = x.to_be());
处理惩罚进程分为两部门。第一部门,base parent有大概有缺失,先增补缺失的数据:
                          // info!(“after unsafe, when getting miss parent”);
2. SDR算法优化思路
逻辑的实现回收“出产者/消费者“模式,出产者”生成“节点依赖数据,消费者获取这些数据并举办sha256的计较。
Filecoin – 为什么SDR这么慢?

CCX是AMD CPU架构的一个术语。CCX – CPU Core Complex。CCX指的是一组(4个)CPU焦点缓和存(L1/L2/L3)。4个CCX一组,也有一个专门的术语 – CCD (Core Chiplet Die)。AMD的Ryzen 3000系列的处理惩罚器都回收雷同的架构。

                      *GenericArray::<u8, U64>::from_slice(&buf[64..128]),
          let exp_labels = exp_labels.as_ref();
设定了几多出产者,就启动个几多线程。每个线程挪用create_label_runner函数,实现了详细的预读取逻辑。
              create_label_runner(
                  }
                          // info!(“before as_slice(), when getting miss parent”);
      cur_node_ptr[..8].copy_from_slice(&SHA256_INITIAL_DIGEST);
SDR算法在之前的文章中具体先容:
  };
                          let start = cur_parent_ptr[0] as usize * NODE_WORDS;
  layer_labels: &mut MmapMut,
                      // info!(“got missing parent, k={}”, k);
                  cur_consumer,
计较分为两种环境,一种是第一层的数据,一种是其他层的数据。计较sha256的进程稍稍有些差异。至此,整个优化算法逻辑就完整了。
          }));
  let cur_awaiting = AtomicU64::new(1);
                  buf[382] = 0x27; // Length (0x2700 = 9984 bits -> 1248 bytes)
  parents_cache: &CacheReader,
                  exp_labels,
fn create_layer_labels(
      for _i in 0..num_producers {
      (800, 2, 128)
  exp_labels: Option<&mut MmapMut>,
  let cur_producer = AtomicU64::new(0);
                              cur_parent_ptr =
      // Fix endianess
                  sha2::compress256((&mut cur_node_ptr[..8]).try_into().unwrap(), &blocks);
  Optimize Phase1.
出产者,通过多线程预读取某个节点依赖的节点数据。lookahead指预读取最大的节点个数。num_producers是利用几多个出产者。producer_stride是每个出产者一次出产几多个节点的依赖数据。
                  producer_stride,
Filecoin官方公布了SDR的优化版本。在AMD3970x上,P1的机能2小时10分钟。优化思路较量清晰,通过预读取base/exp parent的数据,让数据的筹备和sha256的计较并行。

          let layer_labels = &layer_labels;
3.5 启动消费者
                  ring_buf,
              }
                      compress256!(cur_node_ptr, &buf[64..], 3);
              cur_node_ptr = &mut cur_node_ptr[8..];
总结:
                  buf[126] = 0x27; // Length (0x2700 = 9984 bits -> 1248 bytes)
  // Node the consumer is currently working on
3.3 申请原子锁操纵
Author: dignifiedquire <[email protected]>
      prepare_block(replica_id, cur_layer, buf);
  replica_id: &[u8],
第二部门,在base parent数据增补完整后,所有的数据都已经筹备完成,,举办sha256的计较:
                  cur_producer,
                      *GenericArray::<u8, U64>::from_slice(&buf[192..256]),

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。

相关文章阅读