태그 보관물: IIS

Java vs C# Serialize 비교와 C# DataSet을 쓰지 말아야 되는 이유(?)

며칠전에 사석에서 상사분이 톰캣(Tomcat)은 IIS에 비해서 connection을 적게 받고, 그로 인해서 성능이 떨어진다고 말씀을 하시네요. ^^;; 서비스의 전체적인 성능 문제를 자바(Java)나 톰캣이 문제가 있다는 시각이어서, 자바쪽의 경험치로 말씀하시는게 아니라 선입견(Overlapped I/O의 성능만 말씀하시네 ^^;;)으로 얘기를 하고 있다는 느낌이 많이 받았습니다. 대체로, 서비스의 성능 이슈는 톰캣이나 IIS보다 그 위에 올라가는 애플리케이션이 더 성능에 영향을 미칠텐데 말입니다. ^^;;

그래서 IIS 기반의 ASP.NET 서비스들이 주로 사용하는 DataSet에 대해서 Serialize 데이타에 대해서 살펴보았다. 비교는 단순히 자바, C# Object, C# DataSet으로 10개의 리스트를 가지는 모델을 가지고 했다. 아래의 코드로 자바에서의 Object Array, C#에서의 Object Array, C#에서의 DataSet의 Serialize된 객체의 사이즈를 알 수 있다. 단순하게 Serialize하는 속도 및 Deserialize하는 속도는 같다고 가정을 한다.

* Java
– SerializeModel.java

import java.util.ArrayList;
import java.io.Serializable;
public class SerializeModel implements Serializable {
  private static final long serialVersionUID = -7168303693593724718L;
  private int count = 0;
  private String name = null;
  private String address = null;
  private ArrayList<SerializeModel> models = new ArrayList<SerializeModel>();

  public SerializeModel(int count, String name, String address) {
    this.count = count;
    this.name = name;
    this.address = address;
  }

  public void addModel(SerializeModel model) {
    this.models.add(model);
  }
}

– SerializeTest.java

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

public class SerializeTest {
 
  public static void main(String[] args) throws Exception {
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("java.ser"));
    SerializeModel model = new SerializeModel(0, "a", "b");
    
    for(int i=0; i<10; i++) {
      model.addModel(new SerializeModel(i, i +" name", i +" address"));
    }

    out.writeObject(model);
    out.flush();
    out.close();
  }
}

* C#

– SerializeModel.cs

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Collections;
using System.Collections.Generic;
namespace SerialzieTest
{
  [Serializable()]
  class SerializeModel
  {
    private int count = 0;
    private String name = null;
    private String address = null;
    private ArrayList models = new ArrayList();
    public SerializeModel(int count, String name, String address) 
    {
      this.count = count;
      this.name = name;
      this.address = address;
    }

    public void AddModel(SerializeModel model) 
    {
      this.models.Add(model);
    }
  }
}

– SerializeModelDataSet.cs

using System;
using System.Data;
using System.Collections.Generic;
using System.Text;

namespace SerialzieTest
{
  [Serializable]
  class SerializeModelDataSet
  {
    private DataSet dataSet = new DataSet();
    public void AddDataTable(DataTable table) 
    {
      this.dataSet.Tables.Add(table);
    }

    public DataSet GetDataSet()
    {
      return this.dataSet;
    }
  }
}

– Program.cs

using System;
using System.Data;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace SerialzieTest
{
  class Program
  {
    static void SerializeObjectArray()
    {
      Stream stream = File.Open("csharp.ser", FileMode.Create);
      BinaryFormatter bformat = new BinaryFormatter();
      SerializeModel model = new SerializeModel(0, "a", "b");

      for (int i = 0; i &lt; 10; i++)
      {
        model.AddModel(new SerializeModel(i, i + " name", i + " address"));
      }
      
      bformat.Serialize(stream, model);
      stream.Flush();
      stream.Close();
    }

    static void SerializeDataSet()
    {
      Stream stream = File.Open("csharp_dataset.ser", FileMode.Create);
      BinaryFormatter bformat = new BinaryFormatter();
      SerializeModelDataSet model = new SerializeModelDataSet();
      DataTable table = new DataTable();
      DataColumn itemCount = new DataColumn("count", Type.GetType("System.Int32"));
      DataColumn itemName = new DataColumn("name", Type.GetType("System.String"));
      DataColumn itemAddress = new DataColumn("address", Type.GetType("System.String"));
      table.Columns.Add(itemCount);</span>
      table.Columns.Add(itemName);
      table.Columns.Add(itemAddress);

      DataRow row;
      for (int i = 0; i &lt; 10; i++)
      {
        row = table.NewRow();
        row["count"] = i;
        row["name"] = i + " name";
        row["address"] = i + " address";
        table.Rows.Add(row);
      }

      model.AddDataTable(table);
      bformat.Serialize(stream, model);
      stream.Flush();
      stream.Close();
    }

    static void Main(string[] args)
    {
      SerializeObjectArray();
      SerializeDataSet();
    }
  }
}

위 코드를 사용해서 저장한 Object의 사이즈는 아래와 같습니다.

Java(Object ArrayList) : 665 byte
C#(Object ArrayList) : 1035 byte
C#(DataSet) : 3000 byte

네트웍으로 전송되는 데이터의 크기는 성능에 중요한 영향을 준다는 것은 이미 알고 있다. 성능을 높이기 위해서 Socket Buffer의 사이즈를 적당히 줄이는 것도 팁중의 하나이다. 그리고, 위 코드로 만들어지는 C#의 DataSet은 더욱 멋진 모습을 보여준다. 아래의 화면처럼 말이죠.. 아래의 화면은 Hex Viewer로 본 화면이다.

Object의 Serialize는 C#이 version, locale 및 가비지(?) 데이타로 인해서 좀 더 크게 나온다. 그리고, 중간의 DataSet은 Serialize된 내용을 보면, xml의 형태로 데이터를 저장하고 있다. 결국 DataSet을 데이타로 직렬화해서 네트웍으로 전송하게 되면, 데이터 크기보다는 Serialize/Deserialize할때의 String연산(XML 데이타 리드)이 성능저하의 주범이 될 것 같다. 결국, Tomcat, IIS가 중요한게 아니라 C#에서 널리 쓰이는 DataSet을 많이 사용하고 있는 IIS기반의 ASP.NET 어플들의 서비스가 더 문제가 아닐까 생각한다.

Restart IIS from remote computer

Windows의 IIS에 배포를 위해서 xcopy 같은 명령어를 많이 사용하는데, 그런경우 copy가 되는 도중에 IIS가 읽어버리면 계속 cache miss 어쩌구 저쩌구 에러로그가 계속 쌓입니다. 그런 경우 해결책이 웹서버 리스타트 인데요..  iisreset 으로 리스타트를 합니다.  여러대의 서버에 배포를 하고 iis reset을 하기 위해서는 아래는 참고하시면 좋은 팁이 될듯 합니다.^^

파일 카피

restart iis from remote computer

iisreset.exe
\\computer_name

or
sc.exe \\computer_name stop w3svc
sc.exe
\\computer_name start w3svc