为什么 foreach %dopar% 每增加一个节点就会变慢?

2024-01-29

我编写了一个简单的矩阵乘法来测试网络的多线程/并行化功能,我注意到计算速度比预期慢得多。

The Test很简单:乘以 2 个矩阵 (4096x4096) 并返回计算时间。矩阵和结果都不被存储。计算时间并非微不足道(50-90 秒,具体取决于您的处理器)。

条件:我使用 1 个处理器重复此计算 10 次,将这 10 个计算拆分为 2 个处理器(每个处理器 5 个),然后是 3 个处理器,...最多 10 个处理器(每个处理器 1 次计算)。我预计总计算时间会逐步减少,并且预计有 10 个处理器来完成计算10 times与一个处理器完成相同任务所需的速度一样快。

结果:相反,我得到的只是计算时间减少了 2 倍,即 5 倍SLOWER比预期的。

当我计算每个节点的平均计算时间时,我希望每个处理器以相同的时间(平均)计算测试,无论分配的处理器数量如何。我惊讶地发现,仅仅将相同的操作发送到多个处理器就会减慢每个处理器的平均计算时间。

谁能解释为什么会发生这种情况?

请注意,这是一个问题NOT这些问题的重复:

foreach %dopar% 比 for 循环慢 https://stackoverflow.com/questions/16963808/foreach-dopar-slower-than-for-loop/16964023#16964023

or

为什么并行包比仅使用 apply 慢? https://stackoverflow.com/questions/14614306/why-is-the-parallel-package-slower-than-just-using-apply

因为测试计算并不简单(即 50-90 秒而不是 1-2 秒),并且因为我可以看到处理器之间没有通信(即除了计算时间之外没有返回或存储结果)。

我已附加以下脚本和函数以进行复制。

library(foreach); library(doParallel);library(data.table)
# functions adapted from
# http://www.bios.unc.edu/research/genomic_software/Matrix_eQTL/BLAS_Testing.html

Matrix.Multiplier <- function(Dimensions=2^12){
  # Creates a matrix of dim=Dimensions and runs multiplication
  #Dimensions=2^12
  m1 <- Dimensions; m2 <- Dimensions; n <- Dimensions;
  z1 <- runif(m1*n); dim(z1) = c(m1,n)
  z2 <- runif(m2*n); dim(z2) = c(m2,n)
  a <- proc.time()[3]
  z3 <- z1 %*% t(z2)
  b <- proc.time()[3]
  c <- b-a
  names(c) <- NULL
  rm(z1,z2,z3,m1,m2,n,a,b);gc()
  return(c)
}

Nodes <- 10
Results <- NULL
for(i in 1:Nodes){
  cl <- makeCluster(i)
  registerDoParallel(cl)
  ptm <- proc.time()[3]
  i.Node.times <- foreach(z=1:Nodes,.combine="c",.multicombine=TRUE, 
                          .inorder=FALSE) %dopar% {
                            t <- Matrix.Multiplier(Dimensions=2^12)
                          }
  etm <- proc.time()[3]
  i.TotalTime <- etm-ptm
  i.Times <- cbind(Operations=Nodes,Node.No=i,Avr.Node.Time=mean(i.Node.times),
                   sd.Node.Time=sd(i.Node.times),
                   Total.Time=i.TotalTime)
  Results <- rbind(Results,i.Times)
  rm(ptm,etm,i.Node.times,i.TotalTime,i.Times)
  stopCluster(cl)
}
library(data.table)
Results <- data.table(Results)
Results[,lower:=Avr.Node.Time-1.96*sd.Node.Time]
Results[,upper:=Avr.Node.Time+1.96*sd.Node.Time]
Exp.Total <- c(Results[Node.No==1][,Avr.Node.Time]*10,
               Results[Node.No==1][,Avr.Node.Time]*5,
               Results[Node.No==1][,Avr.Node.Time]*4,
               Results[Node.No==1][,Avr.Node.Time]*3,
               Results[Node.No==1][,Avr.Node.Time]*2,
               Results[Node.No==1][,Avr.Node.Time]*2,
               Results[Node.No==1][,Avr.Node.Time]*2,
               Results[Node.No==1][,Avr.Node.Time]*2,
               Results[Node.No==1][,Avr.Node.Time]*2,
               Results[Node.No==1][,Avr.Node.Time]*1)
Results[,Exp.Total.Time:=Exp.Total]

jpeg("Multithread_Test_TotalTime_Results.jpeg")
par(oma=c(0,0,0,0)) # set outer margin to zero
par(mar=c(3.5,3.5,2.5,1.5)) # number of lines per margin (bottom,left,top,right)
plot(x=Results[,Node.No],y=Results[,Total.Time],  type="o", xlab="", ylab="",ylim=c(80,900),
     col="blue",xaxt="n", yaxt="n", bty="l")
title(main="Time to Complete 10 Multiplications", line=0,cex.lab=3)
title(xlab="Nodes",line=2,cex.lab=1.2,
      ylab="Total Computation Time (secs)")
axis(2, at=seq(80, 900, by=100), tick=TRUE, labels=FALSE)
axis(2, at=seq(80, 900, by=100), tick=FALSE, labels=TRUE, line=-0.5)
axis(1, at=Results[,Node.No], tick=TRUE, labels=FALSE)
axis(1, at=Results[,Node.No], tick=FALSE, labels=TRUE, line=-0.5)
lines(x=Results[,Node.No],y=Results[,Exp.Total.Time], type="o",col="red")
legend('topright','groups',
       legend=c("Measured", "Expected"), bty="n",lty=c(1,1),
       col=c("blue","red"))
dev.off()

jpeg("Multithread_Test_PerNode_Results.jpeg")
par(oma=c(0,0,0,0)) # set outer margin to zero
par(mar=c(3.5,3.5,2.5,1.5)) # number of lines per margin (bottom,left,top,right)
plot(x=Results[,Node.No],y=Results[,Avr.Node.Time],  type="o", xlab="", ylab="",
     ylim=c(50,500),col="blue",xaxt="n", yaxt="n", bty="l")
title(main="Per Node Multiplication Time", line=0,cex.lab=3)
title(xlab="Nodes",line=2,cex.lab=1.2,
      ylab="Computation Time (secs) per Node")
axis(2, at=seq(50,500, by=50), tick=TRUE, labels=FALSE)
axis(2, at=seq(50,500, by=50), tick=FALSE, labels=TRUE, line=-0.5)
axis(1, at=Results[,Node.No], tick=TRUE, labels=FALSE)
axis(1, at=Results[,Node.No], tick=FALSE, labels=TRUE, line=-0.5)
abline(h=Results[Node.No==1][,Avr.Node.Time], col="red")
epsilon = 0.2
segments(Results[,Node.No],Results[,lower],Results[,Node.No],Results[,upper])
segments(Results[,Node.No]-epsilon,Results[,upper],
         Results[,Node.No]+epsilon,Results[,upper])
segments(Results[,Node.No]-epsilon, Results[,lower],
         Results[,Node.No]+epsilon,Results[,lower])
legend('topleft','groups',
       legend=c("Measured", "Expected"), bty="n",lty=c(1,1),
       col=c("blue","red"))
dev.off()

编辑:回复@Hong Ooi 的评论

I used lscpu在 UNIX 中获取;

Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                30
On-line CPU(s) list:   0-29
Thread(s) per core:    1
Core(s) per socket:    1
Socket(s):             30
NUMA node(s):          4
Vendor ID:             GenuineIntel
CPU family:            6
Model:                 63
Model name:            Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz
Stepping:              2
CPU MHz:               2394.455
BogoMIPS:              4788.91
Hypervisor vendor:     VMware
Virtualization type:   full
L1d cache:             32K
L1i cache:             32K
L2 cache:              256K
L3 cache:              20480K
NUMA node0 CPU(s):     0-7
NUMA node1 CPU(s):     8-15
NUMA node2 CPU(s):     16-23
NUMA node3 CPU(s):     24-29

编辑:回复@Steve Weston 的评论。

我正在使用一个虚拟机网络(但我不是管理员)最多可以访问 30 个集群。我运行了你建议的测试。打开 5 个 R 会话并同时在 1,2...5 上运行矩阵乘法(或者尽可能快地切换并执行)。得到与之前非常相似的结果(回复:每个额外的进程都会减慢所有单独的会话)。注意我使用检查内存使用情况top and htop并且使用量从未超过网络容量的 5% (~2.5/64Gb)。

结论:

这个问题似乎是 R 特有的。当我使用其他软件运行其他多线程命令时(例如PLINK https://www.cog-genomics.org/plink2)我没有遇到这个问题,并且并行进程按预期运行。我也尝试过运行上面的内容Rmpi and doMPI具有相同(较慢)的结果。问题似乎是相关的R虚拟机网络上的会话/并行命令。我真正需要帮助的是如何查明问题。似乎指出了类似的问题here https://stackoverflow.com/questions/41936951/r-foreach-dompi-reduced-speed-with-increased-iterations#comment71464282_41936951


我发现每个节点的乘法时间非常有趣,因为计时不包括与并行循环相关的任何开销,而仅包括执行矩阵乘法的时间,并且它们表明时间随着矩阵乘法的数量而增加在同一台机器上并行执行。

我可以想到可能发生这种情况的两个原因:

  1. 在核心耗尽之前,机器的内存带宽会因矩阵乘法而饱和;
  2. 矩阵乘法是多线程的。

您可以通过启动多个 R 会话(我在多个终端中执行此操作)来测试第一种情况,在每个会话中创建两个矩阵:

> x <- matrix(rnorm(4096*4096), 4096)
> y <- matrix(rnorm(4096*4096), 4096)

然后在每个会话中大约同时执行矩阵乘法:

> system.time(z <- x %*% t(y))

理想情况下,无论您使用多少个 R 会话(最多核心数量),这个时间都是相同的,但由于矩阵乘法是一个相当内存密集型的操作,因此许多机器在耗尽内存带宽之前会耗尽内存带宽。核心,导致时间增加。

如果您的 R 安装是使用多线程数学库(例如 MKL 或 ATLAS)构建的,那么您可以使用所有内核进行单个矩阵乘法,因此您不能期望通过使用多个进程获得更好的性能,除非您使用多台计算机。

您可以使用“top”等工具来查看您是否正在使用多线程数学库。

最后,输出lscpu表明您正在使用虚拟机。我从未在多核虚拟机上进行过任何性能测试,但这也可能是问题的根源。


Update

我相信并行矩阵乘法比单个矩阵乘法运行得更慢的原因是你的 CPU 读取内存的速度不够快,无法全速为两个以上的内核提供数据,我将其称为内存带宽饱和。如果你的CPU有足够大的缓存,你也许能够避免这个问题,但它实际上与你主板上的内存量没有任何关系。

我认为这只是使用单台计算机进行并行计算的限制。使用集群的优点之一是内存带宽以及总聚合内存都会增加。因此,如果您在多节点并行程序的每个节点上运行一两次矩阵乘法,则不会遇到这个特定问题。

假设您无法访问集群,您可以尝试在计算机上对多线程数学库(例如 MKL 或 ATLAS)进行基准测试。运行一个多线程矩阵乘法很可能比在多个进程中并行运行它们获得更好的性能。但同时使用多线程数学库和并行编程包时要小心。

您也可以尝试使用 GPU。他们显然擅长执行矩阵乘法。


Update 2

要查看问题是否是 R 特定的,我建议您对dgemm函数,这是R用来实现矩阵乘法的BLAS函数。

这是一个简单的 Fortran 程序来进行基准测试dgemm。我建议按照我为基准测试描述的相同方式从多个终端执行它%*% in R:

      program main
      implicit none
      integer n, i, j
      integer*8 stime, etime
      parameter (n=4096)
      double precision a(n,n), b(n,n), c(n,n)
      do i = 1, n
        do j = 1, n
          a(i,j) = (i-1) * n + j
          b(i,j) = -((i-1) * n + j)
          c(i,j) = 0.0d0
        end do
      end do
      stime = time8()
      call dgemm('N','N',n,n,n,1.0d0,a,n,b,n,0.0d0,c,n)
      etime = time8()
      print *, etime - stime
      end

在我的 Linux 机器上,一个实例运行需要 82 秒,而四个实例运行需要 116 秒。这与我在 R 中看到的结果一致,并且与我的猜测一致,这是一个内存带宽问题。

您还可以将其链接到不同的 BLAS 库,以查看哪种实现在您的计算机上效果更好。

您还可以使用以下命令获取有关虚拟机网络内存带宽的一些有用信息pmbw - 并行内存带宽基准 https://panthema.net/2013/pmbw/,虽然我从来没有用过它。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 foreach %dopar% 每增加一个节点就会变慢? 的相关文章

  • 自定义轴缩放后 ggplot2 缺少标签

    我正在尝试使用我的 x 轴应用自定义缩放ggplot2 and scales trans new 但是 当我这样做时 一些轴标签丢失了 有人可以帮我弄清楚为什么吗 Setup library tidyverse the data ds lt
  • 主线程如何在该线程之前运行?

    我有以下代码 public class Derived implements Runnable private int num public synchronized void setA int num try Thread sleep 1
  • 如何将参数从 Excel/VBA 传递到 Rstudio 中的脚本

    我正在尝试使用 Rstudio 从 VBA 打开 R 脚本 同时将参数传递给 R 脚本 然后我可以使用 commandArgs 访问该脚本 该问题与此处描述的问题非常相似 WScript Shell 用于运行路径中包含空格且来自 VBA 的
  • R 数据结构的运算效率

    我想知道是否有任何关于操作效率的文档R 特别是那些与数据操作相关的 例如 我认为向数据框添加列是有效的 因为我猜您只是向链接列表添加一个元素 我想添加行会更慢 因为向量保存在数组中C level你必须分配一个新的长度数组n 1并将所有元素复
  • 将整个包传递给雪簇

    我正在尝试并行化 使用snow parLapply 一些依赖于包 即除snow 调用函数中引用的对象parLapply必须使用显式传递给集群clusterExport 有没有办法将整个包传递到集群 而不必显式命名每个函数 包括用户函数调用的
  • 是否有weighted.median()函数?

    我正在寻找类似形式的东西weighted mean 我通过搜索找到了一些解决方案 这些解决方案写出了整个函数 但希望有一些更用户友好的解决方案 以下软件包都有计算加权中位数的函数 aroma light isotone limma cwhm
  • 有没有一种简单的方法来准备 Fortran 代码以进行并行调用

    我想使用 OpenMP 在 C 程序中并行求解多个大型 ODE 系统 由于某些原因 我需要使用 ODE 求解器 但我只能找到 Fortran 90 子例程 而且代码太大 无法简单地将其转换为 C 我知道 Fortran 广泛使用静态内存 因
  • R 中的数据框操作 - 将单元格向左移动并删除 NA

    我有一个数据框 其列由随机分布的值和 NA 组成 如下所示 a lt c S E NA S NA b lt c A NA M G K c lt c I NA NA NA L meh lt dataframe a b c 1 2 3 4 5
  • 斯皮尔曼相关性和联系

    我正在一小组配对排名上计算斯皮尔曼的 rho 斯皮尔曼因处理领带不当而闻名 例如 取2组8个排名 即使两组中有6个是平局 相关性仍然很高 gt cor test c 1 2 3 4 5 6 7 8 c 0 0 0 0 0 0 7 8 met
  • R 颜色 - 许多独特的颜色仍然很漂亮

    我很好奇你是否有一些关于 R 中颜色酿造的技巧 对于许多独特的颜色 在某种程度上图形仍然好看 我需要相当数量的独特颜色 至少 24 种 可能需要更多 50 种 用于堆叠区域图 所以不是热图 渐变颜色不起作用 我发现了 viridis 它的调
  • 识别包含字符串的行的最快方法[重复]

    这个问题在这里已经有答案了 我有一个字符串数据框 尺寸为 30 列 x 500 万行 我想识别包含任何预定义字符串列表的行 有没有比下面我的 apply any 方法更快的方法 这是一个可重现的示例 请注意 此示例中的字符串是随机数 但在我
  • R:根据列名部分匹配计算行平均值

    我有一个看起来像这样的表 er er 1 as as 1 as 2 rt op a 1 6 90 8 6 4 87 b 1 8 56 7 5 5 9 c 8 7 6 4 5 9 6 d 1 0 8 6 4 3 6 e 9 7 2 4 3 8
  • ggplot 的每个方面都有不同的 `geom_hline()`

    这个问题在这里已经有答案了 library tidyverse ggplot mpg aes cty hwy geom point facet grid year fl geom hline yintercept mean mpg hwy
  • 有没有办法等待原子整数的变化

    有没有办法等待AtomicInteger这样我就不必继续休眠当前线程并继续检查AtomicInteger像这样 while atomicInt get gt 0 Thread sleep 1000 我知道有这样的事情CountDownLat
  • Volatile.Read 和 Volatile.Write 背后的逻辑是什么?

    来自 MSDN Volatile Read 读取字段的值 在需要它的系统上 插入一个 阻止处理器重新排序内存的内存屏障 操作如下 如果在该方法之后出现读或写 代码 处理器无法移动它before这个方法 and Volatile Write
  • 锁定 ASP.NET 应用程序变量

    我在 ASP NET 应用程序中使用第三方 Web 服务 对第 3 方 Web 服务的调用必须同步 但 ASP NET 显然是多线程的 并且可能会发出多个页面请求 从而导致对第 3 方 Web 服务的同时调用 对 Web 服务的调用封装在自
  • 并行化斐波那契序列生成器

    我正在学习并行化 在一项练习中 我得到了一些我应该提高性能的算法 其中之一是斐波那契数列生成器 array 0 0 array 1 1 for q 2 q lt MAX q array q array q 1 array q 2 我怀疑 这
  • ggplot geom_bar - 条形太宽

    对于这个非信息性的标题 我感到很抱歉 gt y read csv textConnection scan sep n what raw org art type length 191 gk Finish short 4 147 ik Att
  • Linux 中的 R 有哪些可用的 IDE? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 Linux 中的 R 有哪些好的 IDE 我尝试过 Rcmdr 和 Eclipse 但似乎都不具有与 Windows 中的 Tinn R
  • 根据列中的部分字符串匹配选择数据框行

    我想根据列中字符串的部分匹配从数据框中选择行 例如列 x 包含字符串 hsa 使用sqldf if它有一个like语法 我会做类似的事情 select from lt gt where x like hsa 很遗憾 sqldf不支持该语法

随机推荐

  • 如何使 StdIn.isEmpty() 返回 true?

    我正在使用算法 Coursera 算法课程中提供的普林斯顿库中的 StdIn isEmpty 方法 但对其工作原理感到困惑 我有声明 while StdIn isEmpty 附带一些读取用户输入的代码 但我似乎无法跳出循环 根据我的理解 如
  • python sqlite 插入命名参数或 null

    我正在尝试使用命名参数将字典中的数据插入到数据库中 我用一个简单的 SQL 语句来完成这个工作 例如 SQL INSERT INTO status location arrival departure VALUES location arr
  • 处理大型数组而不会出现运行时错误

    当我使用大小为 10 5 10 5 的数组 例如int a 100000 100000 由于该数组消耗更多内存 这可能是运行时错误的原因之一 我应该如何声明这个数组 二维数组 例如int a 100000 100000 这样我就可以应对问题
  • 使用“git filter-branch”来更正最后 N 次提交中的提交者日期?

    我最近需要使用 git am 向我的存储库应用 16 个补丁 并且我小心翼翼地为每个补丁使用 committer date is author date 然而 我还需要调整每个 amd 补丁的提交消息 并且在完成所有 16 个补丁之后 我发
  • MPI - 异步广播/收集

    我有一个项目 需要 n 个进程才能工作 直到问题得到解决 每个从属进程执行相同的代码 当某种情况出现时 该进程需要以非阻塞的方式通知所有其他进程 其他进程也需要以非阻塞的方式接收这个消息 有没有办法不用线程单独的循环 我已经有一段时间没有使
  • 如何在 Drools 中参数化后时间约束?

    我有一个与以下示例非常相似的规则 rule correlate orders when bo BuyOrderEvent id id ae AckEvent id id this after 0 10s bo then do somethi
  • 为什么 PHP Composer 这么慢?

    Why is PHP 作曲家 https en wikipedia org wiki Composer software 当我所做的只是初始化一个零依赖项的项目时 速度这么慢 以下是我运行的命令 composer init composer
  • 为什么闭包比全局变量更能保存变量?

    我了解闭包在 JavaScript 中的工作原理 但我的问题是为什么要费尽心思创建闭包来保存变量 不能把变量设为全局吗 或者这会扰乱全局范围并使您的代码容易出错 这是一个范围界定问题 全局变量就是这样 Global toeveryone 使
  • 我应该如何安装keras-bert才能在R端正确使用?

    我正在尝试按照解释安装 keras berthere https blogs rstudio com ai posts 2019 09 30 bert r 虽然它已成功安装在环境 as 上 但我在 R 端看不到 keras bert ber
  • Kafka 事务失败但仍提交偏移量

    我正在尝试了解 Kafka 事务和一次性事务 我创建了一个事务消费者 我想确保我读取并处理某个主题的所有消息 如果事务失败并且消息因此丢失 Kafka 仍会提交偏移量 更正式地说 如果流处理应用程序消费消息 A 并生成消息 B 使得 B F
  • 如何将重音字符与 PHP preg 匹配?

    我想让我的用户不仅可以选择填写字母和数字 还可以选择 特殊 字母 例如 等 但是 我不希望他们能够使用符号如 等 有没有办法编写正则表达式来完成此任务 最好不要指定每个特殊字母 我现在有 reg w 你可以使用Unicode 字符属性 ht
  • networkx 有向图属性错误 self._succ

    Context 我正在尝试运行另一位研究人员的代码 它描述了湾区道路网络的交通模型 该模型容易受到地震灾害的影响 我是 Python 新手 因此非常感谢您帮助调试以下错误 Issue 当我尝试按照文件中提供的示例数据运行代码时 按照自述文件
  • Membership.DeleteUser 未删除用户的所有相关行

    Membership DeleteUser 设法删除我要删除的用户的用户配置文件 为什么它不删除它存储在的信息webpages membership 我目前没有使用角色 因此请记住这一点以获取答案 我进行了测试并验证了这一点SimpleMe
  • 可重用预处理器 __COUNTER__

    我正在做一些模板元编程 主要是编写我自己的编译时间列表 但我也有一些预处理器魔法 我想用它们来使事情变得更容易 如果可能的话 我想做的是创建一个函子的编译时列表 该部分已完成 但用于简化创建 并添加到列表中 的宏尚未完成 简单举个例子 te
  • 如何实现Web应用程序的屏幕共享? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 Prologue 我想问 如何创建在网络上共享桌面屏幕的解决方案 但后来发现有很多这样的问题 这个任务非常复杂 无法用几个字符串来回答
  • 如何防止我的 .Net dll 被添加为引用?

    假设我编写了一个 Net dll 并希望将其与我的应用程序一起分发 我怎样才能防止任何有能力安装 VS 副本的用户向自己的应用程序添加对此 dll 的引用 无法阻止用户添加引用 这是 DLL 部分的被动操作 您无法采取任何措施来阻止它发生
  • 如果不存在则创建

    我有一个 Django 应用程序 它从 Web API 读取数据并将其放入数据库中 有没有办法从模式创建新对象 但如果对象已经存在 则可以防止重复异常 换句话说 有没有办法保存一个对象 但如果它已经存在则什么都不做 Model object
  • 通过 Powershell 在 IIS 7.5 中启用模拟

    我希望有人可以提供帮助 我正在尝试在 IIS 7 的身份验证会话下启用 ASP Net 模拟 我已使用以下命令启用了其他部分 Set WebConfigurationProperty filter system WebServer secu
  • 在 Perl 中启动非等待后台进程

    我有一个 Perl 脚本 需要在后台启动另一个进程并退出 而不等待其他脚本完成 StackOverflow 上有很多线程介绍如何在 Perl 中等待或如何不等待其他编程语言 但我似乎找不到 Perl 的正确答案 我已经阅读了相当多的内容 并
  • 为什么 foreach %dopar% 每增加一个节点就会变慢?

    我编写了一个简单的矩阵乘法来测试网络的多线程 并行化功能 我注意到计算速度比预期慢得多 The Test很简单 乘以 2 个矩阵 4096x4096 并返回计算时间 矩阵和结果都不被存储 计算时间并非微不足道 50 90 秒 具体取决于您的