Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

C# and Java Comparison: The C# Struct Advantage

DZone 's Guide to

C# and Java Comparison: The C# Struct Advantage

Why C#'s struct still reigns supreme over Java's use of classes.

· Web Dev Zone ·
Free Resource

C# vs. Java and .Net vs. JVM are never-ending wars. Each ecosystem has its own advantages, loyal fans, and success stories. But, I think .NET has a weapon that Java doesn't: C#'s struct.

Java Test Case

I create a naive class with two public fields of type integer.

private static class Coords{
  public int x;
  public int y;

  public Coords(int x, int y) {
    this.x = x;
    this.y = y;
  }
}


Then, I create a test case with JMH.

@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public void testLoopAndGetData(BenchMarkState state, Blackhole bh){
  Coords value;
  for(int i = 0; i < state.SIZE;i++) {
    value = state.data[i];
    bh.consume(value.x);
    bh.consume(value.y);
  }
}

@State(Scope.Benchmark)
public static class BenchMarkState {
  @Setup
  public void init() {
    Random r = new Random();
    for(int i = 0; i < SIZE; i++) {
      int nextInt = r.nextInt();
      data[i] = new Coords(nextInt, nextInt);
    }
  }

  public final int SIZE = 5000000;
  public Coords[] data = new Coords[SIZE];
}


Java takes nearly 20ms for each operation, as seen below:

Benchmark     

Mode

Cnt

Score

Error

Units

TestJavaStruct.testLoopAndGetData

avgt

200

19.787

± 0.152

 ms/op


C# Test Case

I run .NET on Ubuntu 16 with .NET Core and MonoDevelop as my IDE. I create a similar struct with the class version of Java.

    public struct Coords
    {
        public int x, y;

        public Coords(int p1, int p2)
        {
            x = p1;
            y = p2;
        }
    }


Then, I use BenchmarkDotNet to create a test case.

    [CoreJob]
    [RPlotExporter, RankColumn]
    public class BenchmarkStruct
    {
        private Coords[] data;
        public int N = 5000000;

        [GlobalSetup]
        public void Setup()
        {
            Random r= new Random(42);
            data = new Coords[N];
            for (int i = 0; i < N; i++)
            {
                data[i] = new Coords(r.Next(), r.Next());
            }
        }

        [Benchmark]
        public int Loop() {
            int a = 0, b = 0;
            for(int i = 0; i < N; i++) {
                a = data[i].x;
                b = data[i].y;
            }
            return a & b;
        }
    }


We can see that .NET performs very well with struct in this scenario

| Method |     Mean |     Error |    StdDev | Rank |
|------- |---------:|----------:|----------:|-----:|
|   Loop | 3.167 ms | 0.0473 ms | 0.0420 ms |    1 |


A Solution From Java

There is an attempt to "deliver struct types for Java programming language", called Junion. As they described, a struct can save a significant amount of memory when compared with a standard class in Java. It's a very good optimization when you have some arrays with millions of objects. But I was wondering if Junion can provide a solution as fast as a struct in C#.

Declaring a struct with Junion is simple.

@Struct
private static class CoordsStruct{
  public int x;
  public int y;
}


Then, the test case with JMH

@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public void testJunion(BenchMarkState state, Blackhole bh){
  CoordsStruct value;
  for(int i = 0; i < state.SIZE;i++) {
    value = state.dataStruct[i];
    bh.consume(value.x);
    bh.consume(value.y);
  }
}

@State(Scope.Benchmark)
public static class BenchMarkState {
  @Setup
  public void init() {
    Random r = new Random();
    for(int i = 0; i < SIZE; i++) {
      int nextInt = r.nextInt();
      dataStruct[i] = new CoordsStruct();
      dataStruct[i].x = nextInt;
      dataStruct[i].y = nextInt;
    }
  }

  public final int SIZE = 5000000;
  public CoordsStruct[] dataStruct = new CoordsStruct[SIZE];
}


This is no surprise — Junion can save memory, but it cannot boost the processing time.

Benchmark

Mode

Cnt

Score

Error

Units

TestJavaStruct.testJunion

avgt

200

19.970

± 0.236

ms/op

Conclusion

As I understand it, C#'s struct is a ValueType, and it's "stack-allocated or allocated inline in a structure." So, when we declare an array of Struct, .NET can allocate a region of memory to store the array, and all items of the array are consecutive. Consequently, when we iterate on an array of structs, this action takes advantage of the CPU cache line and performs very fast.

Java is a different story; an array of objects stores only a pointer (or reference, the memory address of object). Every access might force JVM to read data from memory (or L3 cache of the CPU). This action isn't "Mechanical Sympathy" and slows down the iteration. You can get more detail about CPU cache line and how it impacts iteration in my previous article.


Disclaimer: I have not used .NET or C# for professional purposes since 2012, and I know a little bit about Java (a small part of the JVM ecosystem). So, please correct me if I'm wrong; I appreciate any comment or correction.

Topics:
jvm ,java ,dotnet ,c# ,performance ,struct

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}