Coverage for tests/benchmark/benchmark.py: 0%

7 statements  

« prev     ^ index     » next       coverage.py v7.4.3, created at 2024-04-21 11:16 +0000

1"""The files at `tests/benchmark` provide an example script to do profiling that can be customized for different 

2scenarios, it relies on `line_profiler <https://github.com/pyutils/line_profiler>`_ package. 

3 

4The code snippet below shows how to run the benchmark script from a PowerShell terminal and save the results to file: 

5 

6.. code-block:: powershell 

7 

8 kernprof --line-by-line --builtin --outfile './tests/benchmark/benchmark.lprof' './tests/benchmark/benchmark.py'; 

9 $output = python -m line_profiler -rmt "./tests/benchmark/benchmark.lprof"; 

10 $output = $output -join [System.Environment]::NewLine; 

11 Set-Content -Path "./tests/benchmark/benchmark.txt" -Value "$output" -Force; 

12 

13The following shows benchmarks for signals of different types using these scripts: 

14 

15.. code-block:: 

16 

17 ~100k samples with ~500 edges: 

18 Filtering: 0.00 seconds, State Levels: 0.01 seconds, Edges: 0.06 seconds 

19 ~100k samples with ~5k edges: 

20 Filtering: 0.00 seconds, State Levels: 0.01 seconds, Edges: 0.61 seconds 

21 ~1M samples with ~5k edges: 

22 Filtering: 0.03 seconds, State Levels: 0.03 seconds, Edges: 0.58 seconds 

23 ~1M samples with ~50k edges: 

24 Filtering: 0.02 seconds, State Levels: 0.03 seconds, Edges: 4.86 seconds 

25 ~10M samples with ~50k edges: 

26 Filtering: 0.23 seconds, State Levels: 0.25 seconds, Edges: 5.32 seconds 

27 ~10M samples with ~500k edges: 

28 Filtering: 0.24 seconds, State Levels: 0.31 seconds, Edges: 52.75 seconds 

29 ~30M samples with ~25k edges: 

30 Filtering: 0.68 seconds, State Levels: 1.83 seconds, Edges: 4.50 seconds 

31 ~30M samples with ~2k edges: 

32 Filtering: 0.08 seconds, State Levels: 0.52 seconds, Edges: 0.88 seconds 

33 ~30M samples with ~150k edges: 

34 Filtering: 0.66 seconds, State Levels: 0.87 seconds, Edges: 19.44 seconds 

35 

36Below some notes are listed in terms of performance: 

37 

38- If the full range of the signal calculated from the state levels, :class:`.StateLevels`, is below an expected value 

39 for the user system, then a processing to extract edges can be skipped as there isn't any. 

40 

41- Runt edges, :class:`~.edges.definitions.Type` are more time consuming to extract than normal edges, a signal with 

42 an excessive amount of runt edges can be a symptom of issues in the system or in the acquisition and a better 

43 signal conditioning could reduce their number. 

44 

45- The intermediate point policy for edges, :class:`.IntPointPolicy`, can be adjusted to avoid calculation of 

46 intermediate points, forcing the use of the ``begin`` or ``end`` values as ``intermediate``. This can be convenient 

47 when the rising or falling times of the system are fast for the acquisition system to acquire samples during 

48 that period. 

49 

50- For specific scenarios, disabling logging and the garbage collector temporarily during the processing.""" 

51 

52# pylint: skip-file 

53 

54import gc 

55import os 

56import sys 

57 

58# Add path to 'signal-edges' package and perform imports. 

59sys.path.append(os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "..", "src"))) 

60 

61from signal_edges.signal import VoltageSignal 

62from signal_edges.signal.edges import IntPointPolicy 

63from signal_edges.signal.generator import SignalGenerator 

64 

65if __name__ == "__main__": 

66 # Disable garbage collection and enable profiling. 

67 gc.disable() 

68 profile.enable() # type: ignore 

69 

70 # Create generator with initial values. 

71 sampling_frequency = 100000 

72 generator = SignalGenerator(0, 1 / sampling_frequency, 100, 0, 100) 

73 

74 # Build signal with multiple pulses. 

75 generator.add_flat(10) 

76 generator.add_edge("falling", 0, 10) 

77 generator.add_flat(10) 

78 generator.add_edge("rising", 10, 10) 

79 generator.repeat((1000000 - 40) // 40) 

80 

81 # Generate signal with some noise. 

82 (signal_x, signal_y) = generator.generate(noise=(0, 5)) 

83 

84 # Create voltage signal from sample. 

85 signal = VoltageSignal(signal_x, signal_y, "s", "V") 

86 

87 # Create filtered signal. 

88 profile(signal.filters_elliptic) # type: ignore 

89 filtered_signal = signal.filters_elliptic(sampling_frequency, 4, sampling_frequency / 2.5) 

90 # Calculate state levels. 

91 profile(filtered_signal.state_levels) # type: ignore 

92 (state_levels, (_, _)) = filtered_signal.state_levels() 

93 # Calculate edges. 

94 profile(filtered_signal.edges) # type: ignore 

95 edges = filtered_signal.edges(state_levels, IntPointPolicy.POLICY_0) 

96 

97 # Print some data. 

98 print(f"Number of samples: {len(signal.timestamps)}.") 

99 print(f"Number of edges extracted: {len(edges)}.") 

100 

101 # End profile and resume garbage collection. 

102 profile.disable() # type: ignore 

103 gc.enable()