一种快速生成CSV的方法

news2024/11/5 20:26:40

事情是这个样子的

在QQ群在聊把如何100万数据导出成CSV文件?会不会很慢?

俺回了一句“现在的机器性能好,没啥问题”。

然后大家开始谈论机器的配置了。哎,俺的机器配置有点差。

然后俺就进行了一个测试。

测试数据

数据定义

        public struct Rec
        {
            public int v1;
            public int v2;
        }
        private Rec[] Data;

        这里为啥不用class呢,因为这个场景struct 更简单。

测试数据生成

            Data = new Rec[200*10000];
            Random r = new Random(DateTime.Now.Millisecond);
            for(int i= 0;i < Data.Length;i++)
            {
                Data[i].v1 = r.Next();
                Data[i].v2 = r.Next();
            }

测试数据200万行,每行2个数字。

测试结果

测试程序为 .NET Framework 4.8 64 Release 版本

如果用.Net 8 版本进行测试,除了方法3之外,会快一些。方法3速度基本不变。

 开始写代码

方法一

这是最最普通的生成CSV的方法,就是一个一个数据的写入到CSV中。没啥可说的。就是用来当做参照物的,耗时0.536秒

            string fn = @"1.csv";  
            FileStream fs = new FileStream(fn, FileMode.Create);
            DateTime t1 = DateTime.Now;
            byte[] buff;
            byte[] buff_rn = ASCIIEncoding.ASCII.GetBytes("\r\n");
            byte[] buff_split = ASCIIEncoding.ASCII.GetBytes(",");
            string s ="v1,v2";
            buff = ASCIIEncoding.ASCII.GetBytes(s);
            fs.Write(buff,0,buff.Length);
            foreach (Rec i in Data)
            {
                fs.Write(buff_rn, 0, buff_rn.Length); 

                s = i.v1.ToString();
                buff = ASCIIEncoding.ASCII.GetBytes(s);
                fs.Write(buff, 0, buff.Length);

                fs.Write(buff_split, 0, buff_split.Length);

                s = i.v2.ToString();
                buff = ASCIIEncoding.ASCII.GetBytes(s);
                fs.Write(buff, 0, buff.Length);  
            }
            
            fs.Flush();
            fs.Close();
            fs.Dispose();
            DateTime t2 = DateTime.Now;
            label1.Text=("输出"+Data.Length.ToString()+"行CSV文件,耗时" +((t2 - t1).TotalMilliseconds/1000).ToString("0.000")+"秒");
 

方法二

在方法1上加了一个8M的缓存。其实这种写法在其他语言(c++、delphi 等)还有效果,C#效果不大,因为C#的文件流本身有优惠。耗时0.503秒

            string fn = @"2.csv"; 
            int ms_size = 8 * 1024 * 1024;
            byte[] ms=new byte[ms_size + 1024];
            int ms_position = 0;
            FileStream fs = new FileStream(fn, FileMode.Create);
            DateTime t1 = DateTime.Now;
            byte[] buff;
            byte[] buff_rn = ASCIIEncoding.ASCII.GetBytes("\r\n");
            byte[] buff_split = ASCIIEncoding.ASCII.GetBytes(",");
            string s = "v1,v2";
            buff = ASCIIEncoding.ASCII.GetBytes(s);
            Array.Copy(buff, 0, ms, ms_position, buff.Length);
            ms_position+= buff.Length;
            foreach (Rec i in Data)
            {
                Array.Copy(buff_rn, 0, ms, ms_position, buff_rn.Length);
                ms_position += buff_rn.Length; 

                s = i.v1.ToString();
                buff = ASCIIEncoding.ASCII.GetBytes(s); 
                Array.Copy(buff, 0, ms, ms_position, buff.Length);
                ms_position += buff.Length;
                 
                Array.Copy(buff_split, 0, ms, ms_position, buff_split.Length);
                ms_position += buff_split.Length;

                s = i.v2.ToString();
                buff = ASCIIEncoding.ASCII.GetBytes(s);
                Array.Copy(buff, 0, ms, ms_position, buff.Length);
                ms_position += buff.Length;

                if (ms_position > ms_size)
                { 
                    fs.Write(ms,0, ms_position); 
                    ms_position = 0;
                }
            }
            if (ms_position > 0)
            {
                fs.Write(ms, 0, ms_position);
            }
            fs.Flush();
            fs.Close();
            fs.Dispose();
            DateTime t2 = DateTime.Now;
            label2.Text = ("输出" + Data.Length.ToString() + "行CSV文件,耗时" + ((t2 - t1).TotalMilliseconds / 1000).ToString("0.000") + "秒");

优化

我们先分析一下,生成CSV中,主要的计算是啥?在这个测试上主要计算就是,整型转字符串。执行了400万次(200万行,每行2个数字)整型转字符串。那么我们就先测试一下执行了400万次整型转字符串多少时间。

            string s = "";
            DateTime t1 = DateTime.Now;
            foreach (Rec i in Data)
            {
                s = i.v1.ToString();
                s = i.v2.ToString();
            }
            DateTime t2 = DateTime.Now;
            label4.Text = ( (Data.Length*2).ToString() + "次ToString,耗时" + ((t2 - t1).TotalMilliseconds / 1000).ToString("0.000") + "秒");

耗时是0.265秒

那么我们就像从 整型转字符串 开始优化。

方法三

  使用了 IntToString 函数,速度优化到了 耗时0.176秒。优化到这里出现了一个有趣的数据:

 只执行 整型转字符串 耗时是0.265秒,方法三的速度比只 整型转字符串 还快。为什么呢?

因为在 方法三 中,其实没有使用字符串,取消中间商赚差价。

           string fn = @"3.csv";
            int ms_size = 8 * 1024 * 1024;
            byte[] ms = new byte[ms_size + 1024];
            int ms_position = 0;
            FileStream fs = new FileStream(fn, FileMode.Create);
            DateTime t1 = DateTime.Now;
            byte[] buff;
            byte[] buff_rn = ASCIIEncoding.ASCII.GetBytes("\r\n");
            byte[] buff_split = ASCIIEncoding.ASCII.GetBytes(",");
            string s = "v1,v2";
            buff = ASCIIEncoding.ASCII.GetBytes(s);
            Array.Copy(buff, 0, ms, ms_position, buff.Length);
            ms_position += buff.Length;
            foreach (Rec i in Data)
            {
                Array.Copy(buff_rn, 0, ms, ms_position, buff_rn.Length);
                ms_position += buff_rn.Length;

                IntToString(i.v1,ref ms, ref ms_position); 


                Array.Copy(buff_split, 0, ms, ms_position, buff_split.Length);
                ms_position += buff_split.Length;

                IntToString(i.v2, ref ms, ref ms_position);

                if (ms_position > ms_size)
                {
                    fs.Write(ms, 0, ms_position);
                    ms_position = 0;
                }
            }
            if (ms_position > 0)
            {
                fs.Write(ms, 0, ms_position);
            }
            fs.Flush();
            fs.Close();
            fs.Dispose();
            DateTime t2 = DateTime.Now;
            label3.Text = ("输出" + Data.Length.ToString() + "行CSV文件,耗时" + ((t2 - t1).TotalMilliseconds / 1000).ToString("0.000") + "秒");
 

这个代码的功能  其实就个 int.ToString。 看上去下面这个代码有点拉胯,像是那种故意搞笑的代码,是的确实拉胯,但是它确实快。

        public static void IntToString(int n,ref byte[] buff,ref int position)
        {
            int len = 0;
            int len_inc = 16;
            int p = position+ len_inc;
            do
            {
                buff[p] = (byte)((n % 10)+48);
                p += 1;
                len += 1;
            }
            while ((n /= 10) != 0);
            p -= 1;
            for (int i = 0; i < len;i++)
            {
                buff[position]= buff[p];
                position += 1;
                p -= 1;
            } 
        }

其他

Release 版本 和 Debug版本差别很大,例如 方法三 的Debug版 速度并不快,但是用Release 版本,方法3就明显快。

使用.Net 8 版本进行测试,结果如下

.NET Framework 4.8.Net 8对比
方法10.5380.257.Net 8 快很多
方法20.5030.190.Net 8 快很多
方法30.1760.178两者差不多
整型转字符串0.2650.075.Net 8 快很多

上面的数据进行时测试,不是很准确。但是可以看出 :

.Net 8 在 整型转字符串 上要快很多。方法三因为没有用 .ToString() 所以 速度在.NET Framework 4.8  和  .Net 8 下差不多。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2114762.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++二分查找】2439. 最小化数组中的最大值

本文涉及的基础知识点 C二分查找 LeetCode2439. 最小化数组中的最大值 给你一个下标从 0 开始的数组 nums &#xff0c;它含有 n 个非负整数。 每一步操作中&#xff0c;你需要&#xff1a; 选择一个满足 1 < i < n 的整数 i &#xff0c;且 nums[i] > 0 。 将 num…

C++ | Leetcode C++题解之第392题判断子序列

题目&#xff1a; 题解&#xff1a; class Solution { public:bool isSubsequence(string s, string t) {int n s.size(), m t.size();vector<vector<int> > f(m 1, vector<int>(26, 0));for (int i 0; i < 26; i) {f[m][i] m;}for (int i m - 1; …

.Net6/.Net8(.Net Core) IIS中部署 使用 IFormFile 上传大文件报错解决方案

描述 最近使用.Net6 WebAPI IFormFile对象接收上传文件时大于30MB(兆)的文件就会报错 原因分析 IIS上传文件有大小默认限制大约28.6MB 解决办法 .无论是Net6还是.Net8写法都一样 方法一&#xff1a;IIS可视化操作 1.打开Internet Information Services (llS)管理器&…

Banana Pi BPI-SM9 AI 计算模组采用算能科技BM1688芯片方案设计

产品概述 香蕉派 Banana Pi BPI-SM9 16-ENC-A3 深度学习计算模组搭载算能科技高集成度处理器 BM1688&#xff0c;功耗低、算力强、接口丰富、兼容性好。支持INT4/INT8/FP16/BF16/FP32混合精度计算&#xff0c;可支持 16 路高清视频实时分析&#xff0c;灵活应对图像、语音、自…

LeetCode --- 413周赛

题目列表 3274. 检查棋盘方格颜色是否相同 3275. 第 K 近障碍物查询 3276. 选择矩阵中单元格的最大得分 3277. 查询子数组最大异或值 一、检查棋盘方格颜色是否相同 题目给定两个字符串来表示两个方格的坐标&#xff0c;让我们判断这两个方格的颜色是否相同&#xff0c;这…

C++——关联式容器(2):AVL树(平衡二叉树)

2.AVL树 2.1 AVL树的概念 在学习了二叉搜索树后&#xff0c;我们发现了二叉搜索树可以根据大小比较来进行类似于折半查找的操作&#xff0c;使得搜索时间复杂度达到logn的水准。但是在面对极端情况下&#xff0c;如近似有序的序列&#xff0c;那么整棵树的时间复杂度就有可能退…

【Godot4.3】多边形的斜线填充效果基础实现

概述 图案&#xff08;Pattern&#xff09;填充是一个非常常见的效果。其中又以斜线填充最为简单。本篇就探讨在Godot4.3中如何使用Geometry2D和CanvasItem的绘图函数实现斜线填充效果。 基础思路 Geometry2D类提供了多边形和多边形以及多边形与折线的布尔运算。按照自然的思…

Spring-@Bean的处理流程

Bean前置知识 1 需要再Configuration Class中才能被解析 2 静态Bean也就是标注在static方法上的 实例Bean标注在普通方法上的 所有的Bean在创建之前都会变成BeanDefinition,其中有这样两个属性&#xff1a; setFactoryMethodName&#xff1a;静态方法 setFactoryBeanName&…

【详解 Java 注解】

前言&#xff1a; 注解&#xff08;Annotation&#xff09;是Java中的一种特殊符号&#xff0c;用来为代码提供额外的信息。它不会改变程序的逻辑&#xff0c;只是用来给编译器或工具提供指示。例如&#xff0c;Override 表示一个方法是重写了父类的方法&#xff0c;Deprecated…

[论文笔记]LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale

引言 今天带来第一篇量化论文LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale笔记。 为了简单&#xff0c;下文中以翻译的口吻记录&#xff0c;比如替换"作者"为"我们"。 大语言模型已被广泛采用&#xff0c;但推理时需要大量的GPU内…

GPU 带宽功耗优化

移动端GPU 的内存结构&#xff1a; 先简述移动端内存cache结构&#xff1b;上图的UMA结构 on-Chip memory 包括了 L1、L2 cache&#xff0c;非常关键的移动端的 Tiles 也是保存在 on-chip上还包括寄存器文件&#xff1a;提供给每个核心使用的极高速存储。 共享内存&#xff08…

【C/C++】web服务器项目开发总结【请求 | 响应 | CGI】

博客主页&#xff1a;花果山~程序猿-CSDN博客 文章分栏&#xff1a;Linux_花果山~程序猿的博客-CSDN博客 关注我一起学习&#xff0c;一起进步&#xff0c;一起探索编程的无限可能吧&#xff01;让我们一起努力&#xff0c;一起成长&#xff01; 目录 一&#xff0c;背景 二&…

机器学习(西瓜书)第 4 章 决策树

4.1 决策树基本流程 决策树模型 基本流程 在第⑵种情形下&#xff0c;我们把当前结点标记为叶结点&#xff0c;并将其类别设定为该结点所含样本最多的类别&#xff1b;在第⑶种情形下&#xff0c;同样把当前结点标记为叶结点&#xff0c;但将其类别设定为其父结点所含样本最多…

VMware时提示系统尚未修改安装失败

安装VMware安装失败&#xff0c;提示系统尚未修改 有以下解决方案&#xff1a; 1.操作系统不兼容 2.安装文件损坏 3.安装程序错误 4.硬件问题 解决&#xff1a;由于重装系统前&#xff0c;安装过VAware&#xff0c;所以应该操作系统&#xff0c;硬件没有问题。下载一个软件v…

多线程篇(阻塞队列- ArrayBlockingQueue)(持续更新迭代)

目录 一、源码分析 1. 先看个关系图 2. 构造方法 3. 核心属性 4. 核心功能 入队&#xff08;放入数据&#xff09; 出队&#xff08;取出数据&#xff09; 5. 总结 一、源码分析 1. 先看个关系图 PS&#xff1a;先看个关系图 ArrayBlockingQueue是最典型的有界阻塞队…

CSDN文章无水印转成PDF

文章目录 一、打开检查二、点击进入控制台三、在控制台中输入代码 一、打开检查 f11或者右键打开检查 二、点击进入控制台 三、在控制台中输入代码 (function(){ use strict;var articleBox $("div.article_content");articleBox.removeAttr("style&quo…

sping boot 基于 RESTful 风格,模拟增删改查操作

RESTful -> 增&#xff1a;post 删&#xff1a;delete 改: put 查: get RESTful 资源路径&#xff0c;一般以 s 复数结尾 以下是代码示例&#xff1a; package com.example.springboot.controller;import org.springframework.web.bind.annotation.*;RestControll…

EmguCV学习笔记 C# 9.3 移动检测类

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

汽车网络安全的未来:将车辆视为端点

汽车行业面临着许多与其他行业的成功企业相同的网络安全风险和威胁&#xff0c;但它也在应对一些独特的风险和威胁。 Nuspire 的首席威胁分析师 Josh Smith&#xff08;一家在汽车领域有着深厚根基并保护通用汽车和斯巴鲁等客户的托管安全服务提供商&#xff09;谈到了当前的风…

【AcWing】852. spfa判断负环

#include<iostream> #include<algorithm> #include<cstring> #include<queue> using namespace std;const int N 1e510;int n,m; int h[N],w[N],e[N],ne[N],idx; int dist[N],cnt[N];//cnt存最短路径的边数 bool st[N];void add(int a,int b,int c){e[…