我在这个网站上看到一个关于字符串连接速度的问题。在那个主题中,人们写了一些带有奇怪数字的短暂基准。 How to efficiently concatenate strings in Go?
我决定检查这些速度并编写测试。我的测试显示了其他结果。在大尺寸上,“+”运算符比其他方法更快。是吗?
这是我的代码。
package main
import (
"bytes"
"fmt"
"runtime/debug"
"time"
)
const variations = 30
var time1, time2 time.Time
var delta, catcher string
var x, deltaSize, k, dataSize, operations uint64
var i, j, x_min uint64
var l int
var delta_byte []byte
var method1Speed, method2Speed, method3Speed, method3ASpeed, method2ASpeed [variations]uint64
var dataTotal [variations]uint64
var tmp []byte
func main() {
x_min = 2
operations = 1
for x = variations; x >= x_min; x = x - 2 {
deltaSize = 1 << x // 2^x
dataSize = operations * deltaSize
dataTotal[x-1] = dataSize
fmt.Println("Step #", x, "delta=", deltaSize, "op.=", operations, "data=", dataSize)
fmt.Println("Preparing Data...")
delta_byte = make([]byte, deltaSize)
for i = 0; i < deltaSize; i++ {
delta_byte[i] = 255
}
delta = string(delta_byte)
delta_byte = nil
catcher = ""
debug.FreeOSMemory()
fmt.Println("Testing Method #1...")
time1 = time.Now()
for j = 1; j <= operations; j++ {
//----------------------------
catcher += delta
//----------------------------
}
time2 = time.Now()
method1Speed[x-1] = uint64((1000000 * float64(dataSize)) / float64(time2.Sub(time1).Nanoseconds())) // KiB/sec.
catcher = ""
debug.FreeOSMemory()
fmt.Println("Testing Method #2...")
time1 = time.Now()
for j = 1; j <= operations; j++ {
//----------------------------
stringsJoinViaCopy(&catcher, &catcher, &delta)
//----------------------------
}
time2 = time.Now()
method2Speed[x-1] = uint64((1000000 * float64(dataSize)) / float64(time2.Sub(time1).Nanoseconds())) // KiB/sec.
catcher = ""
debug.FreeOSMemory()
fmt.Println("Testing Method #3...")
time1 = time.Now()
for j = 1; j <= operations; j++ {
//----------------------------
stringsJoinViaBuffer(&catcher, &catcher, &delta)
//----------------------------
}
time2 = time.Now()
method3Speed[x-1] = uint64((1000000 * float64(dataSize)) / float64(time2.Sub(time1).Nanoseconds())) // KiB/sec.
catcher = ""
debug.FreeOSMemory()
fmt.Println("Testing Method #3A...")
time1 = time.Now()
buffer := bytes.NewBuffer(nil)
for j = 1; j <= operations; j++ {
//----------------------------
buffer.WriteString(delta)
//----------------------------
}
catcher = buffer.String()
time2 = time.Now()
method3ASpeed[x-1] = uint64((1000000 * float64(dataSize)) / float64(time2.Sub(time1).Nanoseconds())) // KiB/sec.
catcher = ""
debug.FreeOSMemory()
fmt.Println("Testing Method #2A...")
time1 = time.Now()
tmp = make([]byte, int(operations)*len(delta)) // Cheating (guessing) with size
l = 0
for j = 1; j <= operations; j++ {
//----------------------------
l += copy(tmp[l:], delta)
//----------------------------
}
catcher = string(tmp)
time2 = time.Now()
method2ASpeed[x-1] = uint64((1000000 * float64(dataSize)) / float64(time2.Sub(time1).Nanoseconds())) // KiB/sec.
catcher = ""
delta = ""
debug.FreeOSMemory()
///
operations *= 2
}
// Show Results
fmt.Println("#. ops. Total Data, B. Speed (KiB/sec) M1 M2 M3 M3A M2A")
for x = x_min; x <= variations; x = x + 2 {
dataSize = 1 << x // 2^x
operations = 1 << (variations - x)
fmt.Println(x, operations, dataTotal[x-1], method1Speed[x-1], method2Speed[x-1], method3Speed[x-1],
method3ASpeed[x-1], method2ASpeed[x-1])
}
}
//------------------------------------------------------------------------------
func stringsJoinViaBuffer(dest, a, b *string) {
// Joins two strings (a & b) using Buffer and puts them into dest.
buffer := bytes.NewBuffer(nil)
buffer.WriteString(*a)
buffer.WriteString(*b)
*dest = buffer.String()
}
//------------------------------------------------------------------------------
func stringsJoinViaCopy(dest, a, b *string) {
x := make([]byte, len(*a)+len(*b))
i := 0
i += copy(x[i:], *a)
i += copy(x[i:], *b)
*dest = string(x)
}
这是结果
#. ops. Total Data, B. Speed (KiB/sec) M1 M2 M3 M3A M2A
2 268435456 65536 236 109 57 108413 301653
4 67108864 131072 464 227 113 251519 576660
6 16777216 262144 895 410 202 225300 626165
8 4194304 524288 1514 672 351 205068 552088
10 1048576 1048576 3187 1412 756 207588 532239
12 262144 2097152 7980 3238 1727 209447 592230
14 65536 4194304 16361 6553 3641 230521 536320
16 16384 8388608 29568 12170 6835 241752 604050
18 4096 16777216 55158 23950 13549 238039 563997
20 1024 33554432 98348 43400 25958 216947 521189
22 256 67108864 168906 80442 48725 231806 534722
24 64 134217728 299127 129035 89686 254403 519534
26 16 268435456 529730 207405 153894 284578 506730
28 4 536870912 1167316 353510 268546 359990 523471
30 1 1073741824 909950698305 503703 581848 572763 579852
当您有大量数据持续传输或可以通过猜测大小作弊时,它似乎很有效……是否正确?如果有偶然的字符串,简单的“+”更好?不知何故,在提到的问题中,人们在没有实际任务的情况下测量了字节传输。
不知何故,在步骤 #26 中,“+”运算符甚至比猜测大小作弊更快!
最佳答案
这是一个 Go 基准测试入门工具包。
concat_test.go:
package main
import (
"bytes"
"strconv"
"strings"
"testing"
)
func BenchmarkConcat(b *testing.B) {
var s string
for n := 1; n <= 1<<12; n <<= 3 {
s1 := strings.Repeat("a", n)
s2 := strings.Repeat("b", n)
b.Run("PlusL"+strconv.Itoa(n), func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
s = s1 + s2
}
b.StopTimer()
},
)
b.Run("CopyL"+strconv.Itoa(n), func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
buf := make([]byte, len(s1)+len(s2))
copy(buf[copy(buf, s1):], s2)
s = string(buf)
}
b.StopTimer()
},
)
b.Run("BufferL"+strconv.Itoa(n), func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var buf bytes.Buffer
buf.WriteString(s1)
buf.WriteString(s2)
s = buf.String()
}
b.StopTimer()
},
)
}
_ = s
}
输出:
$ go test -bench=.
goos: linux
goarch: amd64
pkg: so/concat
BenchmarkConcat/PlusL1-4 30000000 55.9 ns/op 2 B/op 1 allocs/op
BenchmarkConcat/CopyL1-4 30000000 63.0 ns/op 4 B/op 2 allocs/op
BenchmarkConcat/BufferL1-4 10000000 115 ns/op 114 B/op 2 allocs/op
BenchmarkConcat/PlusL8-4 20000000 78.1 ns/op 16 B/op 1 allocs/op
BenchmarkConcat/CopyL8-4 20000000 99.2 ns/op 32 B/op 2 allocs/op
BenchmarkConcat/BufferL8-4 10000000 131 ns/op 128 B/op 2 allocs/op
BenchmarkConcat/PlusL64-4 20000000 85.3 ns/op 128 B/op 1 allocs/op
BenchmarkConcat/CopyL64-4 10000000 125 ns/op 256 B/op 2 allocs/op
BenchmarkConcat/BufferL64-4 5000000 328 ns/op 432 B/op 3 allocs/op
BenchmarkConcat/PlusL512-4 5000000 249 ns/op 1024 B/op 1 allocs/op
BenchmarkConcat/CopyL512-4 3000000 457 ns/op 2048 B/op 2 allocs/op
BenchmarkConcat/BufferL512-4 1000000 1012 ns/op 3184 B/op 4 allocs/op
BenchmarkConcat/PlusL4096-4 1000000 1527 ns/op 8192 B/op 1 allocs/op
BenchmarkConcat/CopyL4096-4 500000 3132 ns/op 16384 B/op 2 allocs/op
BenchmarkConcat/BufferL4096-4 300000 4863 ns/op 24688 B/op 4 allocs/op
PASS
ok so/concat 24.308s
$
关于string - Go中字符串变量的连接速度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43460604/
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?
我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,
在我的Rails(2.3,Ruby1.8.7)应用程序中,我需要将字符串截断到一定长度。该字符串是unicode,在控制台中运行测试时,例如'א'.length,我意识到返回了双倍长度。我想要一个与编码无关的长度,以便对unicode字符串或latin1编码字符串进行相同的截断。我已经了解了Ruby的大部分unicode资料,但仍然有些一头雾水。应该如何解决这个问题? 最佳答案 Rails有一个返回多字节字符的mb_chars方法。试试unicode_string.mb_chars.slice(0,50)
对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl
大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje
我试图获取一个长度在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
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我有一大串格式化数据(例如JSON),我想使用Psychinruby同时保留格式转储到YAML。基本上,我希望JSON使用literalstyle出现在YAML中:---json:|{"page":1,"results":["item","another"],"total_pages":0}但是,当我使用YAML.dump时,它不使用文字样式。我得到这样的东西:---json:!"{\n\"page\":1,\n\"results\":[\n\"item\",\"another\"\n],\n\"total_pages\":0\n}\n"我如何告诉Psych以想要的样式转储标量?解
我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击