首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >性能分析

性能分析
EN

Stack Overflow用户
提问于 2019-10-13 17:41:38
回答 1查看 162关注 0票数 0

看看一个非常简单的程序,如何找出CPU花在哪里:

代码语言:javascript
运行
复制
use mmap::*;
use crc::{crc32, Hasher32};

use std::cmp::min;
use std::env::args;
use std::fs::File;
use std::fs::metadata;
use std::os::unix::io::AsRawFd;
use std::slice::from_raw_parts;
use std::time::Instant;

fn main() -> std::io::Result<()> {
   let filename = args().nth(1).unwrap();
   let t0 = Instant::now();
   let file = File::open(String::from(&filename[..]))?;
   let fd = file.as_raw_fd();
   let fs = metadata(filename)?;
   let sz = fs.len() as usize;

   let mut offset: usize = 0;
   let mut c32 = crc32::Digest::new(crc32::IEEE);
   while offset < sz {
      let rem = sz - offset;
      let rem = min(rem, 1024 * 1024);

      let map = MemoryMap::new(rem, &[MapOption::MapFd(fd),
                                      MapOption::MapReadable,
                                      MapOption::MapOffset(offset)]).
                map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
      let buf = map.data();
      c32.write(unsafe { from_raw_parts(buf, rem) });
      offset += rem;
   }
   println!("{:08x} in {:.3}", c32.sum32(), t0.elapsed().as_secs());
   Ok(())
}

这是为了内存-映射命令行上提供的文件,并计算它的CRC32。我并不是在寻找其他可能实现这一目标的实现,因为我的目的是与libc函数进行交互。

该程序似乎运行正常,但比我编写的等效Go或Java程序消耗的时间和CPU要多得多,尽管我认为它是用优化编译的:

Cargo.toml:

代码语言:javascript
运行
复制
[profile.dev]
opt-level = 3

[package]
name = "mmap"
version = "0.1.0"
authors = ["Gee"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

mmap = "~0.1.1"
crc = "^1.0.0"

Mac、SSD以不同的顺序执行Go和Rust程序,以消除“冷”文件缓冲区和“温暖”缓冲区的影响--为Go生成约4秒的冷,为Go生成不到一秒钟的热运行,但对于Rust总是30+秒。我也试过cargo build --release了。锈蚀程序的CPU利用率也要高得多(例如25%用于Go,80+%用于锈蚀)。我希望CPU的使用主要来自CRC-32计算,并在一些缓冲区之间复制文件内容。不同之处在于,有些东西会导致铁锈在这里做额外的工作。

Go程序使用相同的方法syscall.Mmap扫描1GB的XML文件,每次映射1MB:

代码语言:javascript
运行
复制
$ ../a ~/demo/dest.xml 
a772d8c4 in 3.978

货物运输:

代码语言:javascript
运行
复制
$ cargo run ~/demo/dest.xml 
    Finished dev [optimized + debuginfo] target(s) in 0.13s
     Running `target/debug/mmap /Users/ololo/demo/dest.xml`
a772d8c4 in 33

在注释中,有一个显示Java程序的请求。下面是一个可以在3.9秒内读取该文件的Go程序:

代码语言:javascript
运行
复制
package main

import(
   "fmt"
   "hash/crc32"
   "os"
   "syscall"
   "time"
)

func main() {
   t0 := time.Now()
   fn := os.Args[1]
   fd, err := os.Open(fn)
   if err != nil {
      fmt.Printf("Couldn't open %s", err)
      return
   }
   defer fd.Close()

   fi, err := fd.Stat()
   if err != nil {
      fmt.Printf("Couldn't stat: %s", err)
      return
   }

   sz := fi.Size()

   cksum := crc32.NewIEEE()
   var off int64

   off = 0
   for sz > off {
      rem := 1024 * 1024
      if sz - off < int64(rem) {
         rem = int(sz - off)
      }
      data, err := syscall.Mmap(int(fd.Fd()), off, rem, syscall.PROT_READ, syscall.MAP_SHARED)
      if err != nil {
         fmt.Printf("Couldn't mmap at %d", off)
         return
      }
      off += int64(rem)
      n, err := cksum.Write(data)
      if err != nil || n != len(data) {
         fmt.Printf("Somehow could not cksum %d", len(data))
         return
      }

      syscall.Munmap(data)
   }

   fmt.Printf("%x in %.3f\n", cksum.Sum32(), time.Now().Sub(t0).Seconds())
}
EN

回答 1

Stack Overflow用户

发布于 2019-10-14 09:15:01

结果发现这里有操作系统的细节。

代码语言:javascript
运行
复制
let map = MemoryMap::new(
    rem,
    &[
        MapOption::MapFd(fd),
        MapOption::MapNonStandardFlags(libc::MAP_SHARED),
        MapOption::MapReadable,
        MapOption::MapOffset(offset),
    ],
)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;

由于某些原因,我使用的机箱没有显式地公开对MAP_SHARED的访问,并且给出了对这个问题的评论,这在Linux上可能没有什么区别。Mac似乎与未指定标志时对MAP_SHARED的处理不同,使得mmap syscall的速度大大减慢。当指定MAP_SHARED时,在Mac上运行“温暖”时,它将返回到大约3秒。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58366166

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档