以前一直是做WEB开发,ASP.NET,最近公司要求做一个能生成5万条互不相同的字符串的功能.
用VS2005创建windows程序,windows程序写好后测试能生成5万条.但是效率很低,花了3分钟时间,CPU使用率在98%-100%之间徘徊.
我的代码如下,想请各位帮忙给优化下,让代码跑起来快些.(初次写windows程序,有很多不懂,希望在CSDN请教各位达人!)
Form1.cs代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Text;namespace createtxt
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }        private void button1_Click(object sender, EventArgs e)
        {
            string code = "";
            for (int i = 1; i <= 50000; i++)
            {
                code += rc(i) + "\r\n";
            }
            label1.Text="已生成五万条随机字符串";
            string path = "D:/";
            Encoding codee = Encoding.GetEncoding("UTF-8");
            StreamWriter sw = new StreamWriter(path + "string.txt", false, codee);
            sw.Write(code);
            sw.Flush();
            sw.Close();
            label1.Text = "生成文件成功,文件名为:string.txt";
        }        public static string rc(int a)
        {
            char[] arrchar = new char[]{
            '0','1','2','3','4','5','6','7','8','9',
       'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'
       };
            StringBuilder num = new StringBuilder();
            Random rnd = new Random(a);
            for (int i = 1; i <= 4; i++)
            {
                //num2.Append(arrchar[rnd.Next(0,10)].ToString());
                //num.Append(arrchar[rnd.Next(0, 10)].ToString());
                num.Append(arrchar[rnd.Next(11, arrchar.Length)].ToString());
            }
            for (int j = 1; j <= 8; j++)
            {
                num.Append(arrchar[rnd.Next(0, 10)].ToString());
            }
            return num.ToString();
        }
    }
}

解决方案 »

  1.   

    string code = "";
    for (int i = 1; i <= 50000; i++)
    {
        code += rc(i) + "\r\n"; // 这条语句导致频繁分配内存,code的内存最后为50000*(12 + 2)字节
    }可以用文件流来写入,不用放在一个string中再写
    另外楼主怎么保证不重复?
      

  2.   

    zswang 请问具体该怎么写呢?麻烦你给个实例!
      

  3.   

    在我机器上耗时125毫秒private const int CODE_COUNT = 50000; // 记录数
    private byte[] codeBuffer = new byte[14 * CODE_COUNT];private Random random = new Random(); 
    public byte[] RandomCode()
    {
        byte[] vReturn = new byte[14];
        vReturn[12] = 13;
        vReturn[13] = 10;
        for (int i = 0; i < 4; i++)
            vReturn[i] = (byte)('A' + random.Next(26));
        for (int i = 4; i < 12; i++)
            vReturn[i] = (byte)('0' + random.Next(10));
        return vReturn;
    }private Hashtable vHashtable = new Hashtable();private void button1_Click(object sender, EventArgs e)
    {
        long vTickCount = Environment.TickCount;
        byte[] code = RandomCode();
        for (int i = 0; i < CODE_COUNT; )
        {
            code = RandomCode();
            if (!vHashtable.Contains(code)) // 避免重复
            {
                vHashtable.Add(code, null);
                Array.Copy(code, 0, codeBuffer, i * 14, code.Length);
                i++;
            }
        }
        FileStream vFileStream = new FileStream(@"c:\temp\temp.txt", 
            FileMode.Create, FileAccess.Write);
        int vPos = 0;
        for (; vPos + 0x1000 < codeBuffer.Length; vPos += 0x1000)
        {
            vFileStream.Write(codeBuffer, vPos, 0x1000); // 减少文件写入的次数,采用每4096字节写一次。
        }
        vFileStream.Write(codeBuffer, vPos, codeBuffer.Length - vPos);
        vFileStream.Close();
        Console.WriteLine("耗时:{0}毫秒", Environment.TickCount - vTickCount);
    }
    关键的地方两点:1、不要频繁分配内存。2、不要频繁读写文件。
    至于用hash表判断本身并不能起到关键性的提高。
      

  4.   

    Hashtable使用记得加上using System.Collections;
      

  5.   

    if (!vHashtable.Contains(code)) // 避免重复
    这一句错了,code是每一次返回的byte[]实例,每次实例都不同,数组比较是引用比较,所以,vHashtable.Contains(code)不可能返回true
      

  6.   

    to vwxyzh:
      多谢提醒,测试的结果正如你所述,这个判断是错误的。
      用List、Directory也不能判断出byte[]的内容。
      转成string类型测试后可行。修改后的代码如下:
    using System.Collections;private const int CODE_COUNT = 50000; // 记录数
    private byte[] codeBuffer = new byte[14 * CODE_COUNT];private Random random = new Random(); 
    public byte[] RandomCode()
    {
        byte[] vReturn = new byte[14];
        vReturn[12] = 13;
        vReturn[13] = 10;
        for (int i = 0; i < 4; i++)
            vReturn[i] = (byte)('A' + random.Next(26));
        for (int i = 4; i < 12; i++)
            vReturn[i] = (byte)('0' + random.Next(10));
        return vReturn;
    }private Dictionary<string, int> codeList = new Dictionary<string, int>();private void button1_Click(object sender, EventArgs e)
    {
        long vTickCount = Environment.TickCount;
        codeList.Clear(); 
        byte[] vCode = RandomCode();
        for (int i = 0; i < CODE_COUNT; )
        {
            vCode = RandomCode();
            string S = BitConverter.ToString(vCode);
            if (!codeList.ContainsKey(S)) // 避免重复
            {
                codeList.Add(S, i);
                Array.Copy(vCode, 0, codeBuffer, i * 14, vCode.Length);
                i++;
            }
        }
        FileStream vFileStream = new FileStream(@"c:\temp\temp.txt",
            FileMode.Create, FileAccess.Write);
        vFileStream.Write(codeBuffer, 0, codeBuffer.Length); // 内核会优化
        vFileStream.Close();
        Console.WriteLine("耗时:{0}毫秒", Environment.TickCount - vTickCount);
    }
      

  7.   

    其实有一个更省内存的方法,要求5w个不同的数据,远远小于int的4G的取值范围,所以可以考虑类似GetHashCode的方法,在字典中仅仅记录一个HashCode,这个HashCode通过byte[]的当前值算出(并且保证相当离散),由HashCode和Equal的关系,可以得出如果两个的HashCode不同,那么必定不相等。(即使误杀一两个也没什么大影响)
    这样就可以放弃字典对byte[]或字符串的引用,对固定长度的字符串而言,RandomCode()可以改造为
    void RandomCode(ref byte[] code)
    进一步避免对byte[]的重复实例化,提高性能
      

  8.   

    vCode = RandomCode();
    为什么要执行两次啊?
      

  9.   

    发觉用StringBuilder时不少人不愿意初始化capactiy.. 默认好像是16.. 如果稍微长一些也需要线性的时间分配新空间, 然后复制..
    最上面的code+= 也可以考虑改用StringBuilder. 并初始化一定的capacity. 
      

  10.   

    在zswang代码的基础上,稍微修改了一下:
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Diagnostics;namespace ConsoleApplication5
    {
        class Program
        {
            private const int CODE_COUNT = 50000; // 记录数
            private static readonly byte[] NewLine ={ 13, 10 };
            private static Random random = new Random();        static void Main(string[] args)
            {
                Console.Write("Press Enter to start:");
                Console.ReadLine();
                Stopwatch sw = Stopwatch.StartNew();
                using (FileStream fs = new FileStream("test.txt", FileMode.Create, FileAccess.Write))
                using (BinaryWriter bw = new BinaryWriter(fs))
                    Exec(bw);
                sw.Stop();
                Console.WriteLine("耗时:{0}毫秒", sw.ElapsedMilliseconds);
                Console.ReadLine();
            }        public static void RandomCode(ref byte[] buffer)
            {
                for (int i = 0; i < 4; i++)
                    buffer[i] = (byte)('A' + random.Next(26));
                for (int i = 4; i < 12; i++)
                    buffer[i] = (byte)('0' + random.Next(10));
            }        private static void Exec(BinaryWriter bw)
            {
                Dictionary<int, int> codes = new Dictionary<int, int>(CODE_COUNT);
                byte[] buffer = new byte[12];
                int count = 0;
                while (count < CODE_COUNT)
                {
                    RandomCode(ref buffer);
                    int hash = Hash(buffer);
                    if (!codes.ContainsKey(hash))
                    {
                        codes.Add(hash, 0);
                        bw.Write(buffer);
                        bw.Write(NewLine);
                        count++;
                    }
                }
            }        private static int Hash(byte[] buffer)
            {
                return BitConverter.ToInt32(buffer, 0) ^
                    BitConverter.ToInt32(buffer, 4) ^
                    BitConverter.ToInt32(buffer, 8);
            }    }
    }
      

  11.   

    楼主,你把string变成StringBuilder
    string+ 写成  StringBuilder.Append("").....性能最少优化5倍!
      

  12.   


    using System.Collections;private const int CODE_COUNT = 50000; // 记录数private Random random = new Random(); 
    public byte[] RandomCode()
    {
        byte[] vReturn = new byte[14];
        vReturn[12] = 13;
        vReturn[13] = 10;
        for (int i = 0; i < 4; i++)
            vReturn[i] = (byte)('A' + random.Next(26));
        for (int i = 4; i < 12; i++)
            vReturn[i] = (byte)('0' + random.Next(10));
        return vReturn;
    }private Dictionary<string, int> codeList = new Dictionary<string, int>();private void button1_Click(object sender, EventArgs e)
    {
        long vTickCount = Environment.TickCount;
        codeList.Clear(); 
        byte[] vCode;
        FileStream vFileStream = new FileStream(@"c:\temp\temp.txt",FileMode.Create, FileAccess.Write);
        for (int i = 0; i < CODE_COUNT; )
        {
            vCode = RandomCode();
            string S = BitConverter.ToString(vCode);
            if (!codeList.ContainsKey(S)) // 避免重复
            {
                codeList.Add(S, i);i++;
                vFileStream.Write(codeBuffer, 0, codeBuffer.Length); //这样更快,因为本身有循环
            }
        }
        vFileStream.Close();
        Console.WriteLine("耗时:{0}毫秒", Environment.TickCount - vTickCount);
    }
      

  13.   

    测试了下xx22nn说的方法,确实快些
    看来FileStream本身也有内存缓冲机制
    不过楼主好像失踪了。。
      

  14.   

    我以前写过一个随机a-z组成一条1w个字符串  直接死机!
      

  15.   


    vwxyzh 
    的方法确实比较快
      

  16.   

    不好意思啊,这两天事情太多,去开发其他WEB程序去了,这个C#程序一样要关注
    非常感谢各位的参与,以及几位提出解决方法的朋友~~