我用两种语言编写了相同的代码:
import os
file1 = open("z1.txt", "w")
file2 = open("z2.txt", "w")
for x in range(-1000, 1000):
for y in range(-1000, 1000):
z1 = (x + y) * (x + y)
z2 = x * y
args = str(x) + "\t" + str(y) + "\t"
toFile1 = args + str(z1) + "\n"
toFile2 = args + str(z2) + "\n"
file1.write(toFile1)
file2.write(toFile2)
file1.close()
file2.close()
package main
import (
"os"
"strconv"
)
func main() {
file1, _ := os.Create("z1.txt")
file2, _ := os.Create("z2.txt")
for x := -1000; x < 1000; x++ {
for y := -1000; y < 1000; y++ {
z1 := (x + y) * (x + y)
z2 := x * y
args := strconv.Itoa(x) + "\t" + strconv.Itoa(y) + "\t"
toFile1 := []byte(args + strconv.Itoa(z1) + "\n")
toFile2 := []byte(args + strconv.Itoa(z2) + "\n")
file1.Write(toFile1)
file2.Write(toFile2)
}
}
file1.Close()
file2.Close()
}
一个意外的惊喜——在 golang 上,最坏情况下运行时间为 12 秒,而在 python 中则为 7 秒。在平均情况下,go 也远远落后,大约进行了 30 次运行。此外,golang 本身在该程序中以 2 个线程工作,这似乎可以加快写入两个文件的速度。
我怀疑我在 Go 中错误地使用了太低级的函数,并决定在 C 中实现相同的代码:
#include "stdio.h"
int main(){
FILE* f1 = fopen("z1.txt","w");
FILE* f2 = fopen("z2.txt","w");
for (int x =-1000; x <1000;++x ){
for(int y =-1000; y <1000;++y ){
int z1 = (x+y)*(x+y);
int z2 = x*y;
fprintf(f1,"%d\t%d\t%d\n",x,y,z1);
fprintf(f2,"%d\t%d\t%d\n",x,y,z2);
}
}
fclose(f1);
fclose(f2);
return 0;
}
这里没有什么令人惊讶的——它会在瞬间完成所有事情。好吧,我尝试了 GoS 实现,它变得更糟 - 平均 22 秒。
package main
import (
"fmt"
"os"
)
func main() {
file1, _ := os.Create("z1.txt")
file2, _ := os.Create("z2.txt")
for x := -1000; x < 1000; x++ {
for y := -1000; y < 1000; y++ {
z1 := (x + y) * (x + y)
z2 := x * y
fmt.Fprintf(file1, "%d\t%d\t%d\n", x, y, z1)
fmt.Fprintf(file2, "%d\t%d\t%d\n", x, y, z2)
}
}
file1.Close()
file2.Close()
}
接下来,我尝试优化 Go 中的代码并写入文件 1 次,而不是循环写入,并将缓冲区存储在一行中,我在 Python 中做了同样的事情,但没有任何改变,我无法理解出了什么问题,为什么 golang 在这项任务上落后了。
带 Go 手写缓冲区的版本:
package main
import (
"os"
"strconv"
)
func main() {
file1, _ := os.Create("z1.txt")
file2, _ := os.Create("z2.txt")
var toFile1 string
var toFile2 string
for x := -1000; x < 1000; x++ {
for y := -1000; y < 1000; y++ {
z1 := (x + y) * (x + y)
z2 := x * y
args := strconv.Itoa(x) + "\t" + strconv.Itoa(y) + "\t"
toFile1 += (args + strconv.Itoa(z1) + "\n")
toFile2 += (args + strconv.Itoa(z2) + "\n")
}
}
file1.Write([]byte(toFile1))
file2.Write([]byte(toFile2))
file1.Close()
file2.Close()
}
如果可能的话,请给出详细的答案,而不是“在go中还有一个额外的动作——转换为字节”。先感谢您)
python3 Python 3.10.12(主要,2023 年 11 月 20 日,15:14:05)[GCC 11.4.0] 在 Linux 上键入“help”、“copyright”、“credits”或“license”以获取更多信息。
go版本go1.21.6 linux/amd64
Go 中的文件没有缓冲区。对文件的每次写操作都会导致对磁盘的写入。在从操作系统收到数据已写入磁盘的保证后,程序接收控制。测试:
排队时间长
sys
- 将数据写入磁盘的等待时间。让我们添加缓冲区:
它变得好多了:
在Python和C中,缓冲区直接构建到文件对象中,您可以立即获得良好的性能,但不能保证在写入时正确写入数据。该保证仅在缓冲区重置后出现。
对于方向,你的 C 代码对我来说几乎与缓冲的 Go 一样:
但是如果将缓冲区重置添加到 C 中的循环 (
fflush(f1); fflush(f2);
) 中会发生什么?再次,等待录制的七秒: