Skip to content

Commit e712f3c

Browse files
committed
Optimize Elf read/write
1 parent 5820425 commit e712f3c

13 files changed

+324
-83
lines changed

Diff for: src/Directory.Packages.props

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<ItemGroup>
77
<PackageVersion Include="MinVer" Version="6.0.0" />
88
<PackageVersion Include="MSTest" Version="3.6.0" />
9+
<PackageVersion Include="SuperluminalPerf" Version="1.3.0" />
910
<PackageVersion Include="System.Text.Encoding.CodePages" Version="8.0.0" />
1011
<PackageVersion Include="Verify.DiffPlex" Version="3.1.0" />
1112
<PackageVersion Include="Verify.MSTest" Version="26.6.0" />

Diff for: src/LibObjectFile.Bench/LibObjectFile.Bench.csproj

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
<IsPackable>false</IsPackable>
99
</PropertyGroup>
1010

11+
<ItemGroup>
12+
<PackageReference Include="SuperluminalPerf" />
13+
</ItemGroup>
14+
1115
<ItemGroup>
1216
<ProjectReference Include="..\LibObjectFile\LibObjectFile.csproj" />
1317
</ItemGroup>

Diff for: src/LibObjectFile.Bench/Program.cs

+33-5
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,46 @@ internal class Program
1111
{
1212
static void Main(string[] args)
1313
{
14+
Console.WriteLine("Loading files into memory");
1415
var clock = Stopwatch.StartNew();
15-
//var memoryStream = new MemoryStream();
16+
var streams = new List<MemoryStream>();
17+
int biggestCapacity = 0;
1618
foreach (var file in GetLinuxBins())
1719
{
18-
//memoryStream.SetLength(0);
1920
using var stream = File.OpenRead((string)file[0]);
20-
//stream.CopyTo(memoryStream);
21-
2221
if (ElfFile.IsElf(stream))
2322
{
24-
ElfFile.Read(stream);
23+
stream.Position = 0;
24+
var localStream = new MemoryStream((int)stream.Length);
25+
stream.CopyTo(localStream);
26+
localStream.Position = 0;
27+
streams.Add(localStream);
28+
if (localStream.Capacity > biggestCapacity)
29+
{
30+
biggestCapacity = localStream.Capacity;
31+
}
32+
}
33+
}
34+
35+
clock.Stop();
36+
Console.WriteLine($"End reading in {clock.Elapsed.TotalMilliseconds}ms");
37+
Console.ReadLine();
38+
39+
Console.WriteLine("Processing");
40+
var memoryStream = new MemoryStream(biggestCapacity);
41+
clock.Restart();
42+
//SuperluminalPerf.Initialize();
43+
for (int i = 0; i < 10; i++)
44+
{
45+
//SuperluminalPerf.BeginEvent($"Round{i}");
46+
foreach (var stream in streams)
47+
{
48+
stream.Position = 0;
49+
var elf = ElfFile.Read(stream);
50+
memoryStream.SetLength(0);
51+
elf.Write(memoryStream);
2552
}
53+
//SuperluminalPerf.EndEvent();
2654
}
2755
clock.Stop();
2856
Console.WriteLine($"{clock.Elapsed.TotalMilliseconds}ms");
+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright (c) Alexandre Mutel. All rights reserved.
2+
// This file is licensed under the BSD-Clause 2 license.
3+
// See the license.txt file in the project root for more information.
4+
5+
using System;
6+
using System.IO;
7+
using System.Linq;
8+
using System.Runtime.InteropServices;
9+
using LibObjectFile.IO;
10+
11+
namespace LibObjectFile.Tests.IO;
12+
13+
/// <summary>
14+
/// Tests for <see cref="BatchDataReader{T}"/> and <see cref="BatchDataWriter{T}"/>.
15+
/// </summary>
16+
[TestClass]
17+
public class TestBatchDataReaderWriter
18+
{
19+
[DataTestMethod]
20+
[DataRow(0)]
21+
[DataRow(1)]
22+
[DataRow(100)]
23+
[DataRow(1000)]
24+
[DataRow(1024)]
25+
[DataRow(1025)]
26+
public void TestRead(int count)
27+
{
28+
var stream = new MemoryStream();
29+
stream.Write(MemoryMarshal.AsBytes(Enumerable.Range(0, count).ToArray().AsSpan()));
30+
stream.Position = 0;
31+
32+
using var reader = new BatchDataReader<int>(stream, count);
33+
int i = 0;
34+
while (reader.HasNext())
35+
{
36+
Assert.AreEqual(i, reader.Read(), $"Invalid value at index {i}");
37+
i++;
38+
}
39+
Assert.AreEqual(count, i);
40+
}
41+
42+
[DataTestMethod]
43+
[DataRow(0)]
44+
[DataRow(1)]
45+
[DataRow(100)]
46+
[DataRow(1000)]
47+
[DataRow(1024)]
48+
[DataRow(1025)]
49+
public void TestWrite(int count)
50+
{
51+
var stream = new MemoryStream();
52+
int i = 0;
53+
{
54+
using var writer = new BatchDataWriter<int>(stream, count);
55+
{
56+
while (writer.HasNext())
57+
{
58+
writer.Write(i);
59+
i++;
60+
}
61+
}
62+
}
63+
Assert.AreEqual(count * sizeof(int), stream.Length);
64+
65+
stream.Position = 0;
66+
using var reader = new BatchDataReader<int>(stream, count);
67+
i = 0;
68+
while (reader.HasNext())
69+
{
70+
Assert.AreEqual(i, reader.Read(), $"Invalid value at index {i}");
71+
i++;
72+
}
73+
Assert.AreEqual(count, i);
74+
}
75+
}

Diff for: src/LibObjectFile/Elf/ElfFile.Read.cs

+18-2
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,28 @@ private unsafe void VerifyAndFixProgramHeadersAndSections(ElfReader reader)
8484
}
8585

8686
// Connect section Link instance
87-
section.Link = reader.ResolveLink(section.Link, $"Invalid section Link [{{0}}] for section [{i}]");
87+
var link = section.Link;
88+
if (!reader.TryResolveLink(ref link))
89+
{
90+
reader.Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidResolvedLink, $"Invalid section Link [{link.SpecialIndex}] for section [{i}]");
91+
}
92+
else
93+
{
94+
section.Link = link;
95+
}
8896

8997
// Connect section Info instance
9098
if (section.Type != ElfSectionType.DynamicLinkerSymbolTable && section.Type != ElfSectionType.SymbolTable && (section.Flags & ElfSectionFlags.InfoLink) != 0)
9199
{
92-
section.Info = reader.ResolveLink(section.Info, $"Invalid section Info [{{0}}] for section [{i}]");
100+
link = section.Info;
101+
if (!reader.TryResolveLink(ref link))
102+
{
103+
reader.Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidResolvedLink, $"Invalid section Info [{link.SpecialIndex}] for section [{i}]");
104+
}
105+
else
106+
{
107+
section.Info = link;
108+
}
93109
}
94110

95111
if (section != SectionHeaderStringTable && section.HasContent)

Diff for: src/LibObjectFile/Elf/ElfReader.cs

+5-11
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,18 @@ private protected ElfReader(ElfFile file, Stream stream, ElfReaderOptions reader
3030

3131
public override bool KeepOriginalStreamForSubStreams => Options.UseSubStream;
3232

33-
public ElfSectionLink ResolveLink(ElfSectionLink link, string errorMessageFormat)
33+
public bool TryResolveLink(ref ElfSectionLink link)
3434
{
35-
ArgumentNullException.ThrowIfNull(errorMessageFormat);
36-
37-
// Connect section Link instance
3835
if (!link.IsEmpty)
3936
{
4037
if (link.SpecialIndex >= File.Sections.Count)
4138
{
42-
Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidResolvedLink, string.Format(errorMessageFormat, link.SpecialIndex));
43-
}
44-
else
45-
{
46-
link = new ElfSectionLink(File.Sections[link.SpecialIndex]);
39+
return false;
4740
}
48-
}
4941

50-
return link;
42+
link = new ElfSectionLink(File.Sections[link.SpecialIndex]);
43+
}
44+
return true;
5145
}
5246

5347
internal static ElfReader Create(ElfFile file, Stream stream, ElfReaderOptions options)

Diff for: src/LibObjectFile/Elf/ElfSectionLink.cs

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ namespace LibObjectFile.Elf;
2121

2222
public ElfSectionLink(int index)
2323
{
24+
ArgumentOutOfRangeException.ThrowIfNegative(index);
2425
Section = null;
2526
SpecialIndex = index;
2627
}

Diff for: src/LibObjectFile/Elf/Sections/ElfRelocationTable.cs

+30-20
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ private unsafe void Read32(ElfReader reader)
7676
ref var entry = ref MemoryMarshal.GetReference(span);
7777
while (batch.HasNext())
7878
{
79-
ref var rel = ref batch.ReadNext();
79+
ref var rel = ref batch.Read();
8080
entry.Offset = reader.Decode(rel.r_offset);
8181
var r_info = reader.Decode(rel.r_info);
8282
entry.Type = new ElfRelocationType(Parent!.Arch, r_info & 0xFF);
@@ -91,7 +91,7 @@ private unsafe void Read32(ElfReader reader)
9191
ref var entry = ref MemoryMarshal.GetReference(span);
9292
while (batch.HasNext())
9393
{
94-
ref var rel = ref batch.ReadNext();
94+
ref var rel = ref batch.Read();
9595
entry.Offset = reader.Decode(rel.r_offset);
9696
var r_info = reader.Decode(rel.r_info);
9797
entry.Type = new ElfRelocationType(Parent!.Arch, r_info & 0xFF);
@@ -115,7 +115,7 @@ private unsafe void Read64(ElfReader reader)
115115
ref var entry = ref MemoryMarshal.GetReference(span);
116116
while (batch.HasNext())
117117
{
118-
ref var rel = ref batch.ReadNext();
118+
ref var rel = ref batch.Read();
119119
entry.Offset = reader.Decode(rel.r_offset);
120120
var r_info = reader.Decode(rel.r_info);
121121
entry.Type = new ElfRelocationType(Parent!.Arch, (uint)(r_info & 0xFFFFFFFF));
@@ -130,7 +130,7 @@ private unsafe void Read64(ElfReader reader)
130130
ref var entry = ref MemoryMarshal.GetReference(span);
131131
while (batch.HasNext())
132132
{
133-
ref var rel = ref batch.ReadNext();
133+
ref var rel = ref batch.Read();
134134
entry.Offset = reader.Decode(rel.r_offset);
135135
var r_info = reader.Decode(rel.r_info);
136136
entry.Type = new ElfRelocationType(Parent!.Arch, (uint)(r_info & 0xFFFFFFFF));
@@ -143,66 +143,76 @@ private unsafe void Read64(ElfReader reader)
143143

144144
private void Write32(ElfWriter writer)
145145
{
146+
var entries = CollectionsMarshal.AsSpan(_entries);
146147
if (IsRelocationWithAddends)
147148
{
149+
using var batch = new BatchDataWriter<ElfNative.Elf32_Rela>(writer.Stream, entries.Length);
148150
// Write all entries
149-
for (int i = 0; i < Entries.Count; i++)
151+
var rel = new ElfNative.Elf32_Rela();
152+
for (int i = 0; i < entries.Length; i++)
150153
{
151-
var entry = Entries[i];
154+
ref var entry = ref entries[i];
152155

153-
var rel = new ElfNative.Elf32_Rela();
154156
writer.Encode(out rel.r_offset, (uint)entry.Offset);
155157
uint r_info = entry.Info32;
156158
writer.Encode(out rel.r_info, r_info);
157159
writer.Encode(out rel.r_addend, (int)entry.Addend);
158-
writer.Write(rel);
160+
161+
batch.Write(rel);
159162
}
160163
}
161164
else
162165
{
166+
using var batch = new BatchDataWriter<ElfNative.Elf32_Rel>(writer.Stream, entries.Length);
163167
// Write all entries
164-
for (int i = 0; i < Entries.Count; i++)
168+
var rel = new ElfNative.Elf32_Rel();
169+
for (int i = 0; i < entries.Length; i++)
165170
{
166-
var entry = Entries[i];
171+
ref var entry = ref entries[i];
167172

168-
var rel = new ElfNative.Elf32_Rel();
169173
writer.Encode(out rel.r_offset, (uint)entry.Offset);
170174
uint r_info = entry.Info32;
171175
writer.Encode(out rel.r_info, r_info);
172-
writer.Write(rel);
176+
177+
batch.Write(rel);
173178
}
174179
}
175180
}
176181

177182
private void Write64(ElfWriter writer)
178183
{
184+
var entries = CollectionsMarshal.AsSpan(_entries);
179185
if (IsRelocationWithAddends)
180186
{
187+
using var batch = new BatchDataWriter<ElfNative.Elf64_Rela>(writer.Stream, entries.Length);
188+
var rel = new ElfNative.Elf64_Rela();
181189
// Write all entries
182-
for (int i = 0; i < Entries.Count; i++)
190+
for (int i = 0; i < entries.Length; i++)
183191
{
184-
var entry = Entries[i];
192+
ref var entry = ref entries[i];
185193

186-
var rel = new ElfNative.Elf64_Rela();
187194
writer.Encode(out rel.r_offset, entry.Offset);
188195
ulong r_info = entry.Info64;
189196
writer.Encode(out rel.r_info, r_info);
190197
writer.Encode(out rel.r_addend, entry.Addend);
191-
writer.Write(rel);
198+
199+
batch.Write(rel);
192200
}
193201
}
194202
else
195203
{
204+
using var batch = new BatchDataWriter<ElfNative.Elf64_Rel>(writer.Stream, entries.Length);
205+
var rel = new ElfNative.Elf64_Rel();
196206
// Write all entries
197-
for (int i = 0; i < Entries.Count; i++)
207+
for (int i = 0; i < entries.Length; i++)
198208
{
199-
var entry = Entries[i];
209+
ref var entry = ref entries[i];
200210

201-
var rel = new ElfNative.Elf64_Rel();
202211
writer.Encode(out rel.r_offset, (uint)entry.Offset);
203212
ulong r_info = entry.Info64;
204213
writer.Encode(out rel.r_info, r_info);
205-
writer.Write(rel);
214+
215+
batch.Write(rel);
206216
}
207217
}
208218
}

0 commit comments

Comments
 (0)