草庐IT

arrays - 如何在不将所有值清零的情况下初始化长 Golang 数组?

coder 2024-07-12 原文

在 Go 中创建数组时,似乎数组将始终为零,即使在初始化后立即设置不同的值,例如当值应设置为数组中的索引时。

避免这种情况的一种方法是使用数组字面量,例如 a = [5]int{0,1,2,3,4},但对于长数组来说这变得不切实际。我想知道执行初始化的最佳方法是什么。

令人惊讶的是,命名返回函数优于大型数组的复合文字初始化。

我创建了以下基准来比较性能:

package main

import "testing"

const N = 1000000

var result [N]int

func arrayLiteral() [N]int {
    // Replace the 3 dots with the actual value
    // I copy-pasted the output of an other program to do this
    return [N]int{0,1,2,3,...,N-1}
}

func arrayLoopNamedReturn() (a [N]int) {
    for i := 0; i < N; i++ {
        a[i] = i
    }
    return
}

func arrayLoop() [N]int {
    var a [N]int
    for i := 0; i < N; i++ {
        a[i] = i
    }
    return a
}

func BenchmarkArrayLoop(b *testing.B) {
    var r [N]int
    for n := 0; n < b.N; n++ {
        r = arrayLoop()
    }
    result = r
}

func BenchmarkArrayLoopNamedReturn(b *testing.B) {
    var r [N]int
    for n := 0; n < b.N; n++ {
        r = arrayLoopNamedReturn()
    }
    result = r
}

func BenchmarkArrayLiteral(b *testing.B) {
    var r [N]int
    for n := 0; n < b.N; n++ {
        r = arrayLiteral()
    }
    result = r
}

结果:

N = 10,000
BenchmarkArrayLoop-8                      200000              9041 ns/op
BenchmarkArrayLoopNamedReturn-8           200000              6327 ns/op
BenchmarkArrayLiteral-8                   300000              4300 ns/op

N = 100,000
BenchmarkArrayLoop-8                       10000            191582 ns/op
BenchmarkArrayLoopNamedReturn-8            20000             76125 ns/op
BenchmarkArrayLiteral-8                    20000             62714 ns/op

N = 1,000,000
BenchmarkArrayLoop-8                         500           2635713 ns/op
BenchmarkArrayLoopNamedReturn-8             1000           1537282 ns/op
BenchmarkArrayLiteral-8                     1000           1854348 ns/op

观察:

  1. 我没想到命名返回值会对循环产生影响,我认为编译器肯定会做一些优化。对于 1,000,000,它变得比文字初始化更快。

  2. 我期望线性缩放,但我不明白为什么这两种方法都不是这种情况。

我不确定如何解释这一点,尽管它看起来非常基础。有什么想法吗?

编辑:有一个 open issue on Github提示命名返回值不应该有所作为。我也发现这是一个令人惊讶的行为。

最佳答案

您的结果与数组大小不成线性关系的原因是,并非所有涉及获取新填充数组的操作都与数组大小成线性关系。例如,您需要内存分配,可以选择将分配的内存归零,一个循环来填充数组,并且您必须返回(复制)数组的内存。分配是一个很好的例子,它不应该与大小成线性关系,同样,复制内存也不应该是线性的(应该增加,但不是线性的)。

避免冗长的复合文字和询问需要归零并随后填充的新数组值的一种方法是准备好值,并将其分配给数组变量。

我的意思是有一个包级变量存储计算/填充的数组(最简单的填充一个简单的循环),当你需要一个新的数组填充相同的时候,只需分配存储的值:

var cache [N]int

func init() {
    for i := range cache {
        cache[i] = i
    }
}

// If you now need a new array:
var result = cache
// Or re-init an existing array:
result = cache

如果您将此添加到您的基准测试中:

func BenchmarkArrayAssign(b *testing.B) {
    var r [N]int
    for n := 0; n < b.N; n++ {
        r = cache
    }
    result = r
}

或者简单地说:

func BenchmarkArrayAssign(b *testing.B) {
    for n := 0; n < b.N; n++ {
        result = cache
    }
}

这将比您迄今为止最快的 ArrayLoopNamedReturn 快两倍(当 N = 1_000_000 时)。

BenchmarkArrayAssign-4                  1000       1104829 ns/op
BenchmarkArrayLoop-4                     500       3822005 ns/op
BenchmarkArrayLoopNamedReturn-4          500       2326498 ns/op

关于arrays - 如何在不将所有值清零的情况下初始化长 Golang 数组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57391251/

有关arrays - 如何在不将所有值清零的情况下初始化长 Golang 数组?的更多相关文章

  1. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  2. ruby - 在 Ruby 中实现 `call_user_func_array` - 2

    我怎样才能完成http://php.net/manual/en/function.call-user-func-array.php在ruby中?所以我可以这样做:classAppdeffoo(a,b)putsa+benddefbarargs=[1,2]App.send(:foo,args)#doesn'tworkApp.send(:foo,args[0],args[1])#doeswork,butdoesnotscaleendend 最佳答案 尝试分解数组App.send(:foo,*args)

  3. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  4. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  5. ruby-on-rails - 未初始化的常量 Psych::Syck (NameError) - 2

    在我的gem中,我需要yaml并且在我的本地计算机上运行良好。但是在将我的gem推送到ruby​​gems.org之后,当我尝试使用我的gem时,我收到一条错误消息=>"uninitializedconstantPsych::Syck(NameError)"谁能帮我解决这个问题?附言RubyVersion=>ruby1.9.2,GemVersion=>1.6.2,Bundlerversion=>1.0.15 最佳答案 经过几个小时的研究,我发现=>“YAML使用未维护的Syck库,而Psych使用现代的LibYAML”因此,为了解决

  6. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  7. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  8. ruby - 默认情况下使选项为 false - 2

    这是在Ruby中设置默认值的常用方法:classQuietByDefaultdefinitialize(opts={})@verbose=opts[:verbose]endend这是一个容易落入的陷阱:classVerboseNoMatterWhatdefinitialize(opts={})@verbose=opts[:verbose]||trueendend正确的做法是:classVerboseByDefaultdefinitialize(opts={})@verbose=opts.include?(:verbose)?opts[:verbose]:trueendend编写Verb

  9. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  10. Ruby Koans about_array_assignment - 非平行与平行分配歧视 - 2

    通过ruby​​koans.com,我在about_array_assignment.rb中遇到了这两段代码你怎么知道第一个是非并行赋值,第二个是一个变量的并行赋值?在我看来,除了命名差异之外,代码几乎完全相同。4deftest_non_parallel_assignment5names=["John","Smith"]6assert_equal["John","Smith"],names7end45deftest_parallel_assignment_with_one_variable46first_name,=["John","Smith"]47assert_equal'John

随机推荐