Skip to content

Commit 3c0ec5d

Browse files
committed
Refine expert examples and Go smoke handling
1 parent afe3bde commit 3c0ec5d

11 files changed

Lines changed: 226 additions & 99 deletions

File tree

languages/csharp/04-expert/performance-and-profiling-basics/example/main.cs

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,61 @@
77

88
class Program
99
{
10+
static string retainedText = string.Empty;
11+
static List<int> retainedValues = new List<int>();
12+
1013
static void Main()
1114
{
1215
// Program flow: measure two paired implementations on the same workload size.
1316
const int lineCount = 4000;
14-
long concatTicks = Measure(() => BuildWithConcatenation(lineCount));
15-
long builderTicks = Measure(() => BuildWithStringBuilder(lineCount));
17+
const int repetitions = 12;
18+
long concatTicks = MeasureAverage(
19+
() => retainedText = BuildWithConcatenation(lineCount),
20+
repetitions
21+
);
22+
long builderTicks = MeasureAverage(
23+
() => retainedText = BuildWithStringBuilder(lineCount),
24+
repetitions
25+
);
1626

17-
Console.WriteLine($"String concatenation ticks: {concatTicks}");
18-
Console.WriteLine($"StringBuilder ticks: {builderTicks}");
27+
Console.WriteLine(
28+
$"Average string concatenation ticks ({repetitions} runs): {concatTicks}"
29+
);
30+
Console.WriteLine($"Average StringBuilder ticks ({repetitions} runs): {builderTicks}");
1931

2032
const int itemCount = 200000;
21-
long noCapacityTicks = Measure(() => FillWithoutCapacity(itemCount));
22-
long withCapacityTicks = Measure(() => FillWithCapacity(itemCount));
33+
long noCapacityTicks = MeasureAverage(
34+
() => retainedValues = FillWithoutCapacity(itemCount),
35+
repetitions
36+
);
37+
long withCapacityTicks = MeasureAverage(
38+
() => retainedValues = FillWithCapacity(itemCount),
39+
repetitions
40+
);
2341

2442
// Intent: final output keeps the comparison direct and easy to verify.
25-
Console.WriteLine($"List fill without capacity ticks: {noCapacityTicks}");
26-
Console.WriteLine($"List fill with capacity ticks: {withCapacityTicks}");
43+
Console.WriteLine(
44+
$"Average list fill without capacity ticks ({repetitions} runs): {noCapacityTicks}"
45+
);
46+
Console.WriteLine(
47+
$"Average list fill with capacity ticks ({repetitions} runs): {withCapacityTicks}"
48+
);
2749
}
2850

29-
static long Measure(Action action)
51+
static long MeasureAverage(Action action, int repetitions)
3052
{
31-
Stopwatch stopwatch = Stopwatch.StartNew();
3253
action();
33-
stopwatch.Stop();
34-
return stopwatch.ElapsedTicks;
54+
55+
long totalTicks = 0;
56+
for (int iteration = 0; iteration < repetitions; iteration++)
57+
{
58+
Stopwatch stopwatch = Stopwatch.StartNew();
59+
action();
60+
stopwatch.Stop();
61+
totalTicks += stopwatch.ElapsedTicks;
62+
}
63+
64+
return totalTicks / repetitions;
3565
}
3666

3767
static string BuildWithConcatenation(int lineCount)

languages/csharp/04-expert/smart-pointers-in-depth/example/main.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,6 @@ public Report(string title)
1111
}
1212

1313
public string Title { get; }
14-
15-
~Report()
16-
{
17-
Console.WriteLine($"Finalized report: {Title}");
18-
}
1914
}
2015

2116
sealed class ReportOwner
@@ -74,7 +69,7 @@ class Program
7469
{
7570
static void Main()
7671
{
77-
// Program flow: move one strongly-owned reference, then watch a weak observer expire.
72+
// Program flow: move one strongly-owned reference, then observe a weak reference expire.
7873
ReportOwner inbox = new ReportOwner("Inbox", new Report("Quarterly Summary"));
7974
ReportOwner archive = new ReportOwner("Archive", null);
8075

@@ -87,11 +82,11 @@ static void Main()
8782
PreviewPane preview = CreatePreview();
8883
preview.Describe();
8984

85+
// Intent: the helper method released the last strong owner before we force collection.
9086
GC.Collect();
9187
GC.WaitForPendingFinalizers();
9288
GC.Collect();
9389

94-
// Intent: final state confirms that the weak observer did not keep the object alive.
9590
preview.Describe();
9691
}
9792

languages/go/04-expert/modularization-and-build-structure/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ This module introduces separation of responsibilities and package-oriented desig
55
## Quick Run
66

77
~~~bash
8-
go run example/main.go
8+
go run example/main.go example/pricing.go example/formatting.go
99
~~~
1010

1111
## Topics Covered
1212

13-
- Separating calculation, formatting, and orchestration responsibilities.
13+
- Separating calculation, formatting, and orchestration responsibilities across files.
1414
- Keeping `main` thin and coordination-focused.
15-
- Designing helper functions and types that could later move into packages.
15+
- Designing helper functions and types that live outside the entrypoint.
1616
- Treating file and package layout as part of program design.
1717

1818
## Common Pitfalls
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package main
2+
3+
import "fmt"
4+
5+
func renderSummary(summary invoiceSummary) string {
6+
return fmt.Sprintf(
7+
"Subtotal: %.2f\nDiscount: %.2f\nTax: %.2f\nTotal: %.2f",
8+
summary.subtotal,
9+
summary.discountValue,
10+
summary.taxValue,
11+
summary.total,
12+
)
13+
}

languages/go/04-expert/modularization-and-build-structure/example/main.go

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,45 +4,6 @@ package main
44

55
import "fmt"
66

7-
type lineItem struct {
8-
name string
9-
quantity int
10-
unitPrice float64
11-
}
12-
13-
type invoiceSummary struct {
14-
subtotal float64
15-
discountValue float64
16-
taxValue float64
17-
total float64
18-
}
19-
20-
func buildSummary(items []lineItem, discountPercent, taxPercent float64) invoiceSummary {
21-
subtotal := 0.0
22-
for _, item := range items {
23-
subtotal += float64(item.quantity) * item.unitPrice
24-
}
25-
discountValue := subtotal * (discountPercent / 100.0)
26-
taxedBase := subtotal - discountValue
27-
taxValue := taxedBase * (taxPercent / 100.0)
28-
return invoiceSummary{
29-
subtotal: subtotal,
30-
discountValue: discountValue,
31-
taxValue: taxValue,
32-
total: taxedBase + taxValue,
33-
}
34-
}
35-
36-
func renderSummary(summary invoiceSummary) string {
37-
return fmt.Sprintf(
38-
"Subtotal: %.2f\nDiscount: %.2f\nTax: %.2f\nTotal: %.2f",
39-
summary.subtotal,
40-
summary.discountValue,
41-
summary.taxValue,
42-
summary.total,
43-
)
44-
}
45-
467
func main() {
478
// Program flow: define input data, delegate calculations, then format a report.
489
items := []lineItem{
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package main
2+
3+
type lineItem struct {
4+
name string
5+
quantity int
6+
unitPrice float64
7+
}
8+
9+
type invoiceSummary struct {
10+
subtotal float64
11+
discountValue float64
12+
taxValue float64
13+
total float64
14+
}
15+
16+
func buildSummary(items []lineItem, discountPercent, taxPercent float64) invoiceSummary {
17+
subtotal := 0.0
18+
for _, item := range items {
19+
subtotal += float64(item.quantity) * item.unitPrice
20+
}
21+
22+
discountValue := subtotal * (discountPercent / 100.0)
23+
taxedBase := subtotal - discountValue
24+
taxValue := taxedBase * (taxPercent / 100.0)
25+
26+
return invoiceSummary{
27+
subtotal: subtotal,
28+
discountValue: discountValue,
29+
taxValue: taxValue,
30+
total: taxedBase + taxValue,
31+
}
32+
}

languages/go/04-expert/performance-and-profiling-basics/example/main.go

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,40 @@ import (
88
"time"
99
)
1010

11+
var (
12+
retainedText string
13+
retainedValues []int
14+
)
15+
1116
func main() {
1217
// Program flow: measure two paired implementations on the same workload size.
1318
const lineCount = 4000
14-
concatDuration := measure(func() { _ = buildWithConcatenation(lineCount) })
15-
builderDuration := measure(func() { _ = buildWithBuilder(lineCount) })
19+
const repetitions = 12
20+
concatDuration := measureAverage(func() { retainedText = buildWithConcatenation(lineCount) }, repetitions)
21+
builderDuration := measureAverage(func() { retainedText = buildWithBuilder(lineCount) }, repetitions)
1622

17-
fmt.Printf("String concatenation: %v\n", concatDuration)
18-
fmt.Printf("strings.Builder: %v\n", builderDuration)
23+
fmt.Printf("Average string concatenation (%d runs): %v\n", repetitions, concatDuration)
24+
fmt.Printf("Average strings.Builder (%d runs): %v\n", repetitions, builderDuration)
1925

2026
const itemCount = 200000
21-
noCapacity := measure(func() { _ = fillWithoutCapacity(itemCount) })
22-
withCapacity := measure(func() { _ = fillWithCapacity(itemCount) })
27+
noCapacity := measureAverage(func() { retainedValues = fillWithoutCapacity(itemCount) }, repetitions)
28+
withCapacity := measureAverage(func() { retainedValues = fillWithCapacity(itemCount) }, repetitions)
2329

2430
// Intent: final output keeps the comparison direct and easy to verify.
25-
fmt.Printf("Slice fill without capacity: %v\n", noCapacity)
26-
fmt.Printf("Slice fill with capacity: %v\n", withCapacity)
31+
fmt.Printf("Average slice fill without capacity (%d runs): %v\n", repetitions, noCapacity)
32+
fmt.Printf("Average slice fill with capacity (%d runs): %v\n", repetitions, withCapacity)
2733
}
2834

29-
func measure(action func()) time.Duration {
30-
start := time.Now()
35+
func measureAverage(action func(), repetitions int) time.Duration {
3136
action()
32-
return time.Since(start)
37+
38+
var total time.Duration
39+
for iteration := 0; iteration < repetitions; iteration++ {
40+
start := time.Now()
41+
action()
42+
total += time.Since(start)
43+
}
44+
return total / time.Duration(repetitions)
3345
}
3446

3547
func buildWithConcatenation(lineCount int) string {

languages/python/04-expert/performance-and-profiling-basics/example/main.py

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,18 @@
55
import time
66

77

8-
def measure(action) -> float:
9-
start = time.perf_counter()
8+
retained_object: object | None = None
9+
10+
11+
def measure_average(action, repetitions: int) -> float:
1012
action()
11-
return time.perf_counter() - start
13+
14+
total = 0.0
15+
for _ in range(repetitions):
16+
start = time.perf_counter()
17+
action()
18+
total += time.perf_counter() - start
19+
return total / repetitions
1220

1321

1422
def build_with_concatenation(line_count: int) -> str:
@@ -40,19 +48,37 @@ def fill_with_presize(item_count: int) -> list[int]:
4048
def main() -> None:
4149
# Program flow: measure two paired implementations on the same workload size.
4250
line_count = 4_000
43-
concat_duration = measure(lambda: build_with_concatenation(line_count))
44-
join_duration = measure(lambda: build_with_join(line_count))
51+
repetitions = 12
52+
53+
def remember(value: object) -> None:
54+
global retained_object
55+
retained_object = value
56+
57+
concat_duration = measure_average(
58+
lambda: remember(build_with_concatenation(line_count)),
59+
repetitions,
60+
)
61+
join_duration = measure_average(
62+
lambda: remember(build_with_join(line_count)),
63+
repetitions,
64+
)
4565

46-
print(f"String concatenation: {concat_duration:.6f}s")
47-
print(f"str.join: {join_duration:.6f}s")
66+
print(f"Average string concatenation ({repetitions} runs): {concat_duration:.6f}s")
67+
print(f"Average str.join ({repetitions} runs): {join_duration:.6f}s")
4868

4969
item_count = 200_000
50-
no_presize = measure(lambda: fill_without_presize(item_count))
51-
with_presize = measure(lambda: fill_with_presize(item_count))
70+
no_presize = measure_average(
71+
lambda: remember(fill_without_presize(item_count)),
72+
repetitions,
73+
)
74+
with_presize = measure_average(
75+
lambda: remember(fill_with_presize(item_count)),
76+
repetitions,
77+
)
5278

5379
# Intent: final output keeps the comparison direct and easy to verify.
54-
print(f"List fill without pre-size: {no_presize:.6f}s")
55-
print(f"List fill with pre-size: {with_presize:.6f}s")
80+
print(f"Average list fill without pre-size ({repetitions} runs): {no_presize:.6f}s")
81+
print(f"Average list fill with pre-size ({repetitions} runs): {with_presize:.6f}s")
5682

5783

5884
if __name__ == "__main__":

languages/python/04-expert/smart-pointers-in-depth/example/main.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ def __init__(self, title: str) -> None:
1111
self.title = title
1212
print(f"Created report: {title}")
1313

14-
def __del__(self) -> None:
15-
print(f"Finalized report: {self.title}")
16-
1714

1815
class ReportOwner:
1916
def __init__(self, name: str, report: Report | None) -> None:
@@ -44,13 +41,27 @@ def describe(self) -> None:
4441
print(f"Preview can still see: {report.title}")
4542

4643

47-
def make_preview() -> PreviewPane:
48-
transient = Report("Transient Draft")
49-
return PreviewPane(transient)
44+
class PreviewSession:
45+
def __init__(self, report: Report) -> None:
46+
self._report = report
47+
48+
@property
49+
def current(self) -> Report | None:
50+
return self._report
51+
52+
def release(self) -> None:
53+
self._report = None
54+
55+
56+
def create_preview_session() -> tuple[PreviewPane, PreviewSession]:
57+
session = PreviewSession(Report("Transient Draft"))
58+
current = session.current
59+
assert current is not None
60+
return PreviewPane(current), session
5061

5162

5263
def main() -> None:
53-
# Program flow: move one strongly-owned reference, then watch a weak observer expire.
64+
# Program flow: move one strongly-owned reference, then release a temporary strong owner.
5465
inbox = ReportOwner("Inbox", Report("Quarterly Summary"))
5566
archive = ReportOwner("Archive", None)
5667

@@ -60,11 +71,12 @@ def main() -> None:
6071
inbox.describe()
6172
archive.describe()
6273

63-
preview = make_preview()
74+
preview, session = create_preview_session()
6475
preview.describe()
65-
gc.collect()
6676

67-
# Intent: final state confirms that the weak observer did not keep the object alive.
77+
# Intent: dropping the last strong owner makes the weak observer expire on the next collection.
78+
session.release()
79+
gc.collect()
6880
preview.describe()
6981

7082

0 commit comments

Comments
 (0)