Skip to content

Commit 96919e6

Browse files
committed
v0.28
1 parent 6833237 commit 96919e6

8 files changed

+218
-5
lines changed

LineReader.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
#include <wchar.h> // for size_t
1010

1111

12+
//////////////////////////////////////////////////////////////////////////
13+
14+
// Implementation with 2 buffers and async calls to ReadFile()
1215
class CSyncLineReader
1316
{
1417
public:
@@ -31,6 +34,7 @@ class CSyncLineReader
3134

3235
//////////////////////////////////////////////////////////////////////////
3336

37+
// Implementation with usual sync calls to ReadFile()
3438
class CAsyncLineReader
3539
{
3640
public:
@@ -55,6 +59,7 @@ class CAsyncLineReader
5559

5660
//////////////////////////////////////////////////////////////////////////
5761

62+
// Implementation with file mapped to a big single readonly memory block
5863
class CMappingLineReader
5964
{
6065
public:
@@ -73,6 +78,7 @@ class CMappingLineReader
7378

7479
//////////////////////////////////////////////////////////////////////////
7580

81+
// Implementation with 2 buffers and separate thread for sync calls to ReadFile(); synchronization is done without Windows EVENTs (lock free)
7682
class CLockFreeLineReader
7783
{
7884
public:

LogReader.vcxproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,9 @@
184184
<ClInclude Include="CharBuffer.h" />
185185
<ClInclude Include="ScanFile.h" />
186186
</ItemGroup>
187+
<ItemGroup>
188+
<None Include="README.md" />
189+
</ItemGroup>
187190
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
188191
<ImportGroup Label="ExtensionTargets">
189192
</ImportGroup>

LogReader.vcxproj.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,7 @@
4747
<Filter>Header Files</Filter>
4848
</ClInclude>
4949
</ItemGroup>
50+
<ItemGroup>
51+
<None Include="README.md" />
52+
</ItemGroup>
5053
</Project>

README.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
Но профайлер показал перераспределение времени в сторону алгоритма матча. Очень странно, что он замедлился. Я так и не понял почему так.
2626
Я убеждён, что эти 40% можно было сжать до максимум 5% за счет параллелизации вычитки данных и их обработки.
2727
Возможно это как-то связано с тем, что данные лежат в файловом кэше, а не читаются с диска (более короткий путь IRP).
28-
Возможно это банальное копирование памяти в kernel mode плохо параллелизуется. Может стоило переписать, чтобы чтение с диска в выделенном потоке выполнялось...
28+
Возможно это банальное копирование памяти в kernel mode плохо параллелизуется.
29+
Может стоило переписать, чтобы чтение с диска в выделенном потоке выполнялось...
2930
Если у вас есть идеи почему так вышло или в чём ошибка, то буду рад если поделитесь.
3031

3132
Также заметил вставку перед циклом команды nop при оптимизации по скорости компилятором.
@@ -55,19 +56,27 @@
5556

5657
В последней версии grep показывает 2.5 секунды, а моё решение - 1.6 секунды на тестовых данных.
5758

59+
**ДОПОЛНЕНО:**
60+
Реализовал также чтение файла в отдельном потоке. Файловая операция синхронная, синхронизация с потоком lock free (spinlock).
61+
Получил общий выигрыш в 25% относительно решения на синхронном и асинхронном API (1.2 секунды). Итого в 2 раза быстрее чем grep.
62+
5863
## Особенности реализации
5964

6065
В итоге у меня остались все три реализации чтения файлов. Я оставил асинхронную версию как самую перспективную. Переключаются так:
6166

6267
```
63-
#if 1
68+
#if 0
6469
#if 0
6570
CSyncLineReader _lineReader;
6671
#else
67-
CAsyncLineReader _lineReader;
72+
CMappingLineReader _lineReader;
6873
#endif
6974
#else
70-
CMappingLineReader _lineReader;
75+
#if 0
76+
CAsyncLineReader _lineReader;
77+
#else
78+
CLockFreeLineReader _lineReader;
79+
#endif
7180
#endif
7281
```
7382

@@ -76,5 +85,8 @@
7685
Как и в условии задачи, основное консольное приложение собрано с отключенными исключениями и заодно без RTTI.
7786

7887
Я притянул часть STL на мой страх и риск. Ту часть, которая не требует исключений и работает без лишних накладных расходов.
88+
Не вижу смысла не пользоваться дешевыми абстракциями, позволяющими писать более чистый код
89+
и более защищенный от ошибок программиста. И без велосипедов.
90+
Я имею в виду всякие std::unique_ptr, std::string_view, std::optional, std::atomic.
7991

8092
---

TestLineReaderAsync.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
namespace
1212
{
1313
# define CLineReader CAsyncLineReader
14-
# define CLineReader CLockFreeLineReader
1514

1615
const size_t MaxLogLineLength = 1024; // copy-pasted value from LineReader.cpp
1716
}

TestLineReaderLockFree.cpp

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
#include "LineReader.h"
2+
3+
#include "TestHelpers.h"
4+
5+
#include <algorithm>
6+
#include <string>
7+
8+
#include "gtest/gtest.h"
9+
10+
11+
namespace
12+
{
13+
# define CLineReader CLockFreeLineReader
14+
15+
const size_t MaxLogLineLength = 1024; // copy-pasted value from LineReader.cpp
16+
}
17+
18+
19+
TEST(CLineReader, Open)
20+
{
21+
TempFile file("");
22+
CLineReader reader;
23+
EXPECT_TRUE(reader.Open(file.GetFilename().c_str()));
24+
reader.Close();
25+
}
26+
27+
TEST(CLineReader, MissedOpen)
28+
{
29+
CLineReader reader;
30+
const auto line = reader.GetNextLine();
31+
EXPECT_FALSE(line);
32+
}
33+
34+
TEST(CLineReader, EmptyFile)
35+
{
36+
TempFile file("");
37+
CLineReader reader;
38+
EXPECT_TRUE(reader.Open(file.GetFilename().c_str()));
39+
const auto line = reader.GetNextLine();
40+
EXPECT_FALSE(line);
41+
}
42+
43+
TEST(CLineReader, OneLineNoLF)
44+
{
45+
TempFile file("ABCD");
46+
CLineReader reader;
47+
EXPECT_TRUE(reader.Open(file.GetFilename().c_str()));
48+
auto line = reader.GetNextLine();
49+
ASSERT_TRUE(line);
50+
EXPECT_EQ(std::string(*line), "ABCD");
51+
line = reader.GetNextLine();
52+
EXPECT_FALSE(line);
53+
}
54+
55+
TEST(CLineReader, OneLineCRLF)
56+
{
57+
TempFile file("ABCD\r\n");
58+
CLineReader reader;
59+
EXPECT_TRUE(reader.Open(file.GetFilename().c_str()));
60+
auto line = reader.GetNextLine();
61+
ASSERT_TRUE(line);
62+
EXPECT_EQ(std::string(*line), "ABCD\r\n");
63+
line = reader.GetNextLine();
64+
EXPECT_FALSE(line);
65+
}
66+
67+
TEST(CLineReader, OneLineLF)
68+
{
69+
TempFile file("ABCD\n");
70+
CLineReader reader;
71+
EXPECT_TRUE(reader.Open(file.GetFilename().c_str()));
72+
auto line = reader.GetNextLine();
73+
ASSERT_TRUE(line);
74+
EXPECT_EQ(std::string(*line), "ABCD\n");
75+
line = reader.GetNextLine();
76+
EXPECT_FALSE(line);
77+
}
78+
79+
TEST(CLineReader, TwoLinesLF_NoLF)
80+
{
81+
TempFile file("abc\nDEFG");
82+
CLineReader reader;
83+
EXPECT_TRUE(reader.Open(file.GetFilename().c_str()));
84+
auto line = reader.GetNextLine();
85+
ASSERT_TRUE(line);
86+
EXPECT_EQ(std::string(*line), "abc\n");
87+
line = reader.GetNextLine();
88+
ASSERT_TRUE(line);
89+
EXPECT_EQ(std::string(*line), "DEFG");
90+
line = reader.GetNextLine();
91+
EXPECT_FALSE(line);
92+
}
93+
94+
TEST(CLineReader, TwoLinesLF_LF)
95+
{
96+
TempFile file("abc\nDEFG\n");
97+
CLineReader reader;
98+
EXPECT_TRUE(reader.Open(file.GetFilename().c_str()));
99+
auto line = reader.GetNextLine();
100+
ASSERT_TRUE(line);
101+
EXPECT_EQ(std::string(*line), "abc\n");
102+
line = reader.GetNextLine();
103+
ASSERT_TRUE(line);
104+
EXPECT_EQ(std::string(*line), "DEFG\n");
105+
line = reader.GetNextLine();
106+
EXPECT_FALSE(line);
107+
}
108+
109+
TEST(CLineReader, EmptyLines)
110+
{
111+
TempFile file("\n\n");
112+
CLineReader reader;
113+
EXPECT_TRUE(reader.Open(file.GetFilename().c_str()));
114+
auto line = reader.GetNextLine();
115+
ASSERT_TRUE(line);
116+
EXPECT_EQ(std::string(*line), "\n");
117+
line = reader.GetNextLine();
118+
ASSERT_TRUE(line);
119+
EXPECT_EQ(std::string(*line), "\n");
120+
line = reader.GetNextLine();
121+
EXPECT_FALSE(line);
122+
}
123+
124+
TEST(CLineReader, ThreeLines)
125+
{
126+
TempFile file("Abcdef\n\n3rd Line");
127+
CLineReader reader;
128+
EXPECT_TRUE(reader.Open(file.GetFilename().c_str()));
129+
auto line = reader.GetNextLine();
130+
ASSERT_TRUE(line);
131+
EXPECT_EQ(std::string(*line), "Abcdef\n");
132+
line = reader.GetNextLine();
133+
ASSERT_TRUE(line);
134+
EXPECT_EQ(std::string(*line), "\n");
135+
line = reader.GetNextLine();
136+
ASSERT_TRUE(line);
137+
EXPECT_EQ(std::string(*line), "3rd Line");
138+
line = reader.GetNextLine();
139+
EXPECT_FALSE(line);
140+
}
141+
142+
TEST(CLineReader, LineMaxLength_1)
143+
{
144+
const std::string str = std::string(MaxLogLineLength, 'x');
145+
TempFile file(str);
146+
CLineReader reader;
147+
EXPECT_TRUE(reader.Open(file.GetFilename().c_str()));
148+
auto line = reader.GetNextLine();
149+
ASSERT_TRUE(line);
150+
EXPECT_EQ(*line, str);
151+
line = reader.GetNextLine();
152+
EXPECT_FALSE(line);
153+
}
154+
155+
TEST(CLineReader, LineMaxLength_2)
156+
{
157+
const std::string str = std::string(MaxLogLineLength - 1, 'x');
158+
TempFile file(str + "\n");
159+
CLineReader reader;
160+
EXPECT_TRUE(reader.Open(file.GetFilename().c_str()));
161+
auto line = reader.GetNextLine();
162+
ASSERT_TRUE(line);
163+
EXPECT_EQ(*line, str + "\n");
164+
line = reader.GetNextLine();
165+
EXPECT_FALSE(line);
166+
}
167+
168+
TEST(CLineReader, LineTooLong_1)
169+
{
170+
const std::string str = std::string(MaxLogLineLength + 1, 'x');
171+
TempFile file(str);
172+
CLineReader reader;
173+
EXPECT_TRUE(reader.Open(file.GetFilename().c_str()));
174+
auto line = reader.GetNextLine();
175+
EXPECT_FALSE(line);
176+
}
177+
178+
TEST(CLineReader, LineTooLong_2)
179+
{
180+
const std::string str = std::string(MaxLogLineLength, 'x');
181+
TempFile file(str + "\n");
182+
CLineReader reader;
183+
EXPECT_TRUE(reader.Open(file.GetFilename().c_str()));
184+
auto line = reader.GetNextLine();
185+
EXPECT_FALSE(line);
186+
}

tests.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
<ClCompile Include="TestFnMatch.cpp" />
4747
<ClCompile Include="TestHelpers.cpp" />
4848
<ClCompile Include="TestLineReaderAsync.cpp" />
49+
<ClCompile Include="TestLineReaderLockFree.cpp" />
4950
<ClCompile Include="TestLineReaderMapping.cpp" />
5051
<ClCompile Include="TestLineReaderSync.cpp" />
5152
</ItemGroup>

tests.vcxproj.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,5 +106,8 @@
106106
<ClCompile Include="TestLineReaderMapping.cpp">
107107
<Filter>Tests</Filter>
108108
</ClCompile>
109+
<ClCompile Include="TestLineReaderLockFree.cpp">
110+
<Filter>Tests</Filter>
111+
</ClCompile>
109112
</ItemGroup>
110113
</Project>

0 commit comments

Comments
 (0)